kirograph 0.22.0 → 0.26.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 (62) hide show
  1. package/README.md +19 -5
  2. package/dist/bin/commands/configure-mcp.js +104 -0
  3. package/dist/bin/commands/configure-mcp.js.map +7 -0
  4. package/dist/bin/commands/memory.js +334 -2
  5. package/dist/bin/commands/memory.js.map +2 -2
  6. package/dist/bin/commands/wiki.js +307 -0
  7. package/dist/bin/commands/wiki.js.map +7 -0
  8. package/dist/bin/installer/common.js +9 -0
  9. package/dist/bin/installer/common.js.map +2 -2
  10. package/dist/bin/installer/config-prompt.js +96 -5
  11. package/dist/bin/installer/config-prompt.js.map +2 -2
  12. package/dist/bin/installer/hooks.js +40 -0
  13. package/dist/bin/installer/hooks.js.map +2 -2
  14. package/dist/bin/installer/index.js +22 -11
  15. package/dist/bin/installer/index.js.map +2 -2
  16. package/dist/bin/installer/instructions.js +27 -1
  17. package/dist/bin/installer/instructions.js.map +2 -2
  18. package/dist/bin/installer/mcp.js +37 -4
  19. package/dist/bin/installer/mcp.js.map +2 -2
  20. package/dist/bin/installer/prompts.js +3 -3
  21. package/dist/bin/installer/prompts.js.map +2 -2
  22. package/dist/bin/installer/steering.js +633 -291
  23. package/dist/bin/installer/steering.js.map +2 -2
  24. package/dist/bin/installer/targets/index.js.map +1 -1
  25. package/dist/bin/installer/targets/kiro.js +5 -4
  26. package/dist/bin/installer/targets/kiro.js.map +2 -2
  27. package/dist/bin/kirograph.js +3 -1
  28. package/dist/bin/kirograph.js.map +3 -3
  29. package/dist/config.js +52 -5
  30. package/dist/config.js.map +2 -2
  31. package/dist/db/database.js +11 -0
  32. package/dist/db/database.js.map +2 -2
  33. package/dist/db/memory-schema.sql +34 -1
  34. package/dist/db/wiki-schema.sql +40 -0
  35. package/dist/mcp/server.js +35 -11
  36. package/dist/mcp/server.js.map +2 -2
  37. package/dist/mcp/tool-names.js +112 -1
  38. package/dist/mcp/tool-names.js.map +2 -2
  39. package/dist/mcp/tools.js +458 -3
  40. package/dist/mcp/tools.js.map +2 -2
  41. package/dist/memory/database.js +131 -7
  42. package/dist/memory/database.js.map +2 -2
  43. package/dist/memory/index.js +130 -4
  44. package/dist/memory/index.js.map +3 -3
  45. package/dist/memory/types.js.map +1 -1
  46. package/dist/watchmen/synthesize.js +2 -0
  47. package/dist/watchmen/synthesize.js.map +2 -2
  48. package/dist/wiki/database.js +143 -0
  49. package/dist/wiki/database.js.map +7 -0
  50. package/dist/wiki/index.js +149 -0
  51. package/dist/wiki/index.js.map +7 -0
  52. package/dist/wiki/ingest.js +259 -0
  53. package/dist/wiki/ingest.js.map +7 -0
  54. package/dist/wiki/lint.js +93 -0
  55. package/dist/wiki/lint.js.map +7 -0
  56. package/dist/wiki/schema.js +148 -0
  57. package/dist/wiki/schema.js.map +7 -0
  58. package/dist/wiki/synthesize.js +104 -0
  59. package/dist/wiki/synthesize.js.map +7 -0
  60. package/dist/wiki/types.js +17 -0
  61. package/dist/wiki/types.js.map +7 -0
  62. package/package.json +5 -3
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var schema_exports = {};
20
+ __export(schema_exports, {
21
+ DEFAULT_SCHEMA: () => DEFAULT_SCHEMA,
22
+ parseWikiDiff: () => parseWikiDiff
23
+ });
24
+ module.exports = __toCommonJS(schema_exports);
25
+ const DEFAULT_SCHEMA = `# Wiki Schema
26
+
27
+ This file tells KiroGraph's LLM how to structure and maintain the project wiki.
28
+ Edit it to match your project's conventions.
29
+
30
+ ## Naming convention
31
+
32
+ - Entities (classes, services, modules): \`<ClassName>.md\` or \`<module-name>.md\`
33
+ - Architecture decisions: \`arch/<slug>.md\`
34
+ - Recurring patterns: \`pattern/<slug>.md\`
35
+ - External integrations: \`ext/<service>.md\`
36
+
37
+ ## Page structure
38
+
39
+ Every page should follow this template:
40
+
41
+ \`\`\`markdown
42
+ ## Summary
43
+ One to three sentences describing what this entity is and why it exists.
44
+
45
+ ## Decisions
46
+ - YYYY-MM-DD \u2014 Decision text (one line per decision, newest first)
47
+
48
+ ## Known Issues / Gotchas
49
+ - Bullet points of non-obvious constraints, bugs, or workarounds
50
+
51
+ ## Related
52
+ - [[other-page-slug]] \u2014 one line description of the relationship
53
+
54
+ ## Sources
55
+ - source-name (YYYY-MM-DD)
56
+ \`\`\`
57
+
58
+ ## Ingest workflow
59
+
60
+ When given a source document to ingest:
61
+
62
+ 1. Read the source and identify entities, decisions, and patterns mentioned
63
+ 2. Check MANIFEST.md to find existing pages for those entities
64
+ 3. For each existing page: integrate new information into the appropriate sections
65
+ 4. For each new entity: create a page following the structure above
66
+ 5. Always update ## Sources with the source name and today's date
67
+ 6. Update ## Related cross-references in all touched pages
68
+ 7. If new information contradicts an existing claim, emit a WIKI_DIFF_CONFLICTS block
69
+
70
+ ## WIKI_DIFF format
71
+
72
+ Produce output using this exact format \u2014 one block per page operation:
73
+
74
+ \`\`\`
75
+ WIKI_DIFF_START
76
+ {"action":"upsert","page":"PageSlug","section":"Decisions","mode":"append"}
77
+ - YYYY-MM-DD \u2014 New decision text
78
+ WIKI_DIFF_END
79
+ \`\`\`
80
+
81
+ Actions:
82
+ - \`create\` \u2014 new page (requires \`title\` field, content is full markdown)
83
+ - \`upsert\` \u2014 update a section in an existing page (mode: \`append\` or \`replace\`)
84
+ - \`append\` \u2014 append to the end of an existing page
85
+
86
+ For conflicts:
87
+ \`\`\`
88
+ WIKI_DIFF_CONFLICTS
89
+ {"page":"PageSlug","section":"SectionName","existing":"old claim","incoming":"new claim","source":"ADR-001.md","existingDate":"2025-01-01","incomingDate":"2025-06-15"}
90
+ WIKI_DIFF_CONFLICTS_END
91
+ \`\`\`
92
+ `;
93
+ function parseWikiDiff(raw) {
94
+ const entries = [];
95
+ const conflicts = [];
96
+ const diffRegex = /WIKI_DIFF_START\n([\s\S]*?)\nWIKI_DIFF_END/g;
97
+ let match;
98
+ while ((match = diffRegex.exec(raw)) !== null) {
99
+ const block = match[1].trim();
100
+ const firstNewline = block.indexOf("\n");
101
+ if (firstNewline === -1) continue;
102
+ const headerStr = block.slice(0, firstNewline).trim();
103
+ const content = block.slice(firstNewline + 1);
104
+ let header;
105
+ try {
106
+ header = JSON.parse(headerStr);
107
+ } catch {
108
+ continue;
109
+ }
110
+ entries.push({
111
+ action: header.action ?? "upsert",
112
+ page: header.page ?? "",
113
+ title: header.title,
114
+ section: header.section,
115
+ mode: header.mode ?? "append",
116
+ content
117
+ });
118
+ }
119
+ const conflictRegex = /WIKI_DIFF_CONFLICTS\n([\s\S]*?)\nWIKI_DIFF_CONFLICTS_END/g;
120
+ while ((match = conflictRegex.exec(raw)) !== null) {
121
+ const block = match[1].trim();
122
+ for (const line of block.split("\n")) {
123
+ const trimmed = line.trim();
124
+ if (!trimmed) continue;
125
+ try {
126
+ const c = JSON.parse(trimmed);
127
+ conflicts.push({
128
+ page: c.page ?? "",
129
+ section: c.section ?? "",
130
+ existing: c.existing ?? "",
131
+ incoming: c.incoming ?? "",
132
+ source: c.source ?? "",
133
+ existingDate: c.existingDate,
134
+ incomingDate: c.incomingDate
135
+ });
136
+ } catch {
137
+ continue;
138
+ }
139
+ }
140
+ }
141
+ return { entries, conflicts, rawDiff: raw };
142
+ }
143
+ // Annotate the CommonJS export names for ESM import in node:
144
+ 0 && (module.exports = {
145
+ DEFAULT_SCHEMA,
146
+ parseWikiDiff
147
+ });
148
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/wiki/schema.ts"],
4
+ "sourcesContent": ["// Default SCHEMA.md content written by `kirograph wiki init`\n\nexport const DEFAULT_SCHEMA = `# Wiki Schema\n\nThis file tells KiroGraph's LLM how to structure and maintain the project wiki.\nEdit it to match your project's conventions.\n\n## Naming convention\n\n- Entities (classes, services, modules): \\`<ClassName>.md\\` or \\`<module-name>.md\\`\n- Architecture decisions: \\`arch/<slug>.md\\`\n- Recurring patterns: \\`pattern/<slug>.md\\`\n- External integrations: \\`ext/<service>.md\\`\n\n## Page structure\n\nEvery page should follow this template:\n\n\\`\\`\\`markdown\n## Summary\nOne to three sentences describing what this entity is and why it exists.\n\n## Decisions\n- YYYY-MM-DD \u2014 Decision text (one line per decision, newest first)\n\n## Known Issues / Gotchas\n- Bullet points of non-obvious constraints, bugs, or workarounds\n\n## Related\n- [[other-page-slug]] \u2014 one line description of the relationship\n\n## Sources\n- source-name (YYYY-MM-DD)\n\\`\\`\\`\n\n## Ingest workflow\n\nWhen given a source document to ingest:\n\n1. Read the source and identify entities, decisions, and patterns mentioned\n2. Check MANIFEST.md to find existing pages for those entities\n3. For each existing page: integrate new information into the appropriate sections\n4. For each new entity: create a page following the structure above\n5. Always update ## Sources with the source name and today's date\n6. Update ## Related cross-references in all touched pages\n7. If new information contradicts an existing claim, emit a WIKI_DIFF_CONFLICTS block\n\n## WIKI_DIFF format\n\nProduce output using this exact format \u2014 one block per page operation:\n\n\\`\\`\\`\nWIKI_DIFF_START\n{\"action\":\"upsert\",\"page\":\"PageSlug\",\"section\":\"Decisions\",\"mode\":\"append\"}\n- YYYY-MM-DD \u2014 New decision text\nWIKI_DIFF_END\n\\`\\`\\`\n\nActions:\n- \\`create\\` \u2014 new page (requires \\`title\\` field, content is full markdown)\n- \\`upsert\\` \u2014 update a section in an existing page (mode: \\`append\\` or \\`replace\\`)\n- \\`append\\` \u2014 append to the end of an existing page\n\nFor conflicts:\n\\`\\`\\`\nWIKI_DIFF_CONFLICTS\n{\"page\":\"PageSlug\",\"section\":\"SectionName\",\"existing\":\"old claim\",\"incoming\":\"new claim\",\"source\":\"ADR-001.md\",\"existingDate\":\"2025-01-01\",\"incomingDate\":\"2025-06-15\"}\nWIKI_DIFF_CONFLICTS_END\n\\`\\`\\`\n`;\n\n// \u2500\u2500 WIKI_DIFF parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nimport type { WikiDiff, WikiDiffEntry, WikiDiffConflict } from './types';\n\nexport function parseWikiDiff(raw: string): WikiDiff {\n const entries: WikiDiffEntry[] = [];\n const conflicts: WikiDiffConflict[] = [];\n\n // Parse WIKI_DIFF_START...WIKI_DIFF_END blocks\n const diffRegex = /WIKI_DIFF_START\\n([\\s\\S]*?)\\nWIKI_DIFF_END/g;\n let match: RegExpExecArray | null;\n\n while ((match = diffRegex.exec(raw)) !== null) {\n const block = match[1].trim();\n const firstNewline = block.indexOf('\\n');\n if (firstNewline === -1) continue;\n\n const headerStr = block.slice(0, firstNewline).trim();\n const content = block.slice(firstNewline + 1);\n\n let header: Record<string, any>;\n try {\n header = JSON.parse(headerStr);\n } catch {\n continue;\n }\n\n entries.push({\n action: header.action ?? 'upsert',\n page: header.page ?? '',\n title: header.title,\n section: header.section,\n mode: header.mode ?? 'append',\n content,\n });\n }\n\n // Parse WIKI_DIFF_CONFLICTS...WIKI_DIFF_CONFLICTS_END blocks\n const conflictRegex = /WIKI_DIFF_CONFLICTS\\n([\\s\\S]*?)\\nWIKI_DIFF_CONFLICTS_END/g;\n\n while ((match = conflictRegex.exec(raw)) !== null) {\n const block = match[1].trim();\n for (const line of block.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const c = JSON.parse(trimmed);\n conflicts.push({\n page: c.page ?? '',\n section: c.section ?? '',\n existing: c.existing ?? '',\n incoming: c.incoming ?? '',\n source: c.source ?? '',\n existingDate: c.existingDate,\n incomingDate: c.incomingDate,\n });\n } catch {\n continue;\n }\n }\n }\n\n return { entries, conflicts, rawDiff: raw };\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,MAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyEvB,SAAS,cAAc,KAAuB;AACnD,QAAM,UAA2B,CAAC;AAClC,QAAM,YAAgC,CAAC;AAGvC,QAAM,YAAY;AAClB,MAAI;AAEJ,UAAQ,QAAQ,UAAU,KAAK,GAAG,OAAO,MAAM;AAC7C,UAAM,QAAQ,MAAM,CAAC,EAAE,KAAK;AAC5B,UAAM,eAAe,MAAM,QAAQ,IAAI;AACvC,QAAI,iBAAiB,GAAI;AAEzB,UAAM,YAAY,MAAM,MAAM,GAAG,YAAY,EAAE,KAAK;AACpD,UAAM,UAAU,MAAM,MAAM,eAAe,CAAC;AAE5C,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,SAAS;AAAA,IAC/B,QAAQ;AACN;AAAA,IACF;AAEA,YAAQ,KAAK;AAAA,MACX,QAAQ,OAAO,UAAU;AAAA,MACzB,MAAM,OAAO,QAAQ;AAAA,MACrB,OAAO,OAAO;AAAA,MACd,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO,QAAQ;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgB;AAEtB,UAAQ,QAAQ,cAAc,KAAK,GAAG,OAAO,MAAM;AACjD,UAAM,QAAQ,MAAM,CAAC,EAAE,KAAK;AAC5B,eAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,QAAS;AACd,UAAI;AACF,cAAM,IAAI,KAAK,MAAM,OAAO;AAC5B,kBAAU,KAAK;AAAA,UACb,MAAM,EAAE,QAAQ;AAAA,UAChB,SAAS,EAAE,WAAW;AAAA,UACtB,UAAU,EAAE,YAAY;AAAA,UACxB,UAAU,EAAE,YAAY;AAAA,UACxB,QAAQ,EAAE,UAAU;AAAA,UACpB,cAAc,EAAE;AAAA,UAChB,cAAc,EAAE;AAAA,QAClB,CAAC;AAAA,MACH,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,WAAW,SAAS,IAAI;AAC5C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var synthesize_exports = {};
20
+ __export(synthesize_exports, {
21
+ runWikiLocalSynthesis: () => runWikiLocalSynthesis
22
+ });
23
+ module.exports = __toCommonJS(synthesize_exports);
24
+ var import_synthesize = require("../watchmen/synthesize");
25
+ const SYSTEM_PROMPT = `You are a project wiki assistant. You receive a source text and a wiki schema, and you produce structured WIKI_DIFF blocks.
26
+
27
+ Rules:
28
+ - Output ONLY WIKI_DIFF_START / WIKI_DIFF_END blocks. No explanations, no prose.
29
+ - Follow the schema exactly \u2014 use the JSON header format shown.
30
+ - Use "page" field (not "slug") in the JSON header.
31
+ - Keep page slugs lowercase-kebab-case.
32
+ - Copy technical terms, function names, and identifiers exactly as they appear in the source.
33
+ - If the source contradicts an existing page, add a WIKI_DIFF_CONFLICTS block.`;
34
+ function isGemma4(modelName) {
35
+ return modelName.includes("gemma-4");
36
+ }
37
+ async function generate(pipe, userPrompt, modelName, quiet) {
38
+ const messages = [
39
+ { role: "system", content: SYSTEM_PROMPT },
40
+ { role: "user", content: userPrompt }
41
+ ];
42
+ const genOpts = {
43
+ max_new_tokens: 600,
44
+ return_full_text: false
45
+ };
46
+ if (isGemma4(modelName)) {
47
+ genOpts.temperature = 1;
48
+ genOpts.top_p = 0.95;
49
+ genOpts.top_k = 64;
50
+ genOpts.do_sample = true;
51
+ } else {
52
+ genOpts.do_sample = false;
53
+ genOpts.repetition_penalty = 1.3;
54
+ }
55
+ const output = await pipe(messages, genOpts);
56
+ let text;
57
+ if (Array.isArray(output) && output[0]?.generated_text) {
58
+ const gt = output[0].generated_text;
59
+ text = Array.isArray(gt) ? gt[gt.length - 1]?.content ?? "" : String(gt);
60
+ } else {
61
+ text = String(output);
62
+ }
63
+ return (0, import_synthesize.removeHallucinatedContent)((0, import_synthesize.truncateAtRepetitionLoop)((0, import_synthesize.stripCodeFences)(text)));
64
+ }
65
+ async function runWikiLocalSynthesis(wiki, modelName, quiet = false) {
66
+ const result = {
67
+ processed: 0,
68
+ created: [],
69
+ updated: [],
70
+ errors: []
71
+ };
72
+ const queue = wiki.getPendingQueue();
73
+ if (queue.length === 0) return result;
74
+ if (!quiet) process.stderr.write(`[wiki] Loading model ${modelName}...
75
+ `);
76
+ const pipe = await (0, import_synthesize.getGenPipeline)(modelName, quiet);
77
+ for (const entry of queue) {
78
+ if (!quiet) process.stderr.write(`[wiki] Synthesizing source "${entry.sourceName}"...
79
+ `);
80
+ try {
81
+ const prompt = wiki.getIngestPrompt(entry.sourceText, entry.sourceName);
82
+ const raw = await generate(pipe, prompt, modelName, quiet);
83
+ if (!raw.includes("WIKI_DIFF_START")) {
84
+ if (!quiet) process.stderr.write(`[wiki] No WIKI_DIFF found for "${entry.sourceName}" \u2014 skipping
85
+ `);
86
+ result.errors.push(`No WIKI_DIFF produced for source "${entry.sourceName}"`);
87
+ continue;
88
+ }
89
+ const applyResult = wiki.applyDiff(raw);
90
+ result.created.push(...applyResult.created);
91
+ result.updated.push(...applyResult.updated);
92
+ result.processed++;
93
+ } catch (e) {
94
+ result.errors.push(`Error processing "${entry.sourceName}": ${e?.message ?? e}`);
95
+ }
96
+ }
97
+ wiki.clearQueue();
98
+ return result;
99
+ }
100
+ // Annotate the CommonJS export names for ESM import in node:
101
+ 0 && (module.exports = {
102
+ runWikiLocalSynthesis
103
+ });
104
+ //# sourceMappingURL=synthesize.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/wiki/synthesize.ts"],
4
+ "sourcesContent": ["/**\n * KiroGraph Wiki \u2014 Local model synthesis\n *\n * Reads the pending wiki_queue, runs a local HuggingFace model to produce\n * WIKI_DIFF blocks for each queued source, then applies them.\n * Mirrors the watchmen local synthesis pattern.\n */\n\nimport {\n getGenPipeline,\n stripCodeFences,\n removeHallucinatedContent,\n truncateAtRepetitionLoop,\n} from '../watchmen/synthesize';\nimport type { KiroGraphWiki } from './index';\n\nexport interface WikiSynthesisResult {\n processed: number;\n created: string[];\n updated: string[];\n errors: string[];\n}\n\nconst SYSTEM_PROMPT = `You are a project wiki assistant. You receive a source text and a wiki schema, and you produce structured WIKI_DIFF blocks.\n\nRules:\n- Output ONLY WIKI_DIFF_START / WIKI_DIFF_END blocks. No explanations, no prose.\n- Follow the schema exactly \u2014 use the JSON header format shown.\n- Use \"page\" field (not \"slug\") in the JSON header.\n- Keep page slugs lowercase-kebab-case.\n- Copy technical terms, function names, and identifiers exactly as they appear in the source.\n- If the source contradicts an existing page, add a WIKI_DIFF_CONFLICTS block.`;\n\nfunction isGemma4(modelName: string): boolean {\n return modelName.includes('gemma-4');\n}\n\nasync function generate(\n pipe: any,\n userPrompt: string,\n modelName: string,\n quiet: boolean,\n): Promise<string> {\n const messages = [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: userPrompt },\n ];\n\n const genOpts: any = {\n max_new_tokens: 600,\n return_full_text: false,\n };\n\n if (isGemma4(modelName)) {\n genOpts.temperature = 1.0;\n genOpts.top_p = 0.95;\n genOpts.top_k = 64;\n genOpts.do_sample = true;\n } else {\n genOpts.do_sample = false;\n genOpts.repetition_penalty = 1.3;\n }\n\n const output = await pipe(messages, genOpts);\n\n let text: string;\n if (Array.isArray(output) && output[0]?.generated_text) {\n const gt = output[0].generated_text;\n text = Array.isArray(gt) ? gt[gt.length - 1]?.content ?? '' : String(gt);\n } else {\n text = String(output);\n }\n\n return removeHallucinatedContent(truncateAtRepetitionLoop(stripCodeFences(text)));\n}\n\nexport async function runWikiLocalSynthesis(\n wiki: KiroGraphWiki,\n modelName: string,\n quiet = false,\n): Promise<WikiSynthesisResult> {\n const result: WikiSynthesisResult = {\n processed: 0,\n created: [],\n updated: [],\n errors: [],\n };\n\n const queue = wiki.getPendingQueue();\n if (queue.length === 0) return result;\n\n if (!quiet) process.stderr.write(`[wiki] Loading model ${modelName}...\\n`);\n const pipe = await getGenPipeline(modelName, quiet);\n\n for (const entry of queue) {\n if (!quiet) process.stderr.write(`[wiki] Synthesizing source \"${entry.sourceName}\"...\\n`);\n try {\n const prompt = wiki.getIngestPrompt(entry.sourceText, entry.sourceName);\n const raw = await generate(pipe, prompt, modelName, quiet);\n\n if (!raw.includes('WIKI_DIFF_START')) {\n if (!quiet) process.stderr.write(`[wiki] No WIKI_DIFF found for \"${entry.sourceName}\" \u2014 skipping\\n`);\n result.errors.push(`No WIKI_DIFF produced for source \"${entry.sourceName}\"`);\n continue;\n }\n\n const applyResult = wiki.applyDiff(raw);\n result.created.push(...applyResult.created);\n result.updated.push(...applyResult.updated);\n result.processed++;\n } catch (e: any) {\n result.errors.push(`Error processing \"${entry.sourceName}\": ${e?.message ?? e}`);\n }\n }\n\n wiki.clearQueue();\n return result;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,wBAKO;AAUP,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUtB,SAAS,SAAS,WAA4B;AAC5C,SAAO,UAAU,SAAS,SAAS;AACrC;AAEA,eAAe,SACb,MACA,YACA,WACA,OACiB;AACjB,QAAM,WAAW;AAAA,IACf,EAAE,MAAM,UAAU,SAAS,cAAc;AAAA,IACzC,EAAE,MAAM,QAAQ,SAAS,WAAW;AAAA,EACtC;AAEA,QAAM,UAAe;AAAA,IACnB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EACpB;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ,cAAc;AACtB,YAAQ,QAAQ;AAChB,YAAQ,QAAQ;AAChB,YAAQ,YAAY;AAAA,EACtB,OAAO;AACL,YAAQ,YAAY;AACpB,YAAQ,qBAAqB;AAAA,EAC/B;AAEA,QAAM,SAAS,MAAM,KAAK,UAAU,OAAO;AAE3C,MAAI;AACJ,MAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,CAAC,GAAG,gBAAgB;AACtD,UAAM,KAAK,OAAO,CAAC,EAAE;AACrB,WAAO,MAAM,QAAQ,EAAE,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,WAAW,KAAK,OAAO,EAAE;AAAA,EACzE,OAAO;AACL,WAAO,OAAO,MAAM;AAAA,EACtB;AAEA,aAAO,iDAA0B,gDAAyB,mCAAgB,IAAI,CAAC,CAAC;AAClF;AAEA,eAAsB,sBACpB,MACA,WACA,QAAQ,OACsB;AAC9B,QAAM,SAA8B;AAAA,IAClC,WAAW;AAAA,IACX,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,IACV,QAAQ,CAAC;AAAA,EACX;AAEA,QAAM,QAAQ,KAAK,gBAAgB;AACnC,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,MAAI,CAAC,MAAO,SAAQ,OAAO,MAAM,wBAAwB,SAAS;AAAA,CAAO;AACzE,QAAM,OAAO,UAAM,kCAAe,WAAW,KAAK;AAElD,aAAW,SAAS,OAAO;AACzB,QAAI,CAAC,MAAO,SAAQ,OAAO,MAAM,+BAA+B,MAAM,UAAU;AAAA,CAAQ;AACxF,QAAI;AACF,YAAM,SAAS,KAAK,gBAAgB,MAAM,YAAY,MAAM,UAAU;AACtE,YAAM,MAAM,MAAM,SAAS,MAAM,QAAQ,WAAW,KAAK;AAEzD,UAAI,CAAC,IAAI,SAAS,iBAAiB,GAAG;AACpC,YAAI,CAAC,MAAO,SAAQ,OAAO,MAAM,kCAAkC,MAAM,UAAU;AAAA,CAAgB;AACnG,eAAO,OAAO,KAAK,qCAAqC,MAAM,UAAU,GAAG;AAC3E;AAAA,MACF;AAEA,YAAM,cAAc,KAAK,UAAU,GAAG;AACtC,aAAO,QAAQ,KAAK,GAAG,YAAY,OAAO;AAC1C,aAAO,QAAQ,KAAK,GAAG,YAAY,OAAO;AAC1C,aAAO;AAAA,IACT,SAAS,GAAQ;AACf,aAAO,OAAO,KAAK,qBAAqB,MAAM,UAAU,MAAM,GAAG,WAAW,CAAC,EAAE;AAAA,IACjF;AAAA,EACF;AAEA,OAAK,WAAW;AAChB,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/wiki/types.ts"],
4
+ "sourcesContent": ["// \u2500\u2500 WikiPage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WikiPage {\n slug: string;\n title: string;\n content: string;\n filePath: string;\n updatedAt: number;\n sourceCount: number;\n}\n\nexport interface ScoredWikiPage {\n page: WikiPage;\n score: number;\n}\n\n// \u2500\u2500 WIKI_DIFF types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DiffAction = 'upsert' | 'create' | 'append';\nexport type DiffMode = 'replace' | 'append';\n\nexport interface WikiDiffEntry {\n action: DiffAction;\n page: string; // slug\n title?: string; // required for action=create\n section?: string; // target section heading (e.g. \"Decisions\")\n mode?: DiffMode; // for action=upsert: replace section or append to it\n content: string; // markdown content to write\n}\n\nexport interface WikiDiffConflict {\n page: string;\n section: string;\n existing: string;\n incoming: string;\n source: string;\n existingDate?: string; // ISO date string\n incomingDate?: string; // ISO date string\n}\n\nexport interface WikiDiff {\n entries: WikiDiffEntry[];\n conflicts: WikiDiffConflict[];\n rawDiff: string;\n}\n\n// \u2500\u2500 Lint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type LintIssueKind = 'contradiction' | 'orphan' | 'stale' | 'broken_link';\n\nexport interface WikiLintIssue {\n kind: LintIssueKind;\n slug: string;\n detail: string;\n relatedSlug?: string;\n}\n\n// \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface WikiStats {\n pageCount: number;\n totalSources: number;\n oldestPage: number | null;\n newestPage: number | null;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kirograph",
3
- "version": "0.22.0",
3
+ "version": "0.26.0",
4
4
  "description": "Semantic code knowledge graph for Kiro: fewer tool calls, instant symbol lookups, 100% local.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,6 +24,8 @@
24
24
  "test:turboquant": "bash scripts/turboquant/test.sh",
25
25
  "test:turbovec": "bash scripts/turbovec/test.sh",
26
26
  "test:data": "bash scripts/data/test.sh",
27
+ "test:wiki": "bash scripts/wiki/test.sh",
28
+ "test:mem": "bash scripts/mem/test.sh",
27
29
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
28
30
  "release": "./scripts/release.sh",
29
31
  "docs": "npx serve docs"
@@ -44,10 +46,10 @@
44
46
  "parquetjs-lite": "^0.8.7",
45
47
  "picomatch": "^4.0.3",
46
48
  "tree-sitter-wasms": "^0.1.11",
47
- "web-tree-sitter": "^0.25.3",
48
- "xlsx": "^0.18.5"
49
+ "web-tree-sitter": "^0.25.3"
49
50
  },
50
51
  "optionalDependencies": {
52
+ "xlsx": "^0.18.5",
51
53
  "@ast-grep/lang-bash": "^0.0.8",
52
54
  "@ast-grep/lang-c": "^0.0.6",
53
55
  "@ast-grep/lang-cpp": "^0.0.6",