kirograph 0.23.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
package/dist/mcp/tools.js CHANGED
@@ -475,7 +475,9 @@ const tools = [
475
475
  enum: ["decision", "error", "pattern", "architecture", "summary", "note"],
476
476
  default: "note"
477
477
  },
478
- projectPath: { type: "string", description: "Project root path (optional)" }
478
+ projectPath: { type: "string", description: "Project root path (optional)" },
479
+ topicKey: { type: "string", description: "Stable semantic key for this observation (e.g. 'architecture/auth-model'). Enables addressing by concept." },
480
+ reviewAfter: { type: "number", description: "Epoch ms: schedule this observation for re-evaluation after this timestamp." }
479
481
  },
480
482
  required: ["content"]
481
483
  }
@@ -502,6 +504,182 @@ const tools = [
502
504
  }
503
505
  }
504
506
  },
507
+ // ── Engram-parity mem tools ──────────────────────────────────────────────────
508
+ {
509
+ name: "kirograph_mem_review",
510
+ description: "List observations past their review_after date \u2014 stale facts the agent should re-evaluate, update, or supersede.",
511
+ inputSchema: {
512
+ type: "object",
513
+ properties: {
514
+ limit: { type: "number", description: "Max results (default: 20)", default: 20 },
515
+ projectPath: { type: "string", description: "Project root path (optional)" }
516
+ }
517
+ }
518
+ },
519
+ {
520
+ name: "kirograph_mem_mark_reviewed",
521
+ description: "Mark an observation as reviewed \u2014 clears its review_after date.",
522
+ inputSchema: {
523
+ type: "object",
524
+ properties: {
525
+ id: { type: "string", description: "Observation ID" },
526
+ projectPath: { type: "string", description: "Project root path (optional)" }
527
+ },
528
+ required: ["id"]
529
+ }
530
+ },
531
+ {
532
+ name: "kirograph_mem_compare",
533
+ description: "Establish a typed relation between two observations (supersedes, conflicts_with, compatible, scoped, related, not_conflict). Accepts observation IDs or topic_key values.",
534
+ inputSchema: {
535
+ type: "object",
536
+ properties: {
537
+ observationA: { type: "string", description: "First observation ID or topic_key" },
538
+ observationB: { type: "string", description: "Second observation ID or topic_key" },
539
+ relation: { type: "string", enum: ["supersedes", "conflicts_with", "compatible", "scoped", "related", "not_conflict"], description: "Relation type" },
540
+ confidence: { type: "number", description: "Confidence 0.0\u20131.0 (default: 1.0)" },
541
+ reason: { type: "string", description: "Explanation for the relation" },
542
+ evidence: { type: "string", description: "Supporting evidence text" },
543
+ projectPath: { type: "string", description: "Project root path (optional)" }
544
+ },
545
+ required: ["observationA", "observationB", "relation"]
546
+ }
547
+ },
548
+ {
549
+ name: "kirograph_mem_judge",
550
+ description: "Finalize a pending conflict relation \u2014 confirm, revise, or dismiss it.",
551
+ inputSchema: {
552
+ type: "object",
553
+ properties: {
554
+ relationId: { type: "string", description: "Relation ID (from kirograph_mem_compare or kirograph_mem_search)" },
555
+ relation: { type: "string", enum: ["supersedes", "conflicts_with", "compatible", "scoped", "related", "not_conflict"], description: "Final relation type" },
556
+ confidence: { type: "number", description: "Final confidence 0.0\u20131.0" },
557
+ reason: { type: "string", description: "Reasoning for judgment" },
558
+ evidence: { type: "string", description: "Supporting evidence" },
559
+ projectPath: { type: "string", description: "Project root path (optional)" }
560
+ },
561
+ required: ["relationId", "relation", "confidence"]
562
+ }
563
+ },
564
+ {
565
+ name: "kirograph_mem_capture",
566
+ description: "Extract and store structured learnings from a freeform text block. Looks for ## Key Learnings, ## Observations, ## Decisions, ## Key Changes sections and saves each bullet as a separate observation.",
567
+ inputSchema: {
568
+ type: "object",
569
+ properties: {
570
+ content: { type: "string", description: "Text block with structured sections" },
571
+ projectPath: { type: "string", description: "Project root path (optional)" }
572
+ },
573
+ required: ["content"]
574
+ }
575
+ },
576
+ {
577
+ name: "kirograph_mem_save_prompt",
578
+ description: "Save the current user prompt to session memory for context reconstruction.",
579
+ inputSchema: {
580
+ type: "object",
581
+ properties: {
582
+ content: { type: "string", description: "Prompt content to save" },
583
+ projectPath: { type: "string", description: "Project root path (optional)" }
584
+ },
585
+ required: ["content"]
586
+ }
587
+ },
588
+ {
589
+ name: "kirograph_mem_suggest_topic_key",
590
+ description: 'Suggest a stable topic_key for an observation based on its kind and content. Returns a deterministic slug like "architecture/auth-model".',
591
+ inputSchema: {
592
+ type: "object",
593
+ properties: {
594
+ kind: { type: "string", enum: ["decision", "error", "pattern", "architecture", "summary", "note"], description: "Observation kind" },
595
+ title: { type: "string", description: "Short title or first sentence of the observation" },
596
+ projectPath: { type: "string", description: "Project root path (optional)" }
597
+ },
598
+ required: ["kind", "title"]
599
+ }
600
+ },
601
+ {
602
+ name: "kirograph_mem_conflicts_scan",
603
+ description: "Scan recent observations for potential conflicts using FTS similarity. Returns candidate pairs that may need a relation established via kirograph_mem_compare.",
604
+ inputSchema: {
605
+ type: "object",
606
+ properties: {
607
+ limit: { type: "number", description: "Max observations to scan (default: 50)", default: 50 },
608
+ projectPath: { type: "string", description: "Project root path (optional)" }
609
+ }
610
+ }
611
+ },
612
+ // ── Wiki tools (require enableWiki=true) ────────────────────────────────────
613
+ {
614
+ name: "kirograph_wiki_ingest",
615
+ description: "Returns an ingest prompt (SCHEMA + MANIFEST + source content) for the LLM to produce a WIKI_DIFF. Call kirograph_wiki_apply_diff with the result.",
616
+ inputSchema: {
617
+ type: "object",
618
+ properties: {
619
+ source: { type: "string", description: "Source content to ingest (markdown, notes, ADR text, etc.)" },
620
+ sourceName: { type: "string", description: 'Name or path of the source (e.g. "ADR-001.md")', default: "source" },
621
+ projectPath: { type: "string", description: "Project root path (optional)" }
622
+ },
623
+ required: ["source"]
624
+ }
625
+ },
626
+ {
627
+ name: "kirograph_wiki_apply_diff",
628
+ description: "Apply a WIKI_DIFF string (produced by the LLM after kirograph_wiki_ingest) to the wiki filesystem and SQLite index.",
629
+ inputSchema: {
630
+ type: "object",
631
+ properties: {
632
+ diff: { type: "string", description: "WIKI_DIFF string with WIKI_DIFF_START/END blocks" },
633
+ projectPath: { type: "string", description: "Project root path (optional)" }
634
+ },
635
+ required: ["diff"]
636
+ }
637
+ },
638
+ {
639
+ name: "kirograph_wiki_search",
640
+ description: "Full-text search over wiki pages. Returns page slugs, titles, and previews ranked by relevance.",
641
+ inputSchema: {
642
+ type: "object",
643
+ properties: {
644
+ query: { type: "string", description: "Search query" },
645
+ limit: { type: "number", description: "Max results (default: 5)", default: 5 },
646
+ projectPath: { type: "string", description: "Project root path (optional)" }
647
+ },
648
+ required: ["query"]
649
+ }
650
+ },
651
+ {
652
+ name: "kirograph_wiki_page",
653
+ description: "Retrieve the full markdown content of a wiki page by slug.",
654
+ inputSchema: {
655
+ type: "object",
656
+ properties: {
657
+ slug: { type: "string", description: 'Page slug (e.g. "AuthService", "arch/auth-model")' },
658
+ projectPath: { type: "string", description: "Project root path (optional)" }
659
+ },
660
+ required: ["slug"]
661
+ }
662
+ },
663
+ {
664
+ name: "kirograph_wiki_lint",
665
+ description: "Health check the wiki for broken links, orphan pages, stale sources, and potential contradictions between pages.",
666
+ inputSchema: {
667
+ type: "object",
668
+ properties: {
669
+ projectPath: { type: "string", description: "Project root path (optional)" }
670
+ }
671
+ }
672
+ },
673
+ {
674
+ name: "kirograph_wiki_list",
675
+ description: "List all wiki pages with slug, title, source count, and last updated date.",
676
+ inputSchema: {
677
+ type: "object",
678
+ properties: {
679
+ projectPath: { type: "string", description: "Project root path (optional)" }
680
+ }
681
+ }
682
+ },
505
683
  // ── Docs tools (require enableDocs=true) ────────────────────────────────────
506
684
  {
507
685
  name: "kirograph_docs_toc",
@@ -2031,7 +2209,15 @@ ${result.content}`;
2031
2209
  if (results.length === 0) return `No memory observations found for "${args.query}".`;
2032
2210
  return results.map((r, i) => {
2033
2211
  const age = formatAge(r.observation.createdAt);
2034
- return `${i + 1}. [${r.observation.kind}] ${r.observation.content} (${age})`;
2212
+ const lines = [`${i + 1}. [${r.observation.kind}] ${r.observation.content} (${age})`];
2213
+ if (r.relations && r.relations.length > 0) {
2214
+ for (const rel of r.relations) {
2215
+ const other = rel.observationA === r.observation.id ? rel.observationB : rel.observationA;
2216
+ const icon = rel.relation === "conflicts_with" ? "\u26A1" : rel.relation === "supersedes" ? "\u21A9" : "~";
2217
+ lines.push(` ${icon} ${rel.relation} ${other} (confidence: ${rel.confidence})${rel.judgmentStatus === "judged" ? " (judged)" : ""}`);
2218
+ }
2219
+ }
2220
+ return lines.join("\n");
2035
2221
  }).join("\n");
2036
2222
  }
2037
2223
  case "kirograph_mem_store": {
@@ -2047,7 +2233,9 @@ ${result.content}`;
2047
2233
  const id = await mem.store({
2048
2234
  content: args.content,
2049
2235
  kind: args.kind ?? "note",
2050
- source: "agent"
2236
+ source: "agent",
2237
+ topicKey: args.topicKey,
2238
+ reviewAfter: args.reviewAfter
2051
2239
  });
2052
2240
  if (!id) return "Observation already exists (duplicate content).";
2053
2241
  return `Stored observation ${id} [${args.kind ?? "note"}]`;
@@ -2098,10 +2286,139 @@ ${result.content}`;
2098
2286
  ` Symbol links: ${stats.links}`,
2099
2287
  ` Embeddings: ${stats.vectors} / ${stats.embeddableCount}`,
2100
2288
  ` Model mismatch: ${stats.modelMismatch ? "\u26A0 yes \u2014 run kirograph mem reembed" : "no"}`,
2289
+ ` Relations: ${stats.relations} (${stats.pendingConflicts} pending)`,
2101
2290
  ` Caveman compression: ${config.cavemanMode !== "off" ? config.cavemanMode : "off (storing raw)"}`
2102
2291
  ];
2103
2292
  return lines.join("\n");
2104
2293
  }
2294
+ case "kirograph_mem_review": {
2295
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2296
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2297
+ const config = await loadConfig(projectRoot);
2298
+ if (!config.enableMemory) return "Memory is not enabled.";
2299
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2300
+ const db = cg.getDatabase();
2301
+ db.applyMemorySchema();
2302
+ const mem = new MemoryManager(config, db.getRawDb());
2303
+ mem.initialize();
2304
+ const obs = mem.getObservationsForReview(args.limit ?? 20);
2305
+ if (obs.length === 0) return "No overdue observations \u2014 all review_after dates are in the future.";
2306
+ const now = Date.now();
2307
+ return obs.map((o, i) => {
2308
+ const daysOverdue = Math.floor((now - (o.reviewAfter ?? now)) / 864e5);
2309
+ return `${i + 1}. [${o.kind}]${o.topicKey ? ` (${o.topicKey})` : ""} ${o.content.slice(0, 120)} \u2014 overdue by ${daysOverdue}d`;
2310
+ }).join("\n");
2311
+ }
2312
+ case "kirograph_mem_mark_reviewed": {
2313
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2314
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2315
+ const config = await loadConfig(projectRoot);
2316
+ if (!config.enableMemory) return "Memory is not enabled.";
2317
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2318
+ const db = cg.getDatabase();
2319
+ db.applyMemorySchema();
2320
+ const mem = new MemoryManager(config, db.getRawDb());
2321
+ mem.initialize();
2322
+ mem.markReviewed(args.id);
2323
+ return `Observation ${args.id} marked as reviewed.`;
2324
+ }
2325
+ case "kirograph_mem_compare": {
2326
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2327
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2328
+ const config = await loadConfig(projectRoot);
2329
+ if (!config.enableMemory) return "Memory is not enabled.";
2330
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2331
+ const db = cg.getDatabase();
2332
+ db.applyMemorySchema();
2333
+ const mem = new MemoryManager(config, db.getRawDb());
2334
+ mem.initialize();
2335
+ const relationId = mem.compareObservations({
2336
+ observationA: args.observationA,
2337
+ observationB: args.observationB,
2338
+ relation: args.relation,
2339
+ confidence: args.confidence ?? 1,
2340
+ reason: args.reason,
2341
+ evidence: args.evidence
2342
+ });
2343
+ return `Relation ${relationId} created (${args.relation}, confidence: ${args.confidence ?? 1}). Use kirograph_mem_judge to finalize.`;
2344
+ }
2345
+ case "kirograph_mem_judge": {
2346
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2347
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2348
+ const config = await loadConfig(projectRoot);
2349
+ if (!config.enableMemory) return "Memory is not enabled.";
2350
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2351
+ const db = cg.getDatabase();
2352
+ db.applyMemorySchema();
2353
+ const mem = new MemoryManager(config, db.getRawDb());
2354
+ mem.initialize();
2355
+ mem.judgeRelation(
2356
+ args.relationId,
2357
+ args.relation,
2358
+ args.confidence,
2359
+ args.reason,
2360
+ args.evidence
2361
+ );
2362
+ return `Relation ${args.relationId} judged as [${args.relation}] (confidence: ${args.confidence}).`;
2363
+ }
2364
+ case "kirograph_mem_capture": {
2365
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2366
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2367
+ const config = await loadConfig(projectRoot);
2368
+ if (!config.enableMemory) return "Memory is not enabled.";
2369
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2370
+ const db = cg.getDatabase();
2371
+ db.applyMemorySchema();
2372
+ const mem = new MemoryManager(config, db.getRawDb());
2373
+ mem.initialize();
2374
+ const extracted = mem.capturePassive(args.content);
2375
+ if (extracted.length === 0) return "No structured sections found. Use ## Key Learnings, ## Observations, ## Decisions, or ## Key Changes headings.";
2376
+ return `Captured ${extracted.length} observation(s):
2377
+ ` + extracted.map((e, i) => `${i + 1}. [${e.kind}] ${e.content.slice(0, 100)}${e.id ? "" : " (duplicate)"}`).join("\n");
2378
+ }
2379
+ case "kirograph_mem_save_prompt": {
2380
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2381
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2382
+ const config = await loadConfig(projectRoot);
2383
+ if (!config.enableMemory) return "Memory is not enabled.";
2384
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2385
+ const db = cg.getDatabase();
2386
+ db.applyMemorySchema();
2387
+ const mem = new MemoryManager(config, db.getRawDb());
2388
+ mem.initialize();
2389
+ const id = await mem.savePrompt(args.content);
2390
+ return `Prompt saved as ${id}.`;
2391
+ }
2392
+ case "kirograph_mem_suggest_topic_key": {
2393
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2394
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2395
+ const config = await loadConfig(projectRoot);
2396
+ if (!config.enableMemory) return "Memory is not enabled.";
2397
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2398
+ const db = cg.getDatabase();
2399
+ db.applyMemorySchema();
2400
+ const mem = new MemoryManager(config, db.getRawDb());
2401
+ mem.initialize();
2402
+ const key = mem.suggestTopicKey(args.kind, args.title);
2403
+ return `Suggested topic_key: ${key}`;
2404
+ }
2405
+ case "kirograph_mem_conflicts_scan": {
2406
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2407
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2408
+ const config = await loadConfig(projectRoot);
2409
+ if (!config.enableMemory) return "Memory is not enabled.";
2410
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2411
+ const db = cg.getDatabase();
2412
+ db.applyMemorySchema();
2413
+ const mem = new MemoryManager(config, db.getRawDb());
2414
+ mem.initialize();
2415
+ const candidates = mem.scanConflicts(args.limit ?? 50);
2416
+ if (candidates.length === 0) return "No potential conflicts found among recent observations.";
2417
+ return `Found ${candidates.length} potential conflict(s):
2418
+ ` + candidates.map(
2419
+ (c, i) => `${i + 1}. [${c.observationA.kind}] "${c.observationA.content.slice(0, 80)}" \u2194 [${c.observationB.kind}] "${c.observationB.content.slice(0, 80)}" (similarity: ${c.similarity.toFixed(2)})`
2420
+ ).join("\n") + "\n\nUse kirograph_mem_compare to establish relations.";
2421
+ }
2105
2422
  // ── Docs tools ────────────────────────────────────────────────────────────
2106
2423
  case "kirograph_docs_toc": {
2107
2424
  const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
@@ -3385,6 +3702,144 @@ ${lines.join("\n")}`;
3385
3702
  }
3386
3703
  return lines.join("\n");
3387
3704
  }
3705
+ // ── Wiki tools ────────────────────────────────────────────────────────────
3706
+ case "kirograph_wiki_ingest": {
3707
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3708
+ const projectRoot = cg.getProjectRoot();
3709
+ const config = await loadConfig(projectRoot);
3710
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3711
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3712
+ const db = cg.getDatabase();
3713
+ db.applyWikiSchema();
3714
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph", {
3715
+ autoResolveConflicts: config.wikiAutoResolveConflicts ?? false
3716
+ });
3717
+ wiki.initialize();
3718
+ const source = args.source ?? "";
3719
+ const sourceName = args.sourceName ?? "source";
3720
+ if (!source) return "source is required";
3721
+ if (config.wikiSynthesisMode === "local") {
3722
+ wiki.queueSource(source, sourceName);
3723
+ const count = wiki.getQueueCount();
3724
+ return `Source "${sourceName}" queued for local wiki synthesis (${count} in queue). The agentStop hook will run "kirograph wiki synthesize" automatically.`;
3725
+ }
3726
+ const prompt = wiki.getIngestPrompt(source, sourceName);
3727
+ return `WIKI_INGEST_PROMPT
3728
+ ${prompt}
3729
+
3730
+ Produce WIKI_DIFF blocks following SCHEMA.md, then call kirograph_wiki_apply_diff with the diff string.`;
3731
+ }
3732
+ case "kirograph_wiki_apply_diff": {
3733
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3734
+ const projectRoot = cg.getProjectRoot();
3735
+ const config = await loadConfig(projectRoot);
3736
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3737
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3738
+ const db = cg.getDatabase();
3739
+ db.applyWikiSchema();
3740
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph", {
3741
+ autoResolveConflicts: config.wikiAutoResolveConflicts ?? false
3742
+ });
3743
+ wiki.initialize();
3744
+ const diff = args.diff ?? "";
3745
+ if (!diff) return "diff is required";
3746
+ const result = wiki.applyDiff(diff);
3747
+ const lines = ["\u2713 Wiki diff applied"];
3748
+ if (result.created.length) lines.push(` Created: ${result.created.join(", ")}`);
3749
+ if (result.updated.length) lines.push(` Updated: ${result.updated.join(", ")}`);
3750
+ if (result.conflictsResolved.length) lines.push(` Conflicts auto-resolved: ${result.conflictsResolved.join(", ")}`);
3751
+ if (result.conflictsPending.length) {
3752
+ lines.push(` \u26A1 Conflicts pending (${result.conflictsPending.length}):`);
3753
+ for (const c of result.conflictsPending) {
3754
+ lines.push(` ${c.page} \xA7${c.section}: "${c.existing}" vs "${c.incoming}" (source: ${c.source})`);
3755
+ }
3756
+ }
3757
+ return lines.join("\n");
3758
+ }
3759
+ case "kirograph_wiki_search": {
3760
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3761
+ const projectRoot = cg.getProjectRoot();
3762
+ const config = await loadConfig(projectRoot);
3763
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3764
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3765
+ const db = cg.getDatabase();
3766
+ db.applyWikiSchema();
3767
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
3768
+ wiki.initialize();
3769
+ const query = args.query ?? "";
3770
+ if (!query) return "query is required";
3771
+ const limit = Math.min(Number(args.limit ?? 5), 20);
3772
+ const results = wiki.search(query, limit);
3773
+ if (results.length === 0) return `No wiki pages found for "${query}".`;
3774
+ const lines = [`Wiki Search: ${query}`, ""];
3775
+ for (const { page, score } of results) {
3776
+ lines.push(`[${page.slug}] ${page.title} (score: ${score.toFixed(3)})`);
3777
+ const preview = page.content.split("\n").find((l) => l.trim() && !l.startsWith("#"));
3778
+ if (preview) lines.push(` ${preview.trim().slice(0, 120)}`);
3779
+ lines.push("");
3780
+ }
3781
+ return lines.join("\n").trim();
3782
+ }
3783
+ case "kirograph_wiki_page": {
3784
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3785
+ const projectRoot = cg.getProjectRoot();
3786
+ const config = await loadConfig(projectRoot);
3787
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3788
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3789
+ const db = cg.getDatabase();
3790
+ db.applyWikiSchema();
3791
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
3792
+ wiki.initialize();
3793
+ const slug = args.slug ?? "";
3794
+ if (!slug) return "slug is required";
3795
+ const page = wiki.getPage(slug);
3796
+ if (!page) return `Wiki page "${slug}" not found. Use kirograph_wiki_list to see available pages.`;
3797
+ return page.content;
3798
+ }
3799
+ case "kirograph_wiki_lint": {
3800
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3801
+ const projectRoot = cg.getProjectRoot();
3802
+ const config = await loadConfig(projectRoot);
3803
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3804
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3805
+ const db = cg.getDatabase();
3806
+ db.applyWikiSchema();
3807
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
3808
+ wiki.initialize();
3809
+ const issues = wiki.lint();
3810
+ if (issues.length === 0) return "\u2713 Wiki lint passed \u2014 no issues found.";
3811
+ const lines = [`Wiki Lint \u2014 ${issues.length} issue(s)`, ""];
3812
+ for (const issue of issues) {
3813
+ const icon = issue.kind === "contradiction" ? "\u26A1" : issue.kind === "orphan" ? "\u25CB" : issue.kind === "broken_link" ? "\u{1F517}" : "\u26A0";
3814
+ lines.push(`${icon} [${issue.kind}] ${issue.slug}`);
3815
+ lines.push(` ${issue.detail}`);
3816
+ lines.push("");
3817
+ }
3818
+ return lines.join("\n").trim();
3819
+ }
3820
+ case "kirograph_wiki_list": {
3821
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3822
+ const projectRoot = cg.getProjectRoot();
3823
+ const config = await loadConfig(projectRoot);
3824
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
3825
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
3826
+ const db = cg.getDatabase();
3827
+ db.applyWikiSchema();
3828
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
3829
+ wiki.initialize();
3830
+ const pages = wiki.listPages();
3831
+ if (pages.length === 0) return "Wiki is empty. Use kirograph_wiki_ingest to add pages.";
3832
+ const stats = wiki.getStats();
3833
+ const lines = [
3834
+ `Wiki \u2014 ${stats.pageCount} page(s), ${stats.totalSources} total sources`,
3835
+ ""
3836
+ ];
3837
+ for (const page of pages) {
3838
+ const date = new Date(page.updatedAt).toISOString().slice(0, 10);
3839
+ lines.push(` ${page.slug.padEnd(32)} ${page.title} (${page.sourceCount} src, ${date})`);
3840
+ }
3841
+ return lines.join("\n");
3842
+ }
3388
3843
  default:
3389
3844
  return `Unknown tool: ${toolName}`;
3390
3845
  }