Files
fusion/packages/core/src/knowledge-graph/derive-modules.ts
gsxdsm 27cb2d2621 FN-8921: add deterministic knowledge graph tooling
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>
2026-08-10 06:15:17 -07:00

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))};}