Add a committable, provenance-tagged knowledge graph layer with CLI generation and query support. - Extract TypeScript, Markdown, and FNXC knowledge into deterministic graph nodes and edges. - Persist recoverable graph artifacts outside ignored Fusion state and expose build/query CLI commands. - Document configuration and add core and CLI coverage for graph structure, serialization, and recovery. Files changed: .changeset/fn-8921-knowledge-graph.md | 7 + .gitattributes | 3 + AGENTS.md | 1 + docs/README.md | 2 + docs/cli-reference.md | 4 + docs/knowledge-graph.md | 37 +++++ docs/settings-reference.md | 4 + docs/storage.md | 2 + packages/cli/package.json | 3 +- .../__tests__/knowledge-graph-bundle-shape.test.ts | 4 + .../src/__tests__/knowledge-graph-command.test.ts | 115 ++++++++++++++ packages/cli/src/bin.ts | 19 +++ packages/cli/src/commands/knowledge-graph.ts | 79 ++++++++++ packages/cli/tsup.config.ts | 2 + packages/core/package.json | 4 +- packages/core/src/config/settings-schema.ts | 2 + packages/core/src/index.ts | 1 + .../__tests__/derive-modules.test.ts | 11 ++ .../__tests__/extract-file-composition.test.ts | 20 +++ .../knowledge-graph/__tests__/extract-fnxc.test.ts | 33 ++++ .../__tests__/extract-markdown.test.ts | 21 +++ .../__tests__/extract-typescript.test.ts | 30 ++++ .../__tests__/file-discovery.test.ts | 26 ++++ .../graph-artifact-not-gitignored.test.ts | 15 ++ .../__tests__/graph-builder-equivalence.test.ts | 76 ++++++++++ .../__tests__/graph-builder-incremental.test.ts | 52 +++++++ .../__tests__/graph-identity.test.ts | 11 ++ .../knowledge-graph/__tests__/graph-query.test.ts | 13 ++ .../__tests__/graph-serialization.test.ts | 29 ++++ .../__tests__/graph-store-recovery.test.ts | 106 +++++++++++++ .../__tests__/resolve-imports.test.ts | 10 ++ .../core/src/knowledge-graph/derive-modules.ts | 4 + packages/core/src/knowledge-graph/extract-file.ts | 6 + packages/core/src/knowledge-graph/extract-fnxc.ts | 168 +++++++++++++++++++++ .../core/src/knowledge-graph/extract-markdown.ts | 9 ++ .../core/src/knowledge-graph/extract-typescript.ts | 107 +++++++++++++ .../core/src/knowledge-graph/file-discovery.ts | 85 +++++++++++ packages/core/src/knowledge-graph/graph-builder.ts | 141 +++++++++++++++++ .../core/src/knowledge-graph/graph-manifest.ts | 4 + packages/core/src/knowledge-graph/graph-query.ts | 126 ++++++++++++++++ .../src/knowledge-graph/graph-serialization.ts | 134 ++++++++++++++++ packages/core/src/knowledge-graph/graph-store.ts | 97 ++++++++++++ packages/core/src/knowledge-graph/graph-types.ts | 54 +++++++ packages/core/src/knowledge-graph/index.ts | 14 ++ .../core/src/knowledge-graph/resolve-imports.ts | 4 + packages/core/src/types/settings/settings-scope.ts | 2 + pnpm-lock.yaml | 12 +- 47 files changed, 1700 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-8921 Fusion-Task-Lineage: 7014d0f1-fc47-454b-afe5-5f0d9b229f33 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
5 lines
1.7 KiB
TypeScript
5 lines
1.7 KiB
TypeScript
import { dirname, basename } from "node:path";
|
|
import { edgeId, fileNodeId, moduleCanonicalPath, moduleNodeId, type GraphEdge, type GraphNode } from "./graph-types.js";
|
|
/** Modules are recomputed from files: assigning a multi-file module to one owner would make pruning unsound. */
|
|
export function deriveModules(files:GraphNode[]):{nodes:GraphNode[];edges:GraphEdge[]}{const paths=files.filter(n=>n.kind==="file"&&/\.tsx?$/.test(n.ownerPath)).map(n=>n.ownerPath),dirs=[...new Set(paths.map(dirname).filter(dir=>dir!=="."))].sort(),nodes:GraphNode[]=[],edges:GraphEdge[]=[];for(const dir of dirs){const direct=paths.filter(p=>dirname(p)===dir),canonical=moduleCanonicalPath(dir,paths),id=moduleNodeId(dir),attributes={directFileCount:String(direct.length),barrel:String(paths.includes(`${dir}/index.ts`)||paths.includes(`${dir}/index.tsx`)),syntheticSource:"true"};nodes.push({id,kind:"module",name:basename(dir),owner:"derived",ownerPath:dir,source:{path:canonical,line:1,column:1},attributes});for(const file of direct)edges.push({id:edgeId("contains",id,fileNodeId(file)),kind:"contains",from:id,to:fileNodeId(file),provenance:"extracted",owner:"derived",ownerPath:dir,source:{path:canonical,line:1,column:1},attributes:{syntheticSource:"true"}});const parent=dirs.filter(d=>dir.startsWith(`${d}/`)).sort((a,b)=>b.length-a.length)[0];if(parent){const parentNode=moduleNodeId(parent),parentCanonical=moduleCanonicalPath(parent,paths);edges.push({id:edgeId("contains",parentNode,id),kind:"contains",from:parentNode,to:id,provenance:"extracted",owner:"derived",ownerPath:parent,source:{path:parentCanonical,line:1,column:1},attributes:{syntheticSource:"true"}});}}return{nodes:nodes.sort((a,b)=>a.id.localeCompare(b.id)),edges:edges.sort((a,b)=>a.id.localeCompare(b.id))};}
|