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
package/README.md CHANGED
@@ -43,8 +43,9 @@ The result is fewer tool calls, less context used, and faster responses on compl
43
43
  | <h4>Security</h4> | |
44
44
  | 🔒 **Security (KiroGraph-Sec opt-in module)** | Goes beyond "this dependency has a CVE" — uses the call graph to determine if vulnerable code is **actually reachable** from your entry points. Maps your **attack surface** (which HTTP routes reach vulnerable deps). Detects **hardcoded secrets** and shows how many entry points expose them. **SAST-lite** finds SQL injection, path traversal, and dangerous eval in your code. **AST-based SAST (opt-in via `enablePatterns`)** runs 10 bundled structural pattern rules via `@ast-grep/napi` — matches actual code structure, not just symbol names. **Supply chain health** checks OpenSSF Scorecard scores and detects dependency confusion attacks. Covers 14 ecosystems, outputs CycloneDX SBOM/VEX and CI-ready SARIF reports. |
45
45
  | <h4>Knowledge & Data</h4> | |
46
- | 🧠 **Persistent Memor (KiroGraph-Mem opt-in module)** | Cross-session observations — decisions, errors, patterns — auto-linked to code symbols |
46
+ | 🧠 **Persistent Memory (KiroGraph-Mem opt-in module)** | Cross-session observations — decisions, errors, patterns — auto-linked to code symbols. **Conflict detection**: typed relations between observations (`supersedes`, `conflicts_with`, `compatible`) with agent judgment workflow (`kirograph_mem_compare`, `kirograph_mem_judge`). **Stale review**: schedule observations for re-evaluation (`review_after` + `kirograph_mem_review`). **Passive capture**: extract learnings from structured text. **Prompt saving**: session context reconstruction. **Topic key**: stable semantic addressing. Inspired by [Engram](https://github.com/Gentleman-Programming/engram). |
47
47
  | 👁️ **Watchmen (KiroGraph-Watchmen opt-in module)** ⚠️ *experimental* | Auto-synthesizes accumulated memory observations into workspace briefs and `inclusion: manual` skill files. Fires via the `watchmenReady` signal in `kirograph_mem_store` when threshold is reached. **Local model** (default, `watchmenSynthesisMode: 'local'`): runs `gemma-4-E4B-it-ONNX` on-device via `@huggingface/transformers` — ~3–4 GB one-time download, ~3–5 GB RAM, 8–15 s on Apple Silicon M1+. No API key, no background daemon, no external calls. **Agent mode** also available for Kiro (`watchmenSynthesisMode: 'agent'`, uses active session). ⚠️ **Experimental:** output quality in local mode depends heavily on the model chosen and your hardware. Smaller models or slower machines may produce incomplete briefs and lower-quality skill files. Use `agent` mode for best results. |
48
+ | 📚 **Wiki (KiroGraph-Wiki opt-in module)** | Karpathy-style LLM wiki — a set of markdown pages that compound knowledge across sessions. Three ops: **ingest** (build structured prompt from source text), **apply-diff** (write LLM-generated WIKI_DIFF to SQLite + disk), **lint** (broken links, orphan pages, contradictions). Context enrichment: `kirograph_context` auto-includes relevant wiki pages above score threshold. Two synthesis modes: `agent` (IDE LLM) or `local` (HuggingFace, same infra as Watchmen). Conflict resolution: deterministic by source date when `wikiAutoResolveConflicts: true`. Inspired by [Karpathy's LLM Wiki pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f). |
48
49
  | 📖 **Documentation Indexing (KiroGraph-Doc opt-in module)** | Section-level retrieval from Markdown, MDX, RST, AsciiDoc, OpenAPI — 92-97% token savings |
49
50
  | 📊 **Data Navigation (KiroGraph-Data opt-in module)** | Query CSV/JSON/Excel/Parquet/**PDF** with filters, aggregations, joins — all server-side in SQLite |
50
51
  | <h4>Token Optimization</h4> | |
@@ -142,11 +143,18 @@ KiroGraph is inspired by [CodeGraph](https://github.com/colbymchenry/codegraph)
142
143
  ### Inspirations
143
144
 
144
145
  - [cavemem](https://github.com/JuliusBrussee/cavemem) by [Julius Brussee](https://www.linkedin.com/in/julius-brussee/): the memory module's hook-based observation capture, deterministic compression, and SQLite storage pattern.
146
+ - [Engram](https://github.com/Gentleman-Programming/engram) by [Gentleman-Programming](https://github.com/Gentleman-Programming): conflict detection (typed relations + judgment workflow), `topic_key` stable addressing, `review_after` stale observation scheduling, passive capture, and prompt saving patterns.
147
+ - [caveman](https://github.com/JuliusBrussee/caveman) by [Julius Brussee](https://www.linkedin.com/in/julius-brussee/): the caveman mode's agent prose compression concept, multi-level steering injection.
145
148
  - [watchmen](https://github.com/firstbatchxyz/watchmen) by [firstbatch](https://github.com/firstbatchxyz): the watchmen module's session-mining concept, workspace brief generation, and `AGENTS.md` mirroring pattern.
149
+ - [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) by [Andrej Karpathy](https://karpathy.ai/): the wiki module's three-op pattern (ingest → apply → lint), WIKI_DIFF block format, two-tool ingest flow, and the principle of knowledge compounding rather than accumulating.
146
150
  - [jDocMunch-MCP](https://github.com/jgravelle/jdocmunch-mcp) by [J. Gravelle](https://www.linkedin.com/in/j-gravelle-2778223/): the documentation module's section-first retrieval approach, stable section IDs, and byte-offset addressing.
147
151
  - [jDataMunch-MCP](https://github.com/jgravelle/jdatamunch-mcp) by [J. Gravelle](https://www.linkedin.com/in/j-gravelle-2778223/): the data module's column profiling, streaming parsers, and server-side aggregation approach.
152
+ - [rtk](https://github.com/rtk-ai/rtk) by [rtk-ai](https://github.com/rtk-ai): the shell compression module's command-family approach and token-optimized output patterns.
148
153
  - [code-review-graph](https://github.com/tirth8205/code-review-graph) by [Tirth Kanani](https://github.com/tirth8205): community detection, execution flow tracing, refactoring tools, and multi-platform auto-detection patterns.
149
154
  - [lean-ctx](https://github.com/yvgude/lean-ctx) by [Yves Gugger](https://github.com/yvgude): file read caching, multiple read modes, and context budget governance concepts.
155
+ - [turboquant-js](https://github.com/danilodevhub/turboquant-js) by [Danilo Dev](https://github.com/danilodevhub): the TurboQuant engine — TypeScript implementation of Google's Walsh-Hadamard + Lloyd-Max quantization algorithm used for embedding compression.
156
+ - [turbovec](https://github.com/RyanCodrai/turbovec) by [Ryan Codrai](https://github.com/RyanCodrai): the TurboVec engine — Rust implementation of TurboQuant with SIMD acceleration, exposed to Node.js via a napi-rs native addon.
157
+ - [pdf-inspector](https://github.com/firecrawl/pdf-inspector) by [Firecrawl](https://github.com/firecrawl): the PDF parser used in the data module — pure Rust, no OCR, no network, prebuilt binaries for linux-x64 and macOS ARM64.
150
158
 
151
159
  ### Contributors
152
160
 
@@ -155,16 +163,22 @@ KiroGraph is inspired by [CodeGraph](https://github.com/colbymchenry/codegraph)
155
163
 
156
164
  ## How It Compares
157
165
 
158
- KiroGraph combines capabilities from 7 separate tools into one integrated MCP server:
166
+ KiroGraph combines capabilities from 13 separate projects into one integrated MCP server:
159
167
 
160
168
  | Capability | Inspired by | What KiroGraph adds |
161
169
  |-----------|-------------|---------------------|
162
- | Code graph | [CodeGraph](https://github.com/colbymchenry/codegraph) | Architecture metrics, community detection, execution flows |
163
- | Memory | [cavemem](https://github.com/JuliusBrussee/cavemem) | Symbol-linked observations, 7 semantic engines |
170
+ | Code graph | [CodeGraph](https://github.com/colbymchenry/codegraph) | Architecture metrics, execution flows, layered dependency view |
171
+ | Community detection | [code-review-graph](https://github.com/tirth8205/code-review-graph) | Coupling metrics (Ca/Ce/instability), refactoring tools, multi-platform auto-detect |
172
+ | Memory | [cavemem](https://github.com/JuliusBrussee/cavemem) + [Engram](https://github.com/Gentleman-Programming/engram) | Symbol-linked observations, 9 semantic engines, conflict detection, stale review |
173
+ | Watchmen synthesis | [watchmen](https://github.com/firstbatchxyz/watchmen) | Local-model synthesis on-device, auto threshold, steering + skill file generation |
174
+ | LLM Wiki | [Karpathy's LLM Wiki gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) | WIKI_DIFF format, FTS5 search, conflict resolution, local model mode |
164
175
  | Docs | [jDocMunch-MCP](https://github.com/jgravelle/jdocmunch-mcp) | Code ↔ docs cross-references |
165
- | Data | [jDataMunch-MCP](https://github.com/jgravelle/jdatamunch-mcp) | Unified with code graph in one server |
176
+ | Data | [jDataMunch-MCP](https://github.com/jgravelle/jdatamunch-mcp) | Unified with code graph in one server, PDF parsing |
177
+ | PDF parsing | [pdf-inspector](https://github.com/firecrawl/pdf-inspector) | Pure Rust, no OCR/network, prebuilt binaries, piped into data module |
166
178
  | Shell compression | [rtk](https://github.com/rtk-ai/rtk) | Integrated as MCP tool, no separate binary |
167
179
  | Prose compression | [caveman](https://github.com/JuliusBrussee/caveman) | Multi-level (lite/full/ultra) via steering |
180
+ | Embedding compression | [turboquant-js](https://github.com/danilodevhub/turboquant-js) | 9 engine options, SIMD Rust variant, 20–30× RAM savings |
181
+ | SIMD vector search | [turbovec](https://github.com/RyanCodrai/turbovec) | NEON on ARM64, AVX-512BW on x86, auto-built by installer |
168
182
  | Context layer | [lean-ctx](https://github.com/yvgude/lean-ctx) | File caching, read modes, budget governance |
169
183
 
170
184
  See the [full comparison](docs/guide/comparison.md) for a detailed feature matrix against CodeGraph, code-review-graph, jCodeMunch, and others.
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var configure_mcp_exports = {};
30
+ __export(configure_mcp_exports, {
31
+ register: () => register
32
+ });
33
+ module.exports = __toCommonJS(configure_mcp_exports);
34
+ var path = __toESM(require("path"));
35
+ var fs = __toESM(require("fs"));
36
+ var os = __toESM(require("os"));
37
+ var import_config = require("../../config");
38
+ var import_tool_names = require("../../mcp/tool-names");
39
+ var import_common = require("../installer/common");
40
+ const PROJECT_MCP_PATHS = [
41
+ ".kiro/settings/mcp.json",
42
+ ".mcp.json",
43
+ ".cursor/mcp.json",
44
+ ".warp/.mcp.json",
45
+ ".roo/mcp.json",
46
+ ".augment/mcp.json",
47
+ ".tabnine/mcp.json",
48
+ ".cline/mcp_settings.json",
49
+ ".trae/mcp.json",
50
+ ".junie/mcp/mcp.json",
51
+ ".qoder/mcp.json",
52
+ ".github/copilot-mcp.json",
53
+ ".continue/mcpServers/kirograph.json"
54
+ ];
55
+ const HOME_MCP_PATHS = [
56
+ path.join(os.homedir(), ".codeium", "windsurf", "mcp_config.json"),
57
+ path.join(os.homedir(), ".copilot", "mcp-config.json"),
58
+ path.join(os.homedir(), ".gemini", "antigravity", "mcp_config.json"),
59
+ path.join(os.homedir(), ".qwen", "settings.json")
60
+ ];
61
+ function register(program) {
62
+ program.command("configure-mcp [projectPath]").description("Sync MCP autoApprove list with the features enabled in .kirograph/config.json").action(async (projectPath) => {
63
+ const target = path.resolve(projectPath ?? process.cwd());
64
+ const configFile = path.join(target, ".kirograph", "config.json");
65
+ if (!fs.existsSync(configFile)) {
66
+ console.error(` \u2717 No .kirograph/config.json found in ${target}`);
67
+ console.error(` Run 'kirograph install' first.`);
68
+ process.exit(1);
69
+ }
70
+ const config = await (0, import_config.loadConfig)(target);
71
+ const cfg = config;
72
+ const toolNames = (0, import_tool_names.filterToolNamesByConfig)(import_tool_names.KIROGRAPH_TOOL_NAMES, cfg);
73
+ const featuresArg = (0, import_tool_names.serializeFeaturesArg)(cfg);
74
+ const enabledFeatures = Object.keys(import_tool_names.FEATURE_TOOLS).filter((f) => cfg[f]);
75
+ console.log(`
76
+ Enabled features: ${enabledFeatures.length ? enabledFeatures.join(", ") : "none (core only)"}`);
77
+ console.log(` Tools to approve: ${toolNames.length} / ${import_tool_names.KIROGRAPH_TOOL_NAMES.length}
78
+ `);
79
+ const candidates = [
80
+ ...PROJECT_MCP_PATHS.map((p) => path.join(target, p)),
81
+ ...HOME_MCP_PATHS
82
+ ];
83
+ let updatedCount = 0;
84
+ for (const mcpPath of candidates) {
85
+ const autoApproveUpdated = (0, import_common.updateMcpAutoApprove)(mcpPath, toolNames);
86
+ const argsUpdated = (0, import_common.updateMcpFeaturesArg)(mcpPath, featuresArg || null);
87
+ if (autoApproveUpdated || argsUpdated) {
88
+ console.log(` \u2713 ${mcpPath}`);
89
+ updatedCount++;
90
+ }
91
+ }
92
+ if (updatedCount === 0) {
93
+ console.error(` \u2717 No registered MCP configs found. Run 'kirograph install' first.`);
94
+ process.exit(1);
95
+ }
96
+ console.log(`
97
+ Updated ${updatedCount} config(s). Restart your IDE for changes to take effect.`);
98
+ });
99
+ }
100
+ // Annotate the CommonJS export names for ESM import in node:
101
+ 0 && (module.exports = {
102
+ register
103
+ });
104
+ //# sourceMappingURL=configure-mcp.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/bin/commands/configure-mcp.ts"],
4
+ "sourcesContent": ["import { Command } from 'commander';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport { loadConfig } from '../../config';\nimport { filterToolNamesByConfig, serializeFeaturesArg, KIROGRAPH_TOOL_NAMES, FEATURE_TOOLS } from '../../mcp/tool-names';\nimport { updateMcpAutoApprove, updateMcpFeaturesArg } from '../installer/common';\n\nconst PROJECT_MCP_PATHS = [\n '.kiro/settings/mcp.json',\n '.mcp.json',\n '.cursor/mcp.json',\n '.warp/.mcp.json',\n '.roo/mcp.json',\n '.augment/mcp.json',\n '.tabnine/mcp.json',\n '.cline/mcp_settings.json',\n '.trae/mcp.json',\n '.junie/mcp/mcp.json',\n '.qoder/mcp.json',\n '.github/copilot-mcp.json',\n '.continue/mcpServers/kirograph.json',\n];\n\nconst HOME_MCP_PATHS = [\n path.join(os.homedir(), '.codeium', 'windsurf', 'mcp_config.json'),\n path.join(os.homedir(), '.copilot', 'mcp-config.json'),\n path.join(os.homedir(), '.gemini', 'antigravity', 'mcp_config.json'),\n path.join(os.homedir(), '.qwen', 'settings.json'),\n];\n\nexport function register(program: Command): void {\n program\n .command('configure-mcp [projectPath]')\n .description('Sync MCP autoApprove list with the features enabled in .kirograph/config.json')\n .action(async (projectPath?: string) => {\n const target = path.resolve(projectPath ?? process.cwd());\n\n const configFile = path.join(target, '.kirograph', 'config.json');\n if (!fs.existsSync(configFile)) {\n console.error(` \u2717 No .kirograph/config.json found in ${target}`);\n console.error(` Run 'kirograph install' first.`);\n process.exit(1);\n }\n\n const config = await loadConfig(target);\n const cfg = config as unknown as Record<string, boolean>;\n const toolNames = filterToolNamesByConfig(KIROGRAPH_TOOL_NAMES, cfg);\n const featuresArg = serializeFeaturesArg(cfg);\n\n const enabledFeatures = Object.keys(FEATURE_TOOLS).filter(f => cfg[f]);\n console.log(`\\n Enabled features: ${enabledFeatures.length ? enabledFeatures.join(', ') : 'none (core only)'}`);\n console.log(` Tools to approve: ${toolNames.length} / ${KIROGRAPH_TOOL_NAMES.length}\\n`);\n\n const candidates = [\n ...PROJECT_MCP_PATHS.map(p => path.join(target, p)),\n ...HOME_MCP_PATHS,\n ];\n\n let updatedCount = 0;\n for (const mcpPath of candidates) {\n const autoApproveUpdated = updateMcpAutoApprove(mcpPath, toolNames);\n const argsUpdated = updateMcpFeaturesArg(mcpPath, featuresArg || null);\n if (autoApproveUpdated || argsUpdated) {\n console.log(` \u2713 ${mcpPath}`);\n updatedCount++;\n }\n }\n\n if (updatedCount === 0) {\n console.error(` \u2717 No registered MCP configs found. Run 'kirograph install' first.`);\n process.exit(1);\n }\n\n console.log(`\\n Updated ${updatedCount} config(s). Restart your IDE for changes to take effect.`);\n });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,WAAsB;AACtB,SAAoB;AACpB,SAAoB;AACpB,oBAA2B;AAC3B,wBAAmG;AACnG,oBAA2D;AAE3D,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,iBAAiB;AAAA,EACrB,KAAK,KAAK,GAAG,QAAQ,GAAG,YAAY,YAAY,iBAAiB;AAAA,EACjE,KAAK,KAAK,GAAG,QAAQ,GAAG,YAAY,iBAAiB;AAAA,EACrD,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,eAAe,iBAAiB;AAAA,EACnE,KAAK,KAAK,GAAG,QAAQ,GAAG,SAAS,eAAe;AAClD;AAEO,SAAS,SAAS,SAAwB;AAC/C,UACG,QAAQ,6BAA6B,EACrC,YAAY,+EAA+E,EAC3F,OAAO,OAAO,gBAAyB;AACtC,UAAM,SAAS,KAAK,QAAQ,eAAe,QAAQ,IAAI,CAAC;AAExD,UAAM,aAAa,KAAK,KAAK,QAAQ,cAAc,aAAa;AAChE,QAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,cAAQ,MAAM,+CAA0C,MAAM,EAAE;AAChE,cAAQ,MAAM,oCAAoC;AAClD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,UAAM,0BAAW,MAAM;AACtC,UAAM,MAAM;AACZ,UAAM,gBAAY,2CAAwB,wCAAsB,GAAG;AACnE,UAAM,kBAAc,wCAAqB,GAAG;AAE5C,UAAM,kBAAkB,OAAO,KAAK,+BAAa,EAAE,OAAO,OAAK,IAAI,CAAC,CAAC;AACrE,YAAQ,IAAI;AAAA,sBAAyB,gBAAgB,SAAS,gBAAgB,KAAK,IAAI,IAAI,kBAAkB,EAAE;AAC/G,YAAQ,IAAI,uBAAuB,UAAU,MAAM,MAAM,uCAAqB,MAAM;AAAA,CAAI;AAExF,UAAM,aAAa;AAAA,MACjB,GAAG,kBAAkB,IAAI,OAAK,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,MAClD,GAAG;AAAA,IACL;AAEA,QAAI,eAAe;AACnB,eAAW,WAAW,YAAY;AAChC,YAAM,yBAAqB,oCAAqB,SAAS,SAAS;AAClE,YAAM,kBAAc,oCAAqB,SAAS,eAAe,IAAI;AACrE,UAAI,sBAAsB,aAAa;AACrC,gBAAQ,IAAI,YAAO,OAAO,EAAE;AAC5B;AAAA,MACF;AAAA,IACF;AAEA,QAAI,iBAAiB,GAAG;AACtB,cAAQ,MAAM,0EAAqE;AACnF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,IAAI;AAAA,YAAe,YAAY,0DAA0D;AAAA,EACnG,CAAC;AACL;",
6
+ "names": []
7
+ }
@@ -80,6 +80,13 @@ function register(program) {
80
80
  for (const r of results) {
81
81
  const age = formatAge(r.observation.createdAt);
82
82
  console.log(` ${import_ui.violet}[${r.observation.kind}]${import_ui.reset} ${r.observation.content}`);
83
+ if (r.relations && r.relations.length > 0) {
84
+ const annotations = r.relations.filter((rel) => rel.judgmentStatus !== "ignored").map((rel) => {
85
+ const icon = rel.relation === "conflicts_with" ? "\u26A1" : rel.relation === "supersedes" ? "\u21A9" : "~";
86
+ return `${icon} ${rel.relation}`;
87
+ }).join(" ");
88
+ if (annotations) console.log(` ${import_ui.dim}${annotations}${import_ui.reset}`);
89
+ }
83
90
  console.log(` ${import_ui.dim}${age} \xB7 score: ${r.score.toFixed(3)}${import_ui.reset}
84
91
  `);
85
92
  }
@@ -89,7 +96,7 @@ function register(program) {
89
96
  trackCliToolSaving(cwd, "kirograph_mem_search", output, { limit: parseInt(opts.limit) });
90
97
  cg.close();
91
98
  });
92
- mem.command("store [content]").description("Store an observation in memory").option("--kind <kind>", "Observation kind", "note").action(async (content, opts) => {
99
+ mem.command("store [content]").description("Store an observation in memory").option("--kind <kind>", "Observation kind", "note").option("--topic-key <key>", "Stable semantic key (e.g. architecture/auth-model)").option("--review-after <ms>", "Epoch ms: schedule re-evaluation after this timestamp").action(async (content, opts) => {
93
100
  const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
94
101
  const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
95
102
  const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
@@ -120,7 +127,13 @@ function register(program) {
120
127
  db.applyMemorySchema();
121
128
  const mem2 = new MemoryManager(config, db.getRawDb());
122
129
  mem2.initialize();
123
- const result = await mem2.store({ content: text, kind: opts.kind, source: "manual" });
130
+ const result = await mem2.store({
131
+ content: text,
132
+ kind: opts.kind,
133
+ source: "manual",
134
+ topicKey: opts.topicKey,
135
+ reviewAfter: opts.reviewAfter ? parseInt(opts.reviewAfter) : void 0
136
+ });
124
137
  if (!result) {
125
138
  console.log(` ${import_ui.dim}Duplicate observation \u2014 already stored.${import_ui.reset}`);
126
139
  } else if (typeof result === "object" && result.watchmenReady) {
@@ -233,6 +246,7 @@ function register(program) {
233
246
  console.log(` Observations: ${import_ui.bold}${stats.observations}${import_ui.reset}`);
234
247
  console.log(` Symbol links: ${import_ui.bold}${stats.links}${import_ui.reset}`);
235
248
  console.log(` Embeddings: ${import_ui.bold}${stats.vectors}${import_ui.reset} / ${stats.embeddableCount}`);
249
+ console.log(` Relations: ${import_ui.bold}${stats.relations}${import_ui.reset} (${stats.pendingConflicts} pending)`);
236
250
  console.log(` Model mismatch: ${stats.modelMismatch ? import_ui.violet + "\u26A0 yes \u2014 run kirograph mem reembed" + import_ui.reset : import_ui.dim + "no" + import_ui.reset}`);
237
251
  console.log(` Compression: ${config.cavemanMode !== "off" ? import_ui.violet + config.cavemanMode + import_ui.reset : import_ui.dim + "off (storing raw)" + import_ui.reset}`);
238
252
  console.log("");
@@ -422,6 +436,324 @@ function register(program) {
422
436
  console.log("");
423
437
  cg.close();
424
438
  });
439
+ mem.command("review").description("List observations past their review_after date").option("--limit <n>", "Max results", "20").option("--format <fmt>", "Output format: text, json", "text").action(async (opts) => {
440
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
441
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
442
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
443
+ const cwd = process.cwd();
444
+ if (!KiroGraph.isInitialized(cwd)) {
445
+ console.error(" \u2716 Not initialized.");
446
+ process.exit(1);
447
+ }
448
+ const config = await loadConfig(cwd);
449
+ if (!config.enableMemory) {
450
+ console.error(" \u2716 Memory not enabled.");
451
+ process.exit(1);
452
+ }
453
+ const cg = await KiroGraph.open(cwd);
454
+ const db = cg.getDatabase();
455
+ db.applyMemorySchema();
456
+ const mem2 = new MemoryManager(config, db.getRawDb());
457
+ mem2.initialize();
458
+ const obs = mem2.getObservationsForReview(parseInt(opts.limit));
459
+ if (opts.format === "json") {
460
+ console.log(JSON.stringify(obs));
461
+ } else if (obs.length === 0) {
462
+ console.log(`
463
+ ${import_ui.dim}No overdue observations.${import_ui.reset}
464
+ `);
465
+ } else {
466
+ console.log(`
467
+ ${(0, import_ui.section)("Memory Review Queue")} \u2014 ${obs.length} overdue
468
+ `);
469
+ const now = Date.now();
470
+ for (const o of obs) {
471
+ const daysOverdue = Math.floor((now - (o.reviewAfter ?? now)) / 864e5);
472
+ console.log(` ${import_ui.violet}[${o.kind}]${import_ui.reset}${o.topicKey ? ` ${import_ui.dim}(${o.topicKey})${import_ui.reset}` : ""} ${o.content.slice(0, 120)}`);
473
+ console.log(` ${import_ui.dim}Overdue by ${daysOverdue}d \xB7 ID: ${o.id}${import_ui.reset}
474
+ `);
475
+ }
476
+ }
477
+ cg.close();
478
+ });
479
+ mem.command("mark-reviewed <id>").description("Mark an observation as reviewed \u2014 clears its review_after date").action(async (id) => {
480
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
481
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
482
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
483
+ const cwd = process.cwd();
484
+ if (!KiroGraph.isInitialized(cwd)) {
485
+ console.error(" \u2716 Not initialized.");
486
+ process.exit(1);
487
+ }
488
+ const config = await loadConfig(cwd);
489
+ if (!config.enableMemory) {
490
+ console.error(" \u2716 Memory not enabled.");
491
+ process.exit(1);
492
+ }
493
+ const cg = await KiroGraph.open(cwd);
494
+ const db = cg.getDatabase();
495
+ db.applyMemorySchema();
496
+ const mem2 = new MemoryManager(config, db.getRawDb());
497
+ mem2.initialize();
498
+ mem2.markReviewed(id);
499
+ console.log(` \u2713 Observation ${import_ui.dim}${id}${import_ui.reset} marked as reviewed`);
500
+ cg.close();
501
+ });
502
+ mem.command("suggest-topic-key").description("Suggest a stable topic_key for an observation").requiredOption("--kind <kind>", "Observation kind: decision, error, pattern, architecture, summary, note").requiredOption("--title <title>", "Short title or first sentence").action(async (opts) => {
503
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
504
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
505
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
506
+ const cwd = process.cwd();
507
+ if (!KiroGraph.isInitialized(cwd)) {
508
+ console.error(" \u2716 Not initialized.");
509
+ process.exit(1);
510
+ }
511
+ const config = await loadConfig(cwd);
512
+ if (!config.enableMemory) {
513
+ console.error(" \u2716 Memory not enabled.");
514
+ process.exit(1);
515
+ }
516
+ const cg = await KiroGraph.open(cwd);
517
+ const db = cg.getDatabase();
518
+ db.applyMemorySchema();
519
+ const mem2 = new MemoryManager(config, db.getRawDb());
520
+ mem2.initialize();
521
+ const key = mem2.suggestTopicKey(opts.kind, opts.title);
522
+ console.log(` ${import_ui.violet}${key}${import_ui.reset}`);
523
+ cg.close();
524
+ });
525
+ mem.command("capture [content]").description("Extract and store structured learnings from text (## Key Learnings, ## Decisions, etc.)").action(async (content) => {
526
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
527
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
528
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
529
+ const cwd = process.cwd();
530
+ if (!KiroGraph.isInitialized(cwd)) {
531
+ console.error(" \u2716 Not initialized.");
532
+ process.exit(1);
533
+ }
534
+ const config = await loadConfig(cwd);
535
+ if (!config.enableMemory) {
536
+ console.error(" \u2716 Memory not enabled.");
537
+ process.exit(1);
538
+ }
539
+ let text = content;
540
+ if (!text) {
541
+ const chunks = [];
542
+ for await (const chunk of process.stdin) chunks.push(chunk);
543
+ text = Buffer.concat(chunks).toString("utf8").trim();
544
+ }
545
+ if (!text) {
546
+ console.error(" \u2716 No content provided.");
547
+ process.exit(1);
548
+ }
549
+ const cg = await KiroGraph.open(cwd);
550
+ const db = cg.getDatabase();
551
+ db.applyMemorySchema();
552
+ const mem2 = new MemoryManager(config, db.getRawDb());
553
+ mem2.initialize();
554
+ const results = mem2.capturePassive(text);
555
+ if (results.length === 0) {
556
+ console.log(` ${import_ui.dim}No structured sections found. Use ## Key Learnings, ## Observations, ## Decisions headings.${import_ui.reset}`);
557
+ } else {
558
+ console.log(`
559
+ ${(0, import_ui.section)("Passive Capture")} \u2014 ${results.length} observation(s)
560
+ `);
561
+ for (const r of results) {
562
+ console.log(` ${import_ui.violet}[${r.kind}]${import_ui.reset} ${r.content.slice(0, 100)}${r.id ? "" : ` ${import_ui.dim}(duplicate)${import_ui.reset}`}`);
563
+ }
564
+ console.log("");
565
+ }
566
+ cg.close();
567
+ });
568
+ mem.command("save-prompt [content]").description("Save the current user prompt to session memory").action(async (content) => {
569
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
570
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
571
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
572
+ const cwd = process.cwd();
573
+ if (!KiroGraph.isInitialized(cwd)) {
574
+ console.error(" \u2716 Not initialized.");
575
+ process.exit(1);
576
+ }
577
+ const config = await loadConfig(cwd);
578
+ if (!config.enableMemory) {
579
+ console.error(" \u2716 Memory not enabled.");
580
+ process.exit(1);
581
+ }
582
+ let text = content;
583
+ if (!text) {
584
+ const chunks = [];
585
+ for await (const chunk of process.stdin) chunks.push(chunk);
586
+ text = Buffer.concat(chunks).toString("utf8").trim();
587
+ }
588
+ if (!text) {
589
+ console.error(" \u2716 No content provided.");
590
+ process.exit(1);
591
+ }
592
+ const cg = await KiroGraph.open(cwd);
593
+ const db = cg.getDatabase();
594
+ db.applyMemorySchema();
595
+ const mem2 = new MemoryManager(config, db.getRawDb());
596
+ mem2.initialize();
597
+ const id = await mem2.savePrompt(text);
598
+ console.log(` \u2713 Prompt saved ${import_ui.dim}${id}${import_ui.reset}`);
599
+ cg.close();
600
+ });
601
+ const conflicts = mem.command("conflicts").description("Manage conflict relations between memory observations");
602
+ conflicts.command("list").description("List pending conflict relations").option("--limit <n>", "Max results", "20").option("--format <fmt>", "Output format: text, json", "text").action(async (opts) => {
603
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
604
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
605
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
606
+ const cwd = process.cwd();
607
+ if (!KiroGraph.isInitialized(cwd)) {
608
+ console.error(" \u2716 Not initialized.");
609
+ process.exit(1);
610
+ }
611
+ const config = await loadConfig(cwd);
612
+ if (!config.enableMemory) {
613
+ console.error(" \u2716 Memory not enabled.");
614
+ process.exit(1);
615
+ }
616
+ const cg = await KiroGraph.open(cwd);
617
+ const db = cg.getDatabase();
618
+ db.applyMemorySchema();
619
+ const mem2 = new MemoryManager(config, db.getRawDb());
620
+ mem2.initialize();
621
+ const pending = mem2.getPendingRelations(parseInt(opts.limit));
622
+ if (opts.format === "json") {
623
+ console.log(JSON.stringify(pending, null, 2));
624
+ } else if (pending.length === 0) {
625
+ console.log(`
626
+ ${import_ui.dim}No pending conflict relations.${import_ui.reset}
627
+ `);
628
+ } else {
629
+ console.log(`
630
+ ${(0, import_ui.section)("Pending Relations")} \u2014 ${pending.length}
631
+ `);
632
+ for (const r of pending) {
633
+ console.log(` ${import_ui.violet}${r.relation}${import_ui.reset} [${import_ui.dim}${r.id}${import_ui.reset}]`);
634
+ console.log(` A: ${r.observationA}`);
635
+ console.log(` B: ${r.observationB}`);
636
+ if (r.reason) console.log(` Reason: ${import_ui.dim}${r.reason}${import_ui.reset}`);
637
+ console.log("");
638
+ }
639
+ }
640
+ cg.close();
641
+ });
642
+ conflicts.command("scan").description("Scan recent observations for potential conflicts using FTS similarity").option("--limit <n>", "Max observations to scan", "50").option("--format <fmt>", "Output format: text, json", "text").action(async (opts) => {
643
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
644
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
645
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
646
+ const cwd = process.cwd();
647
+ if (!KiroGraph.isInitialized(cwd)) {
648
+ console.error(" \u2716 Not initialized.");
649
+ process.exit(1);
650
+ }
651
+ const config = await loadConfig(cwd);
652
+ if (!config.enableMemory) {
653
+ console.error(" \u2716 Memory not enabled.");
654
+ process.exit(1);
655
+ }
656
+ const cg = await KiroGraph.open(cwd);
657
+ const db = cg.getDatabase();
658
+ db.applyMemorySchema();
659
+ const mem2 = new MemoryManager(config, db.getRawDb());
660
+ mem2.initialize();
661
+ const candidates = mem2.scanConflicts(parseInt(opts.limit));
662
+ if (opts.format === "json") {
663
+ console.log(JSON.stringify(candidates.map((c) => ({ a: c.observationA.id, b: c.observationB.id, similarity: c.similarity })), null, 2));
664
+ } else if (candidates.length === 0) {
665
+ console.log(`
666
+ ${import_ui.dim}No potential conflicts found.${import_ui.reset}
667
+ `);
668
+ } else {
669
+ console.log(`
670
+ ${(0, import_ui.section)("Conflict Scan")} \u2014 ${candidates.length} candidate(s)
671
+ `);
672
+ for (const c of candidates) {
673
+ console.log(` ${import_ui.violet}\u2194${import_ui.reset} [${c.observationA.kind}] "${c.observationA.content.slice(0, 80)}"`);
674
+ console.log(` [${c.observationB.kind}] "${c.observationB.content.slice(0, 80)}"`);
675
+ console.log(` ${import_ui.dim}Similarity: ${c.similarity.toFixed(2)} \xB7 Use 'kirograph mem compare' to establish a relation${import_ui.reset}
676
+ `);
677
+ }
678
+ }
679
+ cg.close();
680
+ });
681
+ conflicts.command("compare <observationA> <observationB>").description("Establish a typed relation between two observations (IDs or topic_keys)").requiredOption("--relation <type>", "Relation: supersedes, conflicts_with, compatible, scoped, related, not_conflict").option("--confidence <n>", "Confidence 0.0\u20131.0", "1.0").option("--reason <text>", "Explanation").option("--evidence <text>", "Supporting evidence").action(async (observationA, observationB, opts) => {
682
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
683
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
684
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
685
+ const cwd = process.cwd();
686
+ if (!KiroGraph.isInitialized(cwd)) {
687
+ console.error(" \u2716 Not initialized.");
688
+ process.exit(1);
689
+ }
690
+ const config = await loadConfig(cwd);
691
+ if (!config.enableMemory) {
692
+ console.error(" \u2716 Memory not enabled.");
693
+ process.exit(1);
694
+ }
695
+ const cg = await KiroGraph.open(cwd);
696
+ const db = cg.getDatabase();
697
+ db.applyMemorySchema();
698
+ const mem2 = new MemoryManager(config, db.getRawDb());
699
+ mem2.initialize();
700
+ const id = mem2.compareObservations({
701
+ observationA,
702
+ observationB,
703
+ relation: opts.relation,
704
+ confidence: parseFloat(opts.confidence),
705
+ reason: opts.reason,
706
+ evidence: opts.evidence
707
+ });
708
+ console.log(` \u2713 Relation ${import_ui.dim}${id}${import_ui.reset} created: ${import_ui.violet}${opts.relation}${import_ui.reset} (confidence: ${opts.confidence})`);
709
+ cg.close();
710
+ });
711
+ conflicts.command("judge <relationId>").description("Finalize a pending relation").requiredOption("--relation <type>", "Final relation type").requiredOption("--confidence <n>", "Final confidence 0.0\u20131.0").option("--reason <text>", "Reasoning").option("--evidence <text>", "Evidence").action(async (relationId, opts) => {
712
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
713
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
714
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
715
+ const cwd = process.cwd();
716
+ if (!KiroGraph.isInitialized(cwd)) {
717
+ console.error(" \u2716 Not initialized.");
718
+ process.exit(1);
719
+ }
720
+ const config = await loadConfig(cwd);
721
+ if (!config.enableMemory) {
722
+ console.error(" \u2716 Memory not enabled.");
723
+ process.exit(1);
724
+ }
725
+ const cg = await KiroGraph.open(cwd);
726
+ const db = cg.getDatabase();
727
+ db.applyMemorySchema();
728
+ const mem2 = new MemoryManager(config, db.getRawDb());
729
+ mem2.initialize();
730
+ mem2.judgeRelation(relationId, opts.relation, parseFloat(opts.confidence), opts.reason, opts.evidence);
731
+ console.log(` \u2713 Relation ${import_ui.dim}${relationId}${import_ui.reset} judged as ${import_ui.violet}${opts.relation}${import_ui.reset} (confidence: ${opts.confidence})`);
732
+ cg.close();
733
+ });
734
+ conflicts.command("ignore <relationId>").description("Ignore a pending relation (mark as not relevant)").action(async (relationId) => {
735
+ const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));
736
+ const { loadConfig } = await Promise.resolve().then(() => require("../../config.js"));
737
+ const KiroGraph = (await Promise.resolve().then(() => require("../../index.js"))).default;
738
+ const cwd = process.cwd();
739
+ if (!KiroGraph.isInitialized(cwd)) {
740
+ console.error(" \u2716 Not initialized.");
741
+ process.exit(1);
742
+ }
743
+ const config = await loadConfig(cwd);
744
+ if (!config.enableMemory) {
745
+ console.error(" \u2716 Memory not enabled.");
746
+ process.exit(1);
747
+ }
748
+ const cg = await KiroGraph.open(cwd);
749
+ const db = cg.getDatabase();
750
+ db.applyMemorySchema();
751
+ const mem2 = new MemoryManager(config, db.getRawDb());
752
+ mem2.initialize();
753
+ mem2.ignoreRelation(relationId);
754
+ console.log(` \u2713 Relation ${import_ui.dim}${relationId}${import_ui.reset} ignored`);
755
+ cg.close();
756
+ });
425
757
  const watchmen = mem.command("watchmen").description("Watchmen \u2014 workspace brief synthesis from memory observations (requires enableWatchmen: true)");
426
758
  watchmen.command("status").description("Show pending observation count and which files would be written on next synthesis").action(async () => {
427
759
  const { MemoryManager } = await Promise.resolve().then(() => require("../../memory/index.js"));