@nanonets/graft 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/ai/crux.d.ts +31 -0
  4. package/dist/ai/crux.d.ts.map +1 -0
  5. package/dist/ai/crux.js +91 -0
  6. package/dist/ai/crux.js.map +1 -0
  7. package/dist/ai/providers.d.ts +37 -0
  8. package/dist/ai/providers.d.ts.map +1 -0
  9. package/dist/ai/providers.js +17 -0
  10. package/dist/ai/providers.js.map +1 -0
  11. package/dist/ai/summarize.d.ts +25 -0
  12. package/dist/ai/summarize.d.ts.map +1 -0
  13. package/dist/ai/summarize.js +41 -0
  14. package/dist/ai/summarize.js.map +1 -0
  15. package/dist/ai/synthesize.d.ts +32 -0
  16. package/dist/ai/synthesize.d.ts.map +1 -0
  17. package/dist/ai/synthesize.js +132 -0
  18. package/dist/ai/synthesize.js.map +1 -0
  19. package/dist/cli.d.ts +13 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +131 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/context/build.d.ts +33 -0
  24. package/dist/context/build.d.ts.map +1 -0
  25. package/dist/context/build.js +265 -0
  26. package/dist/context/build.js.map +1 -0
  27. package/dist/context/check.d.ts +21 -0
  28. package/dist/context/check.d.ts.map +1 -0
  29. package/dist/context/check.js +127 -0
  30. package/dist/context/check.js.map +1 -0
  31. package/dist/context/node-file.d.ts +80 -0
  32. package/dist/context/node-file.d.ts.map +1 -0
  33. package/dist/context/node-file.js +165 -0
  34. package/dist/context/node-file.js.map +1 -0
  35. package/dist/engine.d.ts +58 -0
  36. package/dist/engine.d.ts.map +1 -0
  37. package/dist/engine.js +87 -0
  38. package/dist/engine.js.map +1 -0
  39. package/dist/graph/build.d.ts +33 -0
  40. package/dist/graph/build.d.ts.map +1 -0
  41. package/dist/graph/build.js +95 -0
  42. package/dist/graph/build.js.map +1 -0
  43. package/dist/graph/check.d.ts +18 -0
  44. package/dist/graph/check.d.ts.map +1 -0
  45. package/dist/graph/check.js +126 -0
  46. package/dist/graph/check.js.map +1 -0
  47. package/dist/graph/enrich.d.ts +42 -0
  48. package/dist/graph/enrich.d.ts.map +1 -0
  49. package/dist/graph/enrich.js +141 -0
  50. package/dist/graph/enrich.js.map +1 -0
  51. package/dist/graph/extract.d.ts +23 -0
  52. package/dist/graph/extract.d.ts.map +1 -0
  53. package/dist/graph/extract.js +249 -0
  54. package/dist/graph/extract.js.map +1 -0
  55. package/dist/graph/resolve.d.ts +4 -0
  56. package/dist/graph/resolve.d.ts.map +1 -0
  57. package/dist/graph/resolve.js +0 -0
  58. package/dist/graph/resolve.js.map +1 -0
  59. package/dist/graph/types.d.ts +56 -0
  60. package/dist/graph/types.d.ts.map +1 -0
  61. package/dist/graph/types.js +14 -0
  62. package/dist/graph/types.js.map +1 -0
  63. package/dist/graph/write.d.ts +9 -0
  64. package/dist/graph/write.d.ts.map +1 -0
  65. package/dist/graph/write.js +39 -0
  66. package/dist/graph/write.js.map +1 -0
  67. package/dist/index.d.ts +33 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +26 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/ingest/fs.d.ts +10 -0
  72. package/dist/ingest/fs.d.ts.map +1 -0
  73. package/dist/ingest/fs.js +48 -0
  74. package/dist/ingest/fs.js.map +1 -0
  75. package/dist/util/id.d.ts +7 -0
  76. package/dist/util/id.d.ts.map +1 -0
  77. package/dist/util/id.js +14 -0
  78. package/dist/util/id.js.map +1 -0
  79. package/dist/viewer/app.js +1 -0
  80. package/dist/viewer/index.html +48 -0
  81. package/dist/viewer/style.css +119 -0
  82. package/dist/viz/assemble.d.ts +33 -0
  83. package/dist/viz/assemble.d.ts.map +1 -0
  84. package/dist/viz/assemble.js +101 -0
  85. package/dist/viz/assemble.js.map +1 -0
  86. package/dist/viz/serve.d.ts +12 -0
  87. package/dist/viz/serve.d.ts.map +1 -0
  88. package/dist/viz/serve.js +128 -0
  89. package/dist/viz/serve.js.map +1 -0
  90. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/context/check.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,+DAA+D;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,WAAW,CAsE9E;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAyBxD"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * `check` — is the committed `.context/` graph still in sync with the code?
3
+ *
4
+ * Pure I/O + hashing: no LLM, no network, milliseconds. It re-hashes the source
5
+ * files the manifest recorded and compares. Meant to run in CI: exit 0 when the
6
+ * graph is fresh, 1 when it has drifted (so a PR that changed code but not the
7
+ * graph fails until `graft init` is re-run and committed).
8
+ *
9
+ * Drift categories:
10
+ * content a recorded source file's bytes changed
11
+ * removed a recorded source file no longer exists
12
+ * coverage a code file exists that no node was built from (new/uningested)
13
+ * index a node's frontmatter disagrees with the manifest (hand-edited)
14
+ */
15
+ import { readFileSync } from "node:fs";
16
+ import { relative, resolve } from "node:path";
17
+ import { walkDir } from "../ingest/fs.js";
18
+ import { contentHash } from "../util/id.js";
19
+ import { CODE_EXTENSIONS } from "./build.js";
20
+ import { contextDirFor, readManifest, readNodes } from "./node-file.js";
21
+ export function checkContext(dir, opts = {}) {
22
+ const root = resolve(dir);
23
+ const outDir = contextDirFor(root, opts.contextDir);
24
+ const exts = opts.extensions ?? CODE_EXTENSIONS;
25
+ const result = {
26
+ ok: false,
27
+ missing: false,
28
+ contentDrift: [],
29
+ removed: [],
30
+ coverage: [],
31
+ indexDrift: [],
32
+ };
33
+ const manifest = readManifest(outDir);
34
+ if (!manifest) {
35
+ result.missing = true;
36
+ return result;
37
+ }
38
+ // Current code files on disk (same rules `init` used).
39
+ const current = new Map(); // rel → hash
40
+ for (const file of walkDir(root)) {
41
+ if (file.startsWith(outDir))
42
+ continue;
43
+ if (!exts.some((e) => file.toLowerCase().endsWith(e)))
44
+ continue;
45
+ try {
46
+ current.set(relative(root, file), contentHash(readFileSync(file, "utf8")));
47
+ }
48
+ catch {
49
+ // Unreadable now → treat as removed below (it won't be in `current`).
50
+ }
51
+ }
52
+ // Compare the manifest's recorded files against what's on disk.
53
+ const recorded = new Set();
54
+ for (const ref of manifest.files) {
55
+ recorded.add(ref.path);
56
+ const now = current.get(ref.path);
57
+ if (now === undefined) {
58
+ result.removed.push(ref.path);
59
+ }
60
+ else if (now !== ref.hash) {
61
+ result.contentDrift.push({ path: ref.path, from: short(ref.hash), to: short(now) });
62
+ }
63
+ }
64
+ for (const path of current.keys()) {
65
+ if (!recorded.has(path))
66
+ result.coverage.push(path);
67
+ }
68
+ // Node frontmatter must agree with the manifest roster (catches hand edits).
69
+ const manifestNodes = new Map(manifest.nodes.map((n) => [n.slug, n]));
70
+ const onDisk = readNodes(outDir);
71
+ const seen = new Set();
72
+ for (const node of onDisk) {
73
+ seen.add(node.slug);
74
+ const m = manifestNodes.get(node.slug);
75
+ if (!m) {
76
+ result.indexDrift.push(`${node.slug}: node file not in manifest`);
77
+ }
78
+ else if (m.sourcesDigest !== node.sourcesDigest) {
79
+ result.indexDrift.push(`${node.slug}: frontmatter digest ≠ manifest`);
80
+ }
81
+ }
82
+ for (const slug of manifestNodes.keys()) {
83
+ if (!seen.has(slug))
84
+ result.indexDrift.push(`${slug}: in manifest but node file missing`);
85
+ }
86
+ result.ok =
87
+ result.contentDrift.length === 0 &&
88
+ result.removed.length === 0 &&
89
+ result.coverage.length === 0 &&
90
+ result.indexDrift.length === 0;
91
+ return result;
92
+ }
93
+ /** Render a check result as a human-readable report. */
94
+ export function formatCheckReport(r) {
95
+ if (r.missing) {
96
+ return "graft check: NO GRAPH\n\nNo .context/manifest.json found. Run `graft init` first.";
97
+ }
98
+ if (r.ok)
99
+ return "graft check: OK — the graph is in sync with the code.";
100
+ const lines = ["graft check: STALE", ""];
101
+ if (r.contentDrift.length) {
102
+ lines.push(`changed (${r.contentDrift.length}):`);
103
+ for (const c of r.contentDrift)
104
+ lines.push(` ~ ${c.path} (${c.from} → ${c.to})`);
105
+ }
106
+ if (r.removed.length) {
107
+ lines.push(`removed (${r.removed.length}):`);
108
+ for (const p of r.removed)
109
+ lines.push(` - ${p}`);
110
+ }
111
+ if (r.coverage.length) {
112
+ lines.push(`not in graph (${r.coverage.length}):`);
113
+ for (const p of r.coverage)
114
+ lines.push(` + ${p}`);
115
+ }
116
+ if (r.indexDrift.length) {
117
+ lines.push(`index mismatch (${r.indexDrift.length}):`);
118
+ for (const s of r.indexDrift)
119
+ lines.push(` ! ${s}`);
120
+ }
121
+ lines.push("", "Run `graft init` to regenerate, then commit .context/.");
122
+ return lines.join("\n");
123
+ }
124
+ function short(hash) {
125
+ return hash.slice(0, 8);
126
+ }
127
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/context/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /** A source file a node was derived from, with its content hash at generation time. */
2
+ export interface SourceRef {
3
+ path: string;
4
+ hash: string;
5
+ }
6
+ /** A directed edge to another node (by slug). */
7
+ export interface NodeLink {
8
+ to: string;
9
+ relation: string;
10
+ description?: string;
11
+ }
12
+ /** A single node in the graph — one markdown file. */
13
+ export interface ContextNode {
14
+ name: string;
15
+ slug: string;
16
+ /** Coarse category from extraction: system | service | api | concept | … */
17
+ type: string;
18
+ /** Prose description that becomes the node body. */
19
+ summary: string;
20
+ /** Source files that produced this node (empty only for hand-authored nodes). */
21
+ sources: SourceRef[];
22
+ /** sha256 over the sorted `path:hash` lines of {@link sources}. */
23
+ sourcesDigest: string;
24
+ /** Outbound edges. */
25
+ links: NodeLink[];
26
+ /** Free-form region below the generated block, preserved across re-runs. */
27
+ human: string;
28
+ }
29
+ /** The generated index written alongside the node files. */
30
+ export interface Manifest {
31
+ version: number;
32
+ /** Human label for the model that built the graph, e.g. "openrouter:openai/gpt-4o-mini". */
33
+ model: string;
34
+ /** sha256 over every source file's `path:hash` — the whole-graph fingerprint. */
35
+ repoDigest: string;
36
+ /** Every source file `init` processed, with its hash. The staleness ground-truth. */
37
+ files: SourceRef[];
38
+ /** Node roster (a subset of each node's frontmatter, for fast reads). */
39
+ nodes: Array<{
40
+ slug: string;
41
+ name: string;
42
+ type: string;
43
+ sources: string[];
44
+ sourcesDigest: string;
45
+ }>;
46
+ }
47
+ export declare const MANIFEST_VERSION = 1;
48
+ /** Gitignored cache dir (per-file summaries + extractions), never committed. */
49
+ export declare const CACHE_DIR = ".cache";
50
+ /** Turn a display name into a stable, filesystem- and link-safe slug. */
51
+ export declare function slugify(name: string): string;
52
+ /** sha256 over the sorted `path:hash` lines of a set of sources. */
53
+ export declare function digestSources(sources: SourceRef[]): string;
54
+ /** Absolute path of the `.context/` directory for a repo root. */
55
+ export declare function contextDirFor(root: string, override?: string): string;
56
+ /** Serialize a node to its full markdown file contents. */
57
+ export declare function renderNodeFile(node: ContextNode): string;
58
+ /**
59
+ * Write a node file, preserving any existing human region. Returns the file
60
+ * path written.
61
+ */
62
+ export declare function writeNode(dir: string, node: ContextNode): string;
63
+ /** A node file parsed back off disk (frontmatter only; body is not needed for `check`). */
64
+ export interface ParsedNode {
65
+ slug: string;
66
+ name: string;
67
+ type: string;
68
+ sources: SourceRef[];
69
+ sourcesDigest: string;
70
+ links: NodeLink[];
71
+ }
72
+ /** Read and parse every `.md` node file in a context dir (skips the manifest). */
73
+ export declare function readNodes(dir: string): ParsedNode[];
74
+ /** List the node files present (slug → filename), for pruning deleted nodes. */
75
+ export declare function existingNodeSlugs(dir: string): Set<string>;
76
+ /** Delete a node file by slug. */
77
+ export declare function deleteNode(dir: string, slug: string): void;
78
+ export declare function writeManifest(dir: string, manifest: Manifest): void;
79
+ export declare function readManifest(dir: string): Manifest | undefined;
80
+ //# sourceMappingURL=node-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-file.d.ts","sourceRoot":"","sources":["../../src/context/node-file.ts"],"names":[],"mappings":"AA4BA,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iDAAiD;AACjD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4DAA4D;AAC5D,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,UAAU,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,yEAAyE;IACzE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtG;AAED,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAIlC,gFAAgF;AAChF,eAAO,MAAM,SAAS,WAAW,CAAC;AAElC,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAM1D;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrE;AAsBD,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAexD;AAaD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAWhE;AAED,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAgBnD;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAO1D;AAED,kCAAkC;AAClC,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAG1D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAGnE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAQ9D"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * The on-disk format of the context graph: a folder of plain markdown files
3
+ * under `.context/`, one per node. The files ARE the graph — the frontmatter
4
+ * carries the machine-readable node (identity, source provenance, edges) and
5
+ * the body is human/agent-readable prose. There is no database.
6
+ *
7
+ * Layout of a node file:
8
+ *
9
+ * --- ← YAML frontmatter (canonical, machine-owned)
10
+ * name / slug / type
11
+ * sources: [{path, hash}] ← provenance + the staleness key
12
+ * sources_digest
13
+ * links: [{to, relation}] ← edges (canonical form)
14
+ * ---
15
+ * <!-- context:generated:start -->
16
+ * ## Summary … ## Related … ← regenerated on every `init`
17
+ * <!-- context:generated:end -->
18
+ * ## Notes … ← anything a human writes is preserved verbatim
19
+ *
20
+ * `.context/manifest.json` is a generated index over all nodes plus the full
21
+ * file→hash map `init` saw, so `check` can detect drift in O(files) without an
22
+ * LLM and without re-reading every node body.
23
+ */
24
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, rmSync } from "node:fs";
25
+ import { join } from "node:path";
26
+ import matter from "gray-matter";
27
+ import { contentHash, normalizeName } from "../util/id.js";
28
+ export const MANIFEST_VERSION = 1;
29
+ const GEN_START = "<!-- context:generated:start -->";
30
+ const GEN_END = "<!-- context:generated:end -->";
31
+ const MANIFEST_FILE = "manifest.json";
32
+ /** Gitignored cache dir (per-file summaries + extractions), never committed. */
33
+ export const CACHE_DIR = ".cache";
34
+ /** Turn a display name into a stable, filesystem- and link-safe slug. */
35
+ export function slugify(name) {
36
+ const base = normalizeName(name)
37
+ .replace(/[^a-z0-9]+/g, "-")
38
+ .replace(/^-+|-+$/g, "");
39
+ return base || "node";
40
+ }
41
+ /** sha256 over the sorted `path:hash` lines of a set of sources. */
42
+ export function digestSources(sources) {
43
+ const lines = sources
44
+ .map((s) => `${s.path}:${s.hash}`)
45
+ .sort()
46
+ .join("\n");
47
+ return contentHash(lines);
48
+ }
49
+ /** Absolute path of the `.context/` directory for a repo root. */
50
+ export function contextDirFor(root, override) {
51
+ if (override)
52
+ return override;
53
+ return join(root, ".context");
54
+ }
55
+ /** Render the generated body (Summary + Related) for a node. */
56
+ function renderGenerated(node) {
57
+ const lines = [`## Summary`, ``, node.summary.trim() || "_(no summary)_", ``];
58
+ if (node.links.length > 0) {
59
+ lines.push(`## Related`, ``);
60
+ for (const link of [...node.links].sort((a, b) => a.to.localeCompare(b.to))) {
61
+ const rel = link.relation.replace(/_/g, " ");
62
+ const tail = link.description ? ` — ${link.description}` : "";
63
+ lines.push(`- ${rel} [[${link.to}]]${tail}`);
64
+ }
65
+ lines.push(``);
66
+ }
67
+ return lines.join("\n");
68
+ }
69
+ /** Default human region for a brand-new node (preserved verbatim afterwards). */
70
+ function defaultHuman() {
71
+ return `\n## Notes\n\n_Anything written below the generated block is preserved when the graph is regenerated._\n`;
72
+ }
73
+ /** Serialize a node to its full markdown file contents. */
74
+ export function renderNodeFile(node) {
75
+ const data = {
76
+ name: node.name,
77
+ slug: node.slug,
78
+ type: node.type,
79
+ sources: node.sources.map((s) => ({ path: s.path, hash: s.hash })),
80
+ sources_digest: node.sourcesDigest,
81
+ links: node.links.map((l) => l.description ? { to: l.to, relation: l.relation, description: l.description } : { to: l.to, relation: l.relation }),
82
+ generator: { version: MANIFEST_VERSION },
83
+ };
84
+ const body = `${GEN_START}\n${renderGenerated(node)}${GEN_END}\n${node.human}`;
85
+ // gray-matter appends a trailing newline; keep output stable across runs.
86
+ return matter.stringify(body, data);
87
+ }
88
+ /**
89
+ * Extract the human region (everything after the generated-end marker) from an
90
+ * existing node file so it survives regeneration. If the markers are missing
91
+ * (a hand-deleted or foreign file), returns a fresh default region.
92
+ */
93
+ function preserveHuman(existingContent) {
94
+ const idx = existingContent.indexOf(GEN_END);
95
+ if (idx === -1)
96
+ return defaultHuman();
97
+ return existingContent.slice(idx + GEN_END.length).replace(/^\n/, "");
98
+ }
99
+ /**
100
+ * Write a node file, preserving any existing human region. Returns the file
101
+ * path written.
102
+ */
103
+ export function writeNode(dir, node) {
104
+ mkdirSync(dir, { recursive: true });
105
+ const path = join(dir, `${node.slug}.md`);
106
+ if (existsSync(path)) {
107
+ const parsed = matter(readFileSync(path, "utf8"));
108
+ node = { ...node, human: preserveHuman(parsed.content) };
109
+ }
110
+ else {
111
+ node = { ...node, human: node.human || defaultHuman() };
112
+ }
113
+ writeFileSync(path, renderNodeFile(node));
114
+ return path;
115
+ }
116
+ /** Read and parse every `.md` node file in a context dir (skips the manifest). */
117
+ export function readNodes(dir) {
118
+ if (!existsSync(dir))
119
+ return [];
120
+ const out = [];
121
+ for (const entry of readdirSync(dir)) {
122
+ if (!entry.endsWith(".md"))
123
+ continue;
124
+ const fm = matter(readFileSync(join(dir, entry), "utf8")).data;
125
+ out.push({
126
+ slug: String(fm.slug ?? entry.replace(/\.md$/, "")),
127
+ name: String(fm.name ?? ""),
128
+ type: String(fm.type ?? ""),
129
+ sources: Array.isArray(fm.sources) ? fm.sources : [],
130
+ sourcesDigest: String(fm.sources_digest ?? ""),
131
+ links: Array.isArray(fm.links) ? fm.links : [],
132
+ });
133
+ }
134
+ return out;
135
+ }
136
+ /** List the node files present (slug → filename), for pruning deleted nodes. */
137
+ export function existingNodeSlugs(dir) {
138
+ if (!existsSync(dir))
139
+ return new Set();
140
+ return new Set(readdirSync(dir)
141
+ .filter((e) => e.endsWith(".md"))
142
+ .map((e) => e.replace(/\.md$/, "")));
143
+ }
144
+ /** Delete a node file by slug. */
145
+ export function deleteNode(dir, slug) {
146
+ const path = join(dir, `${slug}.md`);
147
+ if (existsSync(path))
148
+ rmSync(path);
149
+ }
150
+ export function writeManifest(dir, manifest) {
151
+ mkdirSync(dir, { recursive: true });
152
+ writeFileSync(join(dir, MANIFEST_FILE), JSON.stringify(manifest, null, 2) + "\n");
153
+ }
154
+ export function readManifest(dir) {
155
+ const path = join(dir, MANIFEST_FILE);
156
+ if (!existsSync(path))
157
+ return undefined;
158
+ try {
159
+ return JSON.parse(readFileSync(path, "utf8"));
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ }
165
+ //# sourceMappingURL=node-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-file.js","sourceRoot":"","sources":["../../src/context/node-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The Context Graph Engine.
3
+ *
4
+ * Two operations, no database:
5
+ * - {@link Graft.init} build `.context/` from a code repo.
6
+ * - {@link Graft.check} report whether `.context/` is still in
7
+ * sync with the code (for CI).
8
+ *
9
+ * The graph is a folder of linked markdown files committed to the repo; git is
10
+ * the sync. This class wires the OpenRouter LLM providers into the build/check
11
+ * pipelines; an API key is required for any LLM-backed operation.
12
+ */
13
+ import { type EngineConfig } from "./ai/providers.js";
14
+ import { CODE_EXTENSIONS, type BuildProgress, type BuildResult } from "./context/build.js";
15
+ import { type CheckResult } from "./context/check.js";
16
+ import { type GraphBuildOptions, type GraphBuildResult } from "./graph/build.js";
17
+ import { type GraphCheckResult } from "./graph/check.js";
18
+ export { CODE_EXTENSIONS };
19
+ export type { BuildResult, BuildProgress, CheckResult, GraphBuildResult, GraphCheckResult };
20
+ export interface InitOptions {
21
+ /** Code extensions to include. Default: {@link CODE_EXTENSIONS}. */
22
+ extensions?: string[];
23
+ /** Progress callback for long builds. */
24
+ onProgress?: (info: BuildProgress) => void;
25
+ }
26
+ export interface CheckRunOptions {
27
+ extensions?: string[];
28
+ }
29
+ export interface GraphRunOptions {
30
+ /** Run the Tier-2 LLM meaning pass (summary + crux). Absent → Tier-1 only. */
31
+ llm?: boolean;
32
+ onProgress?: GraphBuildOptions["onProgress"];
33
+ }
34
+ export declare class Graft {
35
+ private cfg;
36
+ constructor(config?: EngineConfig);
37
+ /** Build the `.context/` graph from the repo at `dir`. */
38
+ init(dir: string, opts?: InitOptions): Promise<BuildResult>;
39
+ /** Report whether the committed `.context/` markdown graph is in sync with the code. */
40
+ check(dir: string, opts?: CheckRunOptions): CheckResult;
41
+ /** Report whether the committed `graph.json` is in sync with the code (Tier-1 diff). */
42
+ checkGraph(dir: string): GraphCheckResult;
43
+ /**
44
+ * Build `.context/graph.json` — a per-symbol code graph from tree-sitter.
45
+ * Tier-1 (structure) always runs; the Tier-2 meaning layer runs only when
46
+ * `opts.llm` is set. Either way the prior meaning layer is preserved.
47
+ */
48
+ graph(dir: string, opts?: GraphRunOptions): Promise<GraphBuildResult>;
49
+ /** The OpenRouter API key, or a clear error telling the user how to set it. */
50
+ private requireKey;
51
+ private synthesizer;
52
+ /** Per-node crux summarizer for the code graph's Tier-2 pass. */
53
+ private cruxSummarizer;
54
+ private summarizer;
55
+ /** Human label for the active model, recorded in the manifest. */
56
+ private modelLabel;
57
+ }
58
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAiB,KAAK,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAI1F,OAAO,EAAgB,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACzG,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAc,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AAE5F,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,yCAAyC;IACzC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;CAC9C;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAiB;gBAEhB,MAAM,GAAE,YAAiB;IAIrC,0DAA0D;IACpD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAWrE,wFAAwF;IACxF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,WAAW;IAI3D,wFAAwF;IACxF,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB;IAIzC;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQzE,+EAA+E;IAC/E,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,WAAW;IAKnB,iEAAiE;IACjE,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,UAAU;IAKlB,kEAAkE;IAClE,OAAO,CAAC,UAAU;CAInB"}
package/dist/engine.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The Context Graph Engine.
3
+ *
4
+ * Two operations, no database:
5
+ * - {@link Graft.init} build `.context/` from a code repo.
6
+ * - {@link Graft.check} report whether `.context/` is still in
7
+ * sync with the code (for CI).
8
+ *
9
+ * The graph is a folder of linked markdown files committed to the repo; git is
10
+ * the sync. This class wires the OpenRouter LLM providers into the build/check
11
+ * pipelines; an API key is required for any LLM-backed operation.
12
+ */
13
+ import { resolveConfig } from "./ai/providers.js";
14
+ import { OpenRouterSynthesizer } from "./ai/synthesize.js";
15
+ import { OpenRouterSummarizer } from "./ai/summarize.js";
16
+ import { OpenRouterCruxSummarizer } from "./ai/crux.js";
17
+ import { buildContext, CODE_EXTENSIONS } from "./context/build.js";
18
+ import { checkContext } from "./context/check.js";
19
+ import { buildGraph } from "./graph/build.js";
20
+ import { checkGraph } from "./graph/check.js";
21
+ export { CODE_EXTENSIONS };
22
+ export class Graft {
23
+ cfg;
24
+ constructor(config = {}) {
25
+ this.cfg = resolveConfig(config);
26
+ }
27
+ /** Build the `.context/` graph from the repo at `dir`. */
28
+ async init(dir, opts = {}) {
29
+ return buildContext(dir, {
30
+ contextDir: this.cfg.contextDir,
31
+ extensions: opts.extensions,
32
+ model: this.modelLabel(),
33
+ summarizer: this.summarizer(),
34
+ synthesizer: this.synthesizer(),
35
+ onProgress: opts.onProgress,
36
+ });
37
+ }
38
+ /** Report whether the committed `.context/` markdown graph is in sync with the code. */
39
+ check(dir, opts = {}) {
40
+ return checkContext(dir, { contextDir: this.cfg.contextDir, extensions: opts.extensions });
41
+ }
42
+ /** Report whether the committed `graph.json` is in sync with the code (Tier-1 diff). */
43
+ checkGraph(dir) {
44
+ return checkGraph(dir, { contextDir: this.cfg.contextDir });
45
+ }
46
+ /**
47
+ * Build `.context/graph.json` — a per-symbol code graph from tree-sitter.
48
+ * Tier-1 (structure) always runs; the Tier-2 meaning layer runs only when
49
+ * `opts.llm` is set. Either way the prior meaning layer is preserved.
50
+ */
51
+ graph(dir, opts = {}) {
52
+ return buildGraph(dir, {
53
+ contextDir: this.cfg.contextDir,
54
+ summarizer: opts.llm ? this.cruxSummarizer() : undefined,
55
+ onProgress: opts.onProgress,
56
+ });
57
+ }
58
+ /** The OpenRouter API key, or a clear error telling the user how to set it. */
59
+ requireKey() {
60
+ if (!this.cfg.openrouterApiKey) {
61
+ throw new Error("No OpenRouter API key. Set OPENROUTER_API_KEY (get one at https://openrouter.ai/keys) " +
62
+ "to build or summarize the graph.");
63
+ }
64
+ return this.cfg.openrouterApiKey;
65
+ }
66
+ synthesizer() {
67
+ if (this.cfg.synthesizer)
68
+ return this.cfg.synthesizer;
69
+ return new OpenRouterSynthesizer(this.requireKey(), this.cfg.openrouterModel, this.cfg.openrouterBaseUrl);
70
+ }
71
+ /** Per-node crux summarizer for the code graph's Tier-2 pass. */
72
+ cruxSummarizer() {
73
+ return new OpenRouterCruxSummarizer(this.requireKey(), this.cfg.openrouterModel, this.cfg.openrouterBaseUrl);
74
+ }
75
+ summarizer() {
76
+ if (this.cfg.summarizer)
77
+ return this.cfg.summarizer;
78
+ return new OpenRouterSummarizer(this.requireKey(), this.cfg.openrouterModel, this.cfg.openrouterBaseUrl);
79
+ }
80
+ /** Human label for the active model, recorded in the manifest. */
81
+ modelLabel() {
82
+ if (this.cfg.synthesizer || this.cfg.summarizer)
83
+ return "custom";
84
+ return `openrouter:${this.cfg.openrouterModel}`;
85
+ }
86
+ }
87
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type EnrichStats } from "./enrich.js";
2
+ import { GRAPH_FILE } from "./write.js";
3
+ import type { Kind, Relation } from "./types.js";
4
+ import type { CruxSummarizer } from "../ai/crux.js";
5
+ export interface GraphBuildOptions {
6
+ /** Override the output dir (default: `<root>/.context`). */
7
+ contextDir?: string;
8
+ /** Run the Tier-2 LLM meaning pass. Absent → Tier-1 only (cache is still preserved). */
9
+ summarizer?: CruxSummarizer;
10
+ onProgress?: (info: {
11
+ phase: "parse" | "enrich";
12
+ index: number;
13
+ total: number;
14
+ file: string;
15
+ }) => void;
16
+ }
17
+ export interface GraphBuildResult {
18
+ contextDir: string;
19
+ graphPath: string;
20
+ files: number;
21
+ nodes: number;
22
+ edges: number;
23
+ byKind: Record<Kind, number>;
24
+ byRelation: Record<Relation, number>;
25
+ languages: string[];
26
+ meaning: EnrichStats;
27
+ errors: string[];
28
+ }
29
+ /** The source files a graph build parses: supported languages, minus the output dir. */
30
+ export declare function listSourceFiles(root: string, outDir: string): string[];
31
+ export declare function buildGraph(dir: string, opts?: GraphBuildOptions): Promise<GraphBuildResult>;
32
+ export { GRAPH_FILE };
33
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/graph/build.ts"],"names":[],"mappings":"AAeA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAyB,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAW,IAAI,EAAU,QAAQ,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC;QAC1B,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CA4E3B;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}