kirograph 0.27.1 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +27 -1
  2. package/dist/bin/commands/callees.js +76 -0
  3. package/dist/bin/commands/callees.js.map +7 -0
  4. package/dist/bin/commands/callers.js +76 -0
  5. package/dist/bin/commands/callers.js.map +7 -0
  6. package/dist/bin/commands/circular-deps.js +68 -0
  7. package/dist/bin/commands/circular-deps.js.map +7 -0
  8. package/dist/bin/commands/impact.js +76 -0
  9. package/dist/bin/commands/impact.js.map +7 -0
  10. package/dist/bin/commands/type-hierarchy.js +76 -0
  11. package/dist/bin/commands/type-hierarchy.js.map +7 -0
  12. package/dist/bin/installer/common.js +15 -9
  13. package/dist/bin/installer/common.js.map +2 -2
  14. package/dist/bin/installer/config-prompt.js +8 -2
  15. package/dist/bin/installer/config-prompt.js.map +2 -2
  16. package/dist/bin/installer/index.js +25 -1
  17. package/dist/bin/installer/index.js.map +2 -2
  18. package/dist/bin/installer/instructions.js +14 -21
  19. package/dist/bin/installer/instructions.js.map +2 -2
  20. package/dist/bin/installer/mcp.js.map +2 -2
  21. package/dist/bin/installer/steering.js +38 -0
  22. package/dist/bin/installer/steering.js.map +2 -2
  23. package/dist/bin/installer/targets/aider.js +3 -3
  24. package/dist/bin/installer/targets/aider.js.map +2 -2
  25. package/dist/bin/installer/targets/amp.js +3 -3
  26. package/dist/bin/installer/targets/amp.js.map +2 -2
  27. package/dist/bin/installer/targets/antigravity.js +4 -4
  28. package/dist/bin/installer/targets/antigravity.js.map +2 -2
  29. package/dist/bin/installer/targets/augment.js +3 -3
  30. package/dist/bin/installer/targets/augment.js.map +2 -2
  31. package/dist/bin/installer/targets/claude.js +3 -3
  32. package/dist/bin/installer/targets/claude.js.map +2 -2
  33. package/dist/bin/installer/targets/cline.js +4 -4
  34. package/dist/bin/installer/targets/cline.js.map +2 -2
  35. package/dist/bin/installer/targets/codex.js +4 -4
  36. package/dist/bin/installer/targets/codex.js.map +2 -2
  37. package/dist/bin/installer/targets/continue.js +4 -4
  38. package/dist/bin/installer/targets/continue.js.map +2 -2
  39. package/dist/bin/installer/targets/copilot-cli.js +4 -4
  40. package/dist/bin/installer/targets/copilot-cli.js.map +2 -2
  41. package/dist/bin/installer/targets/copilot.js +4 -4
  42. package/dist/bin/installer/targets/copilot.js.map +2 -2
  43. package/dist/bin/installer/targets/cursor.js +5 -5
  44. package/dist/bin/installer/targets/cursor.js.map +2 -2
  45. package/dist/bin/installer/targets/devin.js +4 -4
  46. package/dist/bin/installer/targets/devin.js.map +2 -2
  47. package/dist/bin/installer/targets/gemini-cli.js +4 -4
  48. package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
  49. package/dist/bin/installer/targets/generic.js +2 -2
  50. package/dist/bin/installer/targets/generic.js.map +2 -2
  51. package/dist/bin/installer/targets/goose.js +3 -4
  52. package/dist/bin/installer/targets/goose.js.map +3 -3
  53. package/dist/bin/installer/targets/index.js.map +1 -1
  54. package/dist/bin/installer/targets/junie.js +4 -4
  55. package/dist/bin/installer/targets/junie.js.map +2 -2
  56. package/dist/bin/installer/targets/kilo.js +4 -4
  57. package/dist/bin/installer/targets/kilo.js.map +2 -2
  58. package/dist/bin/installer/targets/kiro.js +23 -3
  59. package/dist/bin/installer/targets/kiro.js.map +2 -2
  60. package/dist/bin/installer/targets/opencode.js +3 -3
  61. package/dist/bin/installer/targets/opencode.js.map +2 -2
  62. package/dist/bin/installer/targets/openhands.js +3 -3
  63. package/dist/bin/installer/targets/openhands.js.map +2 -2
  64. package/dist/bin/installer/targets/qoder.js +2 -2
  65. package/dist/bin/installer/targets/qoder.js.map +2 -2
  66. package/dist/bin/installer/targets/qwen.js +2 -2
  67. package/dist/bin/installer/targets/qwen.js.map +2 -2
  68. package/dist/bin/installer/targets/replit.js +3 -3
  69. package/dist/bin/installer/targets/replit.js.map +2 -2
  70. package/dist/bin/installer/targets/roo.js +4 -4
  71. package/dist/bin/installer/targets/roo.js.map +2 -2
  72. package/dist/bin/installer/targets/tabnine.js +3 -3
  73. package/dist/bin/installer/targets/tabnine.js.map +2 -2
  74. package/dist/bin/installer/targets/trae.js +3 -3
  75. package/dist/bin/installer/targets/trae.js.map +2 -2
  76. package/dist/bin/installer/targets/warp.js +4 -4
  77. package/dist/bin/installer/targets/warp.js.map +2 -2
  78. package/dist/bin/installer/targets/windsurf.js +4 -4
  79. package/dist/bin/installer/targets/windsurf.js.map +2 -2
  80. package/dist/bin/kirograph.js +11 -1
  81. package/dist/bin/kirograph.js.map +3 -3
  82. package/dist/config.js +4 -0
  83. package/dist/config.js.map +2 -2
  84. package/dist/mcp/cache.js +1 -1
  85. package/dist/mcp/cache.js.map +2 -2
  86. package/dist/mcp/tool-names.js +43 -4
  87. package/dist/mcp/tool-names.js.map +2 -2
  88. package/dist/mcp/tools.js +774 -182
  89. package/dist/mcp/tools.js.map +3 -3
  90. package/package.json +1 -1
package/dist/mcp/tools.js CHANGED
@@ -101,21 +101,27 @@ const tools = [
101
101
  enum: ["function", "method", "class", "interface", "type_alias", "variable", "route", "component"]
102
102
  },
103
103
  limit: { type: "number", description: "Max results 1-100 (default: 10)", default: 10 },
104
- projectPath: { type: "string", description: "Project root path (optional, defaults to current project)" }
104
+ projectPath: { type: "string" }
105
105
  },
106
106
  required: ["query"]
107
107
  }
108
108
  },
109
109
  {
110
110
  name: "kirograph_context",
111
- description: "PRIMARY TOOL: Build comprehensive context for a task or feature request. Returns entry points, related symbols, and key code \u2014 often enough to understand the codebase without additional tool calls.",
111
+ description: "Build comprehensive context for a task or feature request.",
112
112
  inputSchema: {
113
113
  type: "object",
114
114
  properties: {
115
- task: { type: "string", description: "Description of the task, bug, or feature to build context for" },
116
- maxNodes: { type: "number", description: "Max symbols to include (default: 20)", default: 20 },
117
- includeCode: { type: "boolean", description: "Include code snippets (default: true)", default: true },
118
- projectPath: { type: "string", description: "Project root path (optional, defaults to current project)" }
115
+ task: { type: "string", description: "Task description or feature name" },
116
+ maxNodes: { type: "number", description: "Max symbols to include", default: 20 },
117
+ includeCode: { type: "boolean", description: "Include code snippets. Deprecated \u2014 prefer detail.", default: true },
118
+ detail: {
119
+ type: "string",
120
+ description: "Code verbosity: full, signatures, or summary",
121
+ enum: ["full", "signatures", "summary"],
122
+ default: "full"
123
+ },
124
+ projectPath: { type: "string" }
119
125
  },
120
126
  required: ["task"]
121
127
  }
@@ -128,7 +134,7 @@ const tools = [
128
134
  properties: {
129
135
  symbol: { type: "string", description: "Symbol name to find callers for" },
130
136
  limit: { type: "number", description: "Max results 1-100 (default: 20)", default: 20 },
131
- projectPath: { type: "string", description: "Project root path (optional)" }
137
+ projectPath: { type: "string" }
132
138
  },
133
139
  required: ["symbol"]
134
140
  }
@@ -141,33 +147,39 @@ const tools = [
141
147
  properties: {
142
148
  symbol: { type: "string", description: "Symbol name to find callees for" },
143
149
  limit: { type: "number", description: "Max results 1-100 (default: 20)", default: 20 },
144
- projectPath: { type: "string", description: "Project root path (optional)" }
150
+ projectPath: { type: "string" }
145
151
  },
146
152
  required: ["symbol"]
147
153
  }
148
154
  },
149
155
  {
150
156
  name: "kirograph_impact",
151
- description: "Analyze what code would be affected by changing a symbol. Use before making changes.",
157
+ description: "Analyze what code would be affected by changing a symbol.",
152
158
  inputSchema: {
153
159
  type: "object",
154
160
  properties: {
155
161
  symbol: { type: "string", description: "Symbol name to analyze impact for" },
156
162
  depth: { type: "number", description: "Traversal depth (default: 2)", default: 2 },
157
- projectPath: { type: "string", description: "Project root path (optional)" }
163
+ projectPath: { type: "string" }
158
164
  },
159
165
  required: ["symbol"]
160
166
  }
161
167
  },
162
168
  {
163
169
  name: "kirograph_node",
164
- description: "Get details about a specific symbol, optionally including its source code.",
170
+ description: "Get details about a specific symbol.",
165
171
  inputSchema: {
166
172
  type: "object",
167
173
  properties: {
168
174
  symbol: { type: "string", description: "Symbol name to look up" },
169
- includeCode: { type: "boolean", description: "Include source code (default: false)", default: false },
170
- projectPath: { type: "string", description: "Project root path (optional)" }
175
+ includeCode: { type: "boolean", description: "Include full source code. Deprecated \u2014 prefer detail.", default: false },
176
+ detail: {
177
+ type: "string",
178
+ description: "Output level: summary, signatures, or full",
179
+ enum: ["summary", "signatures", "full"],
180
+ default: "summary"
181
+ },
182
+ projectPath: { type: "string" }
171
183
  },
172
184
  required: ["symbol"]
173
185
  }
@@ -178,27 +190,27 @@ const tools = [
178
190
  inputSchema: {
179
191
  type: "object",
180
192
  properties: {
181
- projectPath: { type: "string", description: "Project root path (optional)" }
193
+ projectPath: { type: "string" }
182
194
  }
183
195
  }
184
196
  },
185
197
  {
186
198
  name: "kirograph_files",
187
- description: "List the indexed file structure of the project. Supports filtering by path prefix, glob pattern, or depth.",
199
+ description: "List indexed file structure with optional filtering.",
188
200
  inputSchema: {
189
201
  type: "object",
190
202
  properties: {
191
203
  filterPath: { type: "string", description: 'Filter by directory path prefix (e.g., "src/")' },
192
204
  pattern: { type: "string", description: 'Filter by glob pattern (e.g., "**/*.ts")' },
193
- maxDepth: { type: "number", description: "Limit tree depth" },
205
+ maxDepth: { type: "number", description: "Max directory depth to traverse" },
194
206
  format: {
195
207
  type: "string",
196
208
  description: 'Output format: "tree" (default, visual tree), "flat" (one path per line), "grouped" (grouped by directory), "compact" (rtk-style summary with counts)',
197
209
  enum: ["tree", "flat", "grouped", "compact"],
198
210
  default: "tree"
199
211
  },
200
- includeMetadata: { type: "boolean", description: "Include language and symbol count (default: true)", default: true },
201
- projectPath: { type: "string", description: "Project root path (optional)" }
212
+ includeMetadata: { type: "boolean", description: "Include language and symbol count", default: true },
213
+ projectPath: { type: "string" }
202
214
  }
203
215
  }
204
216
  },
@@ -209,7 +221,7 @@ const tools = [
209
221
  type: "object",
210
222
  properties: {
211
223
  limit: { type: "number", description: "Max results 1-100 (default: 50)", default: 50 },
212
- projectPath: { type: "string", description: "Project root path (optional)" }
224
+ projectPath: { type: "string" }
213
225
  }
214
226
  }
215
227
  },
@@ -219,7 +231,7 @@ const tools = [
219
231
  inputSchema: {
220
232
  type: "object",
221
233
  properties: {
222
- projectPath: { type: "string", description: "Project root path (optional)" }
234
+ projectPath: { type: "string" }
223
235
  }
224
236
  }
225
237
  },
@@ -231,54 +243,54 @@ const tools = [
231
243
  properties: {
232
244
  from: { type: "string", description: "Source symbol name" },
233
245
  to: { type: "string", description: "Target symbol name" },
234
- projectPath: { type: "string", description: "Project root path (optional)" }
246
+ projectPath: { type: "string" }
235
247
  },
236
248
  required: ["from", "to"]
237
249
  }
238
250
  },
239
251
  {
240
252
  name: "kirograph_architecture",
241
- description: "Get the high-level software architecture: packages, layers, and their dependencies. Requires enableArchitecture=true in config. Call this first on a new task to orient yourself without reading files.",
253
+ description: "Show high-level architecture: packages, layers, dependencies.",
242
254
  inputSchema: {
243
255
  type: "object",
244
256
  properties: {
245
257
  level: {
246
258
  type: "string",
247
- description: 'View level: "packages" (package graph), "layers" (architectural layers), or "both" (default)',
259
+ description: "packages, layers, or both",
248
260
  enum: ["packages", "layers", "both"],
249
261
  default: "both"
250
262
  },
251
- includeFiles: { type: "boolean", description: "Include per-file package/layer assignments (default: false)", default: false },
252
- projectPath: { type: "string", description: "Project root path (optional)" }
263
+ includeFiles: { type: "boolean", description: "Include per-file package/layer assignments", default: false },
264
+ projectPath: { type: "string" }
253
265
  }
254
266
  }
255
267
  },
256
268
  {
257
269
  name: "kirograph_coupling",
258
- description: "Show coupling metrics for packages: afferent (Ca), efferent (Ce), and instability (Ce/(Ca+Ce)). High instability = depends on many others; low instability = depended on by many. Requires enableArchitecture=true.",
270
+ description: "Package coupling metrics: Ca, Ce, and instability (Ce/(Ca+Ce)).",
259
271
  inputSchema: {
260
272
  type: "object",
261
273
  properties: {
262
274
  sortBy: {
263
275
  type: "string",
264
- description: 'Sort order: "instability" (default), "afferent", or "efferent"',
276
+ description: "Sort by instability, ca, ce, or name",
265
277
  enum: ["instability", "afferent", "efferent"],
266
278
  default: "instability"
267
279
  },
268
280
  limit: { type: "number", description: "Max results (default: 20)", default: 20 },
269
- projectPath: { type: "string", description: "Project root path (optional)" }
281
+ projectPath: { type: "string" }
270
282
  }
271
283
  }
272
284
  },
273
285
  {
274
286
  name: "kirograph_package",
275
- description: "Drill into one package: files it contains, symbols it exports, packages it depends on, and packages that depend on it. Requires enableArchitecture=true.",
287
+ description: "Drill into a package: files, exports, and dependencies.",
276
288
  inputSchema: {
277
289
  type: "object",
278
290
  properties: {
279
291
  package: { type: "string", description: "Package name or path (partial match accepted)" },
280
292
  includeFiles: { type: "boolean", description: "List files in the package (default: true)", default: true },
281
- projectPath: { type: "string", description: "Project root path (optional)" }
293
+ projectPath: { type: "string" }
282
294
  },
283
295
  required: ["package"]
284
296
  }
@@ -290,7 +302,7 @@ const tools = [
290
302
  type: "object",
291
303
  properties: {
292
304
  limit: { type: "number", description: "Max results 1-100 (default: 20)", default: 20 },
293
- projectPath: { type: "string", description: "Project root path (optional)" }
305
+ projectPath: { type: "string" }
294
306
  }
295
307
  }
296
308
  },
@@ -301,78 +313,99 @@ const tools = [
301
313
  type: "object",
302
314
  properties: {
303
315
  limit: { type: "number", description: "Max results 1-100 (default: 20)", default: 20 },
304
- projectPath: { type: "string", description: "Project root path (optional)" }
316
+ projectPath: { type: "string" }
305
317
  }
306
318
  }
307
319
  },
308
320
  {
309
321
  name: "kirograph_diff",
310
- description: "Compare the current graph against a saved snapshot. Shows added/removed symbols and relationships since the snapshot was taken. Use `kirograph snapshot` CLI command to save a snapshot first.",
322
+ description: "Compare current graph against a saved snapshot.",
323
+ inputSchema: {
324
+ type: "object",
325
+ properties: {
326
+ snapshot: { type: "string", description: "Snapshot label; omit for latest" },
327
+ projectPath: { type: "string" }
328
+ }
329
+ }
330
+ },
331
+ {
332
+ name: "kirograph_snapshot_save",
333
+ description: "Save a snapshot of the current graph state. Snapshots can be compared with kirograph_diff to track structural changes over time.",
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ label: { type: "string", description: "Label for the snapshot (default: timestamp-based label)" },
338
+ projectPath: { type: "string" }
339
+ }
340
+ }
341
+ },
342
+ {
343
+ name: "kirograph_snapshot_list",
344
+ description: "List all saved graph snapshots with their labels, timestamps, and symbol/edge counts.",
311
345
  inputSchema: {
312
346
  type: "object",
313
347
  properties: {
314
- snapshot: { type: "string", description: "Snapshot label to compare against. Omit to use the latest saved snapshot." },
315
- projectPath: { type: "string", description: "Project root path (optional)" }
348
+ projectPath: { type: "string" }
316
349
  }
317
350
  }
318
351
  },
319
352
  {
320
353
  name: "kirograph_type_hierarchy",
321
- description: "Traverse the type hierarchy of a class or interface (base types and derived types).",
354
+ description: "Traverse base and derived types of a class or interface.",
322
355
  inputSchema: {
323
356
  type: "object",
324
357
  properties: {
325
358
  symbol: { type: "string", description: "Class or interface name" },
326
359
  direction: {
327
360
  type: "string",
328
- description: 'Direction: "up" for base types, "down" for derived types, "both" for all (default)',
361
+ description: "up=base, down=derived, both=all",
329
362
  enum: ["up", "down", "both"],
330
363
  default: "both"
331
364
  },
332
- projectPath: { type: "string", description: "Project root path (optional)" }
365
+ projectPath: { type: "string" }
333
366
  },
334
367
  required: ["symbol"]
335
368
  }
336
369
  },
337
370
  {
338
371
  name: "kirograph_exec",
339
- description: "Run a shell command and return token-optimized output. Automatically filters noise from git, test runners, linters, build tools, docker, and package managers. Use instead of raw shell for 60-90% token savings on verbose commands.",
372
+ description: "Run a shell command with token-optimized, noise-filtered output.",
340
373
  inputSchema: {
341
374
  type: "object",
342
375
  properties: {
343
- command: { type: "string", description: 'Shell command to execute (e.g., "git status", "npm test", "cargo build")' },
344
- cwd: { type: "string", description: "Working directory (default: project root)" },
376
+ command: { type: "string", description: "Shell command to execute" },
377
+ cwd: { type: "string", description: "Working directory" },
345
378
  level: {
346
379
  type: "string",
347
380
  description: 'Compression level: "normal" (balanced), "aggressive" (more compact), "ultra" (maximum compression)',
348
381
  enum: ["normal", "aggressive", "ultra"],
349
382
  default: "normal"
350
383
  },
351
- timeout: { type: "number", description: "Timeout in seconds (default: 60)", default: 60 },
352
- projectPath: { type: "string", description: "Project root path (optional)" }
384
+ timeout: { type: "number", description: "Timeout in seconds", default: 60 },
385
+ projectPath: { type: "string" }
353
386
  },
354
387
  required: ["command"]
355
388
  }
356
389
  },
357
390
  {
358
391
  name: "kirograph_gain",
359
- description: "Show token savings statistics \u2014 both from graph tools (vs manual file reads/grep) and from kirograph_exec shell compression.",
392
+ description: "Show token savings from graph tools and shell compression.",
360
393
  inputSchema: {
361
394
  type: "object",
362
395
  properties: {
363
396
  period: {
364
397
  type: "string",
365
- description: 'Time period: "session" (current), "today", "week", or "all"',
398
+ description: "Time window: session, hour, day, week, all",
366
399
  enum: ["session", "today", "week", "all"],
367
400
  default: "session"
368
401
  },
369
- projectPath: { type: "string", description: "Project root path (optional)" }
402
+ projectPath: { type: "string" }
370
403
  }
371
404
  }
372
405
  },
373
406
  {
374
407
  name: "kirograph_read",
375
- description: 'Read a file with caching and multiple modes. First read returns full content; subsequent reads of unchanged files return a compact "cached" marker (~13 tokens). Supports modes: full, map, signatures, diff, lines, imports, exports.',
408
+ description: "Read a file with caching; unchanged files return cache marker.",
376
409
  inputSchema: {
377
410
  type: "object",
378
411
  properties: {
@@ -385,58 +418,88 @@ const tools = [
385
418
  },
386
419
  start: { type: "number", description: "Start line (for lines mode)" },
387
420
  end: { type: "number", description: "End line (for lines mode)" },
388
- noCache: { type: "boolean", description: "Force fresh read, bypass cache (default: false)", default: false },
389
- projectPath: { type: "string", description: "Project root path (optional)" }
421
+ noCache: { type: "boolean", description: "Force fresh read, bypass cache", default: false },
422
+ projectPath: { type: "string" }
390
423
  },
391
424
  required: ["path"]
392
425
  }
393
426
  },
427
+ {
428
+ name: "kirograph_retrieve",
429
+ description: 'Retrieve cached file content by path (CCR \u2014 Cached Content Retrieval). Returns the full content stored in the session cache, or reads and caches the file if not yet seen. Use after kirograph_read returns a "[cached: file unchanged]" marker to get the actual content without a redundant filesystem read.',
430
+ inputSchema: {
431
+ type: "object",
432
+ properties: {
433
+ path: { type: "string", description: "File path to retrieve (absolute or relative to project root)" },
434
+ projectPath: { type: "string" }
435
+ },
436
+ required: ["path"]
437
+ }
438
+ },
439
+ {
440
+ name: "kirograph_compress",
441
+ description: "Compress text via rtk shell filters or caveman grammar engine.",
442
+ inputSchema: {
443
+ type: "object",
444
+ properties: {
445
+ text: { type: "string", description: "Text to compress" },
446
+ command: { type: "string", description: "Shell command text came from (selects rtk engine)" },
447
+ level: {
448
+ type: "string",
449
+ description: 'Compression intensity: "lite" / "normal" (light), "full" / "aggressive" (default), "ultra" (maximum)',
450
+ enum: ["lite", "normal", "full", "aggressive", "ultra"],
451
+ default: "full"
452
+ }
453
+ },
454
+ required: ["text"]
455
+ }
456
+ },
394
457
  {
395
458
  name: "kirograph_budget",
396
- description: "Show current session context budget usage. Returns tokens consumed, remaining budget, and utilization percentage.",
459
+ description: "Show session context budget: tokens used, remaining, utilization.",
397
460
  inputSchema: {
398
461
  type: "object",
399
462
  properties: {
400
- reset: { type: "boolean", description: "Reset session budget counters (default: false)", default: false },
401
- projectPath: { type: "string", description: "Project root path (optional)" }
463
+ reset: { type: "boolean", description: "Reset session budget counters", default: false },
464
+ projectPath: { type: "string" }
402
465
  }
403
466
  }
404
467
  },
405
468
  {
406
469
  name: "kirograph_flows",
407
- description: "Trace execution flows from entry points (routes, handlers, main functions) through the call graph. Returns ordered call chains sorted by criticality.",
470
+ description: "Trace execution flows from entry points through the call graph.",
408
471
  inputSchema: {
409
472
  type: "object",
410
473
  properties: {
411
- entryPoint: { type: "string", description: "Symbol name to trace from, or omit to auto-detect entry points" },
474
+ entryPoint: { type: "string", description: "Symbol to trace; omit to auto-detect" },
412
475
  maxFlows: { type: "number", description: "Max number of flows to return (default 10)" },
413
476
  maxDepth: { type: "number", description: "Max call chain depth (default 10)" },
414
- projectPath: { type: "string", description: "Project root path (optional)" }
477
+ projectPath: { type: "string" }
415
478
  }
416
479
  }
417
480
  },
418
481
  {
419
482
  name: "kirograph_communities",
420
- description: "Detect code communities (clusters of related symbols) using graph-based community detection. Shows which code belongs together and how communities are coupled.",
483
+ description: "Detect clusters of related symbols via graph community detection.",
421
484
  inputSchema: {
422
485
  type: "object",
423
486
  properties: {
424
- resolution: { type: "number", description: "Resolution parameter (default 1.0, higher = more communities)" },
487
+ resolution: { type: "number", description: "Granularity 0.1\u20132.0; higher=more smaller clusters" },
425
488
  limit: { type: "number", description: "Max communities to return (default 15)" },
426
- projectPath: { type: "string", description: "Project root path (optional)" }
489
+ projectPath: { type: "string" }
427
490
  }
428
491
  }
429
492
  },
430
493
  {
431
494
  name: "kirograph_refactor",
432
- description: 'Refactoring assistant. Use mode "rename" to preview all locations that reference a symbol (rename preview). Use mode "suggest" for community-driven refactoring suggestions.',
495
+ description: "Rename preview (all references) or function summarization.",
433
496
  inputSchema: {
434
497
  type: "object",
435
498
  properties: {
436
- mode: { type: "string", description: 'Mode: "rename" (preview references) or "suggest" (refactoring suggestions)', enum: ["rename", "suggest"] },
499
+ mode: { type: "string", description: "rename=preview references, summarize=summary", enum: ["rename", "suggest"] },
437
500
  symbol: { type: "string", description: "Symbol name (required for rename mode)" },
438
501
  limit: { type: "number", description: "Max results (default 10)" },
439
- projectPath: { type: "string", description: "Project root path (optional)" }
502
+ projectPath: { type: "string" }
440
503
  },
441
504
  required: ["mode"]
442
505
  }
@@ -444,7 +507,7 @@ const tools = [
444
507
  // ── Memory tools (require enableMemory=true) ────────────────────────────────
445
508
  {
446
509
  name: "kirograph_mem_search",
447
- description: "Search project memory for past decisions, errors, patterns, and context. Returns observations ranked by relevance.",
510
+ description: "Search project memory for decisions, errors, patterns, context.",
448
511
  inputSchema: {
449
512
  type: "object",
450
513
  properties: {
@@ -457,14 +520,14 @@ const tools = [
457
520
  limit: { type: "number", description: "Max results (default: 10)", default: 10 },
458
521
  sessionId: { type: "string", description: "Filter to specific session" },
459
522
  asOf: { type: "number", description: "Query facts valid at this timestamp (epoch ms). Filters out expired/superseded observations." },
460
- projectPath: { type: "string", description: "Project root path (optional)" }
523
+ projectPath: { type: "string" }
461
524
  },
462
525
  required: ["query"]
463
526
  }
464
527
  },
465
528
  {
466
529
  name: "kirograph_mem_store",
467
- description: "Store an observation in project memory. Content is automatically compressed (if caveman mode is on) and linked to relevant code symbols.",
530
+ description: "Store an observation in project memory.",
468
531
  inputSchema: {
469
532
  type: "object",
470
533
  properties: {
@@ -475,9 +538,9 @@ const tools = [
475
538
  enum: ["decision", "error", "pattern", "architecture", "summary", "note"],
476
539
  default: "note"
477
540
  },
478
- projectPath: { type: "string", description: "Project root path (optional)" },
541
+ projectPath: { type: "string" },
479
542
  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." }
543
+ reviewAfter: { type: "number", description: "ISO date to flag for review" }
481
544
  },
482
545
  required: ["content"]
483
546
  }
@@ -488,42 +551,42 @@ const tools = [
488
551
  inputSchema: {
489
552
  type: "object",
490
553
  properties: {
491
- limit: { type: "number", description: "Number of sessions to show (default: 5)", default: 5 },
554
+ limit: { type: "number", description: "Number of sessions to show", default: 5 },
492
555
  sessionId: { type: "string", description: "Show observations for a specific session" },
493
- projectPath: { type: "string", description: "Project root path (optional)" }
556
+ projectPath: { type: "string" }
494
557
  }
495
558
  }
496
559
  },
497
560
  {
498
561
  name: "kirograph_mem_status",
499
- description: "Memory subsystem health: session count, observations, embedding coverage, storage size.",
562
+ description: "Memory subsystem health: sessions, observations, embeddings, size.",
500
563
  inputSchema: {
501
564
  type: "object",
502
565
  properties: {
503
- projectPath: { type: "string", description: "Project root path (optional)" }
566
+ projectPath: { type: "string" }
504
567
  }
505
568
  }
506
569
  },
507
570
  // ── Engram-parity mem tools ──────────────────────────────────────────────────
508
571
  {
509
572
  name: "kirograph_mem_review",
510
- description: "List observations past their review_after date \u2014 stale facts the agent should re-evaluate, update, or supersede.",
573
+ description: "List observations past their review_after date.",
511
574
  inputSchema: {
512
575
  type: "object",
513
576
  properties: {
514
577
  limit: { type: "number", description: "Max results (default: 20)", default: 20 },
515
- projectPath: { type: "string", description: "Project root path (optional)" }
578
+ projectPath: { type: "string" }
516
579
  }
517
580
  }
518
581
  },
519
582
  {
520
583
  name: "kirograph_mem_mark_reviewed",
521
- description: "Mark an observation as reviewed \u2014 clears its review_after date.",
584
+ description: "Clear review_after date on an observation.",
522
585
  inputSchema: {
523
586
  type: "object",
524
587
  properties: {
525
588
  id: { type: "string", description: "Observation ID" },
526
- projectPath: { type: "string", description: "Project root path (optional)" }
589
+ projectPath: { type: "string" }
527
590
  },
528
591
  required: ["id"]
529
592
  }
@@ -538,9 +601,9 @@ const tools = [
538
601
  observationB: { type: "string", description: "Second observation ID or topic_key" },
539
602
  relation: { type: "string", enum: ["supersedes", "conflicts_with", "compatible", "scoped", "related", "not_conflict"], description: "Relation type" },
540
603
  confidence: { type: "number", description: "Confidence 0.0\u20131.0 (default: 1.0)" },
541
- reason: { type: "string", description: "Explanation for the relation" },
604
+ reason: { type: "string", description: "Explanation of the relation" },
542
605
  evidence: { type: "string", description: "Supporting evidence text" },
543
- projectPath: { type: "string", description: "Project root path (optional)" }
606
+ projectPath: { type: "string" }
544
607
  },
545
608
  required: ["observationA", "observationB", "relation"]
546
609
  }
@@ -554,218 +617,303 @@ const tools = [
554
617
  relationId: { type: "string", description: "Relation ID (from kirograph_mem_compare or kirograph_mem_search)" },
555
618
  relation: { type: "string", enum: ["supersedes", "conflicts_with", "compatible", "scoped", "related", "not_conflict"], description: "Final relation type" },
556
619
  confidence: { type: "number", description: "Final confidence 0.0\u20131.0" },
557
- reason: { type: "string", description: "Reasoning for judgment" },
620
+ reason: { type: "string", description: "Reasoning" },
558
621
  evidence: { type: "string", description: "Supporting evidence" },
559
- projectPath: { type: "string", description: "Project root path (optional)" }
622
+ projectPath: { type: "string" }
560
623
  },
561
624
  required: ["relationId", "relation", "confidence"]
562
625
  }
563
626
  },
564
627
  {
565
628
  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.",
629
+ description: "Extract and store structured learnings from freeform text.",
567
630
  inputSchema: {
568
631
  type: "object",
569
632
  properties: {
570
633
  content: { type: "string", description: "Text block with structured sections" },
571
- projectPath: { type: "string", description: "Project root path (optional)" }
634
+ projectPath: { type: "string" }
572
635
  },
573
636
  required: ["content"]
574
637
  }
575
638
  },
576
639
  {
577
640
  name: "kirograph_mem_save_prompt",
578
- description: "Save the current user prompt to session memory for context reconstruction.",
641
+ description: "Save the current user prompt to session memory.",
579
642
  inputSchema: {
580
643
  type: "object",
581
644
  properties: {
582
- content: { type: "string", description: "Prompt content to save" },
583
- projectPath: { type: "string", description: "Project root path (optional)" }
645
+ content: { type: "string", description: "The user prompt text to save" },
646
+ projectPath: { type: "string" }
584
647
  },
585
648
  required: ["content"]
586
649
  }
587
650
  },
588
651
  {
589
652
  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".',
653
+ description: "Suggest a stable topic_key slug for an observation.",
591
654
  inputSchema: {
592
655
  type: "object",
593
656
  properties: {
594
657
  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)" }
658
+ title: { type: "string", description: "Short description of the observation" },
659
+ projectPath: { type: "string" }
597
660
  },
598
661
  required: ["kind", "title"]
599
662
  }
600
663
  },
601
664
  {
602
665
  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.",
666
+ description: "Scan recent observations for potential conflicts using FTS.",
604
667
  inputSchema: {
605
668
  type: "object",
606
669
  properties: {
607
670
  limit: { type: "number", description: "Max observations to scan (default: 50)", default: 50 },
608
- projectPath: { type: "string", description: "Project root path (optional)" }
671
+ projectPath: { type: "string" }
672
+ }
673
+ }
674
+ },
675
+ {
676
+ name: "kirograph_mem_prune",
677
+ description: "Remove memory observations older than a given duration.",
678
+ inputSchema: {
679
+ type: "object",
680
+ properties: {
681
+ olderThan: { type: "string", description: 'Duration threshold e.g. "90d" or "6m"', default: "90d" },
682
+ projectPath: { type: "string" }
683
+ }
684
+ }
685
+ },
686
+ {
687
+ name: "kirograph_mem_lint",
688
+ description: "Health check: stale links, model mismatch, orphaned sessions.",
689
+ inputSchema: {
690
+ type: "object",
691
+ properties: {
692
+ fix: { type: "boolean", description: "Auto-remove stale links", default: false },
693
+ projectPath: { type: "string" }
609
694
  }
610
695
  }
611
696
  },
697
+ {
698
+ name: "kirograph_mem_conflicts_list",
699
+ description: "List pending conflict relations needing resolution.",
700
+ inputSchema: {
701
+ type: "object",
702
+ properties: {
703
+ limit: { type: "number", description: "Max results (default: 20)", default: 20 },
704
+ projectPath: { type: "string" }
705
+ }
706
+ }
707
+ },
708
+ {
709
+ name: "kirograph_mem_conflicts_ignore",
710
+ description: "Dismiss a pending conflict relation as not relevant.",
711
+ inputSchema: {
712
+ type: "object",
713
+ properties: {
714
+ relationId: { type: "string", description: "Relation ID to ignore (from kirograph_mem_conflicts_list)" },
715
+ projectPath: { type: "string" }
716
+ },
717
+ required: ["relationId"]
718
+ }
719
+ },
612
720
  // ── Wiki tools (require enableWiki=true) ────────────────────────────────────
613
721
  {
614
722
  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.",
723
+ description: "Build ingest prompt for LLM to produce a WIKI_DIFF.",
616
724
  inputSchema: {
617
725
  type: "object",
618
726
  properties: {
619
727
  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)" }
728
+ sourceName: { type: "string", description: "Name or path of the source", default: "source" },
729
+ projectPath: { type: "string" }
622
730
  },
623
731
  required: ["source"]
624
732
  }
625
733
  },
626
734
  {
627
735
  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.",
736
+ description: "Apply a WIKI_DIFF to the wiki filesystem and SQLite index.",
629
737
  inputSchema: {
630
738
  type: "object",
631
739
  properties: {
632
740
  diff: { type: "string", description: "WIKI_DIFF string with WIKI_DIFF_START/END blocks" },
633
- projectPath: { type: "string", description: "Project root path (optional)" }
741
+ projectPath: { type: "string" }
634
742
  },
635
743
  required: ["diff"]
636
744
  }
637
745
  },
638
746
  {
639
747
  name: "kirograph_wiki_search",
640
- description: "Full-text search over wiki pages. Returns page slugs, titles, and previews ranked by relevance.",
748
+ description: "FTS over wiki pages; returns slugs, titles, previews.",
641
749
  inputSchema: {
642
750
  type: "object",
643
751
  properties: {
644
752
  query: { type: "string", description: "Search query" },
645
753
  limit: { type: "number", description: "Max results (default: 5)", default: 5 },
646
- projectPath: { type: "string", description: "Project root path (optional)" }
754
+ projectPath: { type: "string" }
647
755
  },
648
756
  required: ["query"]
649
757
  }
650
758
  },
651
759
  {
652
760
  name: "kirograph_wiki_page",
653
- description: "Retrieve the full markdown content of a wiki page by slug.",
761
+ description: "Retrieve full markdown of a wiki page by slug.",
654
762
  inputSchema: {
655
763
  type: "object",
656
764
  properties: {
657
- slug: { type: "string", description: 'Page slug (e.g. "AuthService", "arch/auth-model")' },
658
- projectPath: { type: "string", description: "Project root path (optional)" }
765
+ slug: { type: "string", description: 'Page slug (e.g. "arch/auth-model")' },
766
+ projectPath: { type: "string" }
659
767
  },
660
768
  required: ["slug"]
661
769
  }
662
770
  },
663
771
  {
664
772
  name: "kirograph_wiki_lint",
665
- description: "Health check the wiki for broken links, orphan pages, stale sources, and potential contradictions between pages.",
773
+ description: "Check wiki for broken links, orphans, stale sources, contradictions.",
666
774
  inputSchema: {
667
775
  type: "object",
668
776
  properties: {
669
- projectPath: { type: "string", description: "Project root path (optional)" }
777
+ projectPath: { type: "string" }
670
778
  }
671
779
  }
672
780
  },
673
781
  {
674
782
  name: "kirograph_wiki_list",
675
- description: "List all wiki pages with slug, title, source count, and last updated date.",
783
+ description: "List all wiki pages with slug, title, source count, date.",
676
784
  inputSchema: {
677
785
  type: "object",
678
786
  properties: {
679
- projectPath: { type: "string", description: "Project root path (optional)" }
787
+ projectPath: { type: "string" }
788
+ }
789
+ }
790
+ },
791
+ {
792
+ name: "kirograph_wiki_synthesize",
793
+ description: 'Run local-model wiki synthesis: process the pending source queue (requires wikiSynthesisMode: "local").',
794
+ inputSchema: {
795
+ type: "object",
796
+ properties: {
797
+ projectPath: { type: "string" }
798
+ }
799
+ }
800
+ },
801
+ {
802
+ name: "kirograph_wiki_init",
803
+ description: "Initialize the wiki: create SCHEMA.md and MANIFEST.md in .kirograph/wiki/.",
804
+ inputSchema: {
805
+ type: "object",
806
+ properties: {
807
+ projectPath: { type: "string" }
808
+ }
809
+ }
810
+ },
811
+ {
812
+ name: "kirograph_wiki_reindex",
813
+ description: "Rebuild the SQLite index from .kirograph/wiki/*.md files on disk.",
814
+ inputSchema: {
815
+ type: "object",
816
+ properties: {
817
+ projectPath: { type: "string" }
818
+ }
819
+ }
820
+ },
821
+ {
822
+ name: "kirograph_wiki_status",
823
+ description: "Show wiki subsystem stats: page count, total sources, oldest/newest page dates.",
824
+ inputSchema: {
825
+ type: "object",
826
+ properties: {
827
+ projectPath: { type: "string" }
680
828
  }
681
829
  }
682
830
  },
683
831
  // ── Docs tools (require enableDocs=true) ────────────────────────────────────
684
832
  {
685
833
  name: "kirograph_docs_toc",
686
- description: "Get table of contents for a documentation file or the whole project. Returns section IDs, titles, levels, and summaries.",
834
+ description: "Get TOC for a doc file or whole project (IDs, titles, levels, summaries).",
687
835
  inputSchema: {
688
836
  type: "object",
689
837
  properties: {
690
838
  file: { type: "string", description: "Filter to a specific doc file (relative path). Omit for project-wide TOC." },
691
- tree: { type: "boolean", description: "Return nested tree structure (default: false, flat list)", default: false },
692
- projectPath: { type: "string", description: "Project root path (optional)" }
839
+ tree: { type: "boolean", description: "Return nested tree structure.", default: false },
840
+ projectPath: { type: "string" }
693
841
  }
694
842
  }
695
843
  },
696
844
  {
697
845
  name: "kirograph_docs_search",
698
- description: "Search documentation sections by query. Returns matching sections ranked by relevance. Independent from kirograph_search (code-only).",
846
+ description: "Search documentation sections by query, ranked by relevance.",
699
847
  inputSchema: {
700
848
  type: "object",
701
849
  properties: {
702
850
  query: { type: "string", description: "Search query (natural language or keywords)" },
703
- file: { type: "string", description: "Narrow search to a specific doc file (relative path)" },
851
+ file: { type: "string", description: "Limit search to a specific doc file." },
704
852
  limit: { type: "number", description: "Max results (default: 10)", default: 10 },
705
- projectPath: { type: "string", description: "Project root path (optional)" }
853
+ projectPath: { type: "string" }
706
854
  },
707
855
  required: ["query"]
708
856
  }
709
857
  },
710
858
  {
711
859
  name: "kirograph_docs_section",
712
- description: "Retrieve full content of a documentation section by its stable ID. Use context=true to also get ancestor headings and child summaries.",
860
+ description: "Retrieve full content of a doc section by its stable ID.",
713
861
  inputSchema: {
714
862
  type: "object",
715
863
  properties: {
716
- id: { type: "string", description: "Section ID (from kirograph_docs_toc or kirograph_docs_search results)" },
717
- context: { type: "boolean", description: "Include ancestor heading chain and child summaries (default: false)", default: false },
718
- projectPath: { type: "string", description: "Project root path (optional)" }
864
+ id: { type: "string", description: "Section ID." },
865
+ context: { type: "boolean", description: "Include ancestor headings and child summaries.", default: false },
866
+ projectPath: { type: "string" }
719
867
  },
720
868
  required: ["id"]
721
869
  }
722
870
  },
723
871
  {
724
872
  name: "kirograph_docs_outline",
725
- description: "Get the heading hierarchy for a single documentation file. Lighter than full TOC when you know which file is relevant.",
873
+ description: "Get heading hierarchy for a single documentation file.",
726
874
  inputSchema: {
727
875
  type: "object",
728
876
  properties: {
729
- file: { type: "string", description: "Relative path to the doc file" },
730
- projectPath: { type: "string", description: "Project root path (optional)" }
877
+ file: { type: "string", description: "Documentation file path (relative to project root)." },
878
+ projectPath: { type: "string" }
731
879
  },
732
880
  required: ["file"]
733
881
  }
734
882
  },
735
883
  {
736
884
  name: "kirograph_docs_refs",
737
- description: "Find code symbols referenced by a doc section, or doc sections that reference a code symbol. Bidirectional lookup.",
885
+ description: "Bidirectional lookup: doc section\u2194code symbol references.",
738
886
  inputSchema: {
739
887
  type: "object",
740
888
  properties: {
741
- sectionId: { type: "string", description: "Doc section ID (find code symbols it references)" },
742
- nodeId: { type: "string", description: "Code symbol qualified name (find doc sections that reference it)" },
743
- projectPath: { type: "string", description: "Project root path (optional)" }
889
+ sectionId: { type: "string", description: "Doc section ID to find referenced symbols." },
890
+ nodeId: { type: "string", description: "Code symbol ID to find referencing doc sections." },
891
+ projectPath: { type: "string" }
744
892
  }
745
893
  }
746
894
  },
747
895
  // ── Data tools (require enableData=true) ────────────────────────────────────
748
896
  {
749
897
  name: "kirograph_data_list",
750
- description: "List all indexed datasets with row counts, column counts, and file sizes.",
751
- inputSchema: { type: "object", properties: { projectPath: { type: "string", description: "Project root path (optional)" } } }
898
+ description: "List all indexed datasets with row/column counts and file sizes.",
899
+ inputSchema: { type: "object", properties: { projectPath: { type: "string" } } }
752
900
  },
753
901
  {
754
902
  name: "kirograph_data_describe",
755
- description: "Full schema profile of a dataset: column names, types, cardinality, null%, sample values. Use to orient on a dataset without reading any rows.",
903
+ description: "Full schema profile: types, cardinality, null%, sample values.",
756
904
  inputSchema: {
757
905
  type: "object",
758
906
  properties: {
759
907
  dataset: { type: "string", description: "Dataset ID (from kirograph_data_list)" },
760
- column: { type: "string", description: "Optional: deep-dive on a single column" },
761
- projectPath: { type: "string", description: "Project root path (optional)" }
908
+ column: { type: "string", description: "Deep-dive on a single column." },
909
+ projectPath: { type: "string" }
762
910
  },
763
911
  required: ["dataset"]
764
912
  }
765
913
  },
766
914
  {
767
915
  name: "kirograph_data_query",
768
- description: "Filtered row retrieval with structured operators. Returns only matching rows (max 500). Use instead of reading raw data files.",
916
+ description: "Filtered row retrieval with structured operators (max 500 rows).",
769
917
  inputSchema: {
770
918
  type: "object",
771
919
  properties: {
@@ -774,14 +922,14 @@ const tools = [
774
922
  columns: { type: "array", description: "Column projection (only return these columns)" },
775
923
  limit: { type: "number", description: "Max rows (default: 100, hard cap: 500)", default: 100 },
776
924
  offset: { type: "number", description: "Pagination offset", default: 0 },
777
- projectPath: { type: "string", description: "Project root path (optional)" }
925
+ projectPath: { type: "string" }
778
926
  },
779
927
  required: ["dataset"]
780
928
  }
781
929
  },
782
930
  {
783
931
  name: "kirograph_data_aggregate",
784
- description: "Server-side GROUP BY aggregation. Computation runs in SQLite \u2014 only the result set enters context. Use for count, sum, avg, min, max questions.",
932
+ description: "Server-side GROUP BY aggregation: count, sum, avg, min, max.",
785
933
  inputSchema: {
786
934
  type: "object",
787
935
  properties: {
@@ -789,27 +937,27 @@ const tools = [
789
937
  groupBy: { type: "array", description: "Columns to group by" },
790
938
  metrics: { type: "array", description: "Array of {column, op} metrics. Ops: count, sum, avg, min, max, count_distinct" },
791
939
  filters: { type: "array", description: "Optional pre-filters (same format as kirograph_data_query)" },
792
- projectPath: { type: "string", description: "Project root path (optional)" }
940
+ projectPath: { type: "string" }
793
941
  },
794
942
  required: ["dataset", "groupBy", "metrics"]
795
943
  }
796
944
  },
797
945
  {
798
946
  name: "kirograph_data_search",
799
- description: "Search column names and sample values by keyword. Tells you which column holds the answer without loading data.",
947
+ description: "Search column names and sample values by keyword.",
800
948
  inputSchema: {
801
949
  type: "object",
802
950
  properties: {
803
951
  dataset: { type: "string", description: "Dataset ID" },
804
952
  query: { type: "string", description: "Search keyword" },
805
- projectPath: { type: "string", description: "Project root path (optional)" }
953
+ projectPath: { type: "string" }
806
954
  },
807
955
  required: ["dataset", "query"]
808
956
  }
809
957
  },
810
958
  {
811
959
  name: "kirograph_data_join",
812
- description: "SQL JOIN across two indexed datasets. Combines data without loading either file into context.",
960
+ description: "SQL JOIN across two indexed datasets.",
813
961
  inputSchema: {
814
962
  type: "object",
815
963
  properties: {
@@ -820,50 +968,126 @@ const tools = [
820
968
  type: { type: "string", description: "Join type: inner (default), left, right", enum: ["inner", "left", "right"], default: "inner" },
821
969
  columns: { type: "array", description: "Column projection (prefix with dataset ID)" },
822
970
  limit: { type: "number", description: "Max rows (default: 100, hard cap: 500)", default: 100 },
823
- projectPath: { type: "string", description: "Project root path (optional)" }
971
+ projectPath: { type: "string" }
824
972
  },
825
973
  required: ["left", "right", "leftColumn", "rightColumn"]
826
974
  }
827
975
  },
828
976
  {
829
977
  name: "kirograph_data_correlations",
830
- description: "Pairwise Pearson correlations between numeric columns. Discovers hidden relationships without loading data.",
978
+ description: "Pairwise Pearson correlations between numeric columns.",
831
979
  inputSchema: {
832
980
  type: "object",
833
981
  properties: {
834
982
  dataset: { type: "string", description: "Dataset ID" },
835
983
  threshold: { type: "number", description: "Min absolute correlation to include (default: 0.3)", default: 0.3 },
836
- projectPath: { type: "string", description: "Project root path (optional)" }
984
+ projectPath: { type: "string" }
837
985
  },
838
986
  required: ["dataset"]
839
987
  }
840
988
  },
841
989
  {
842
990
  name: "kirograph_data_quality",
843
- description: "Data quality triage: rank columns by risk (null rate, cardinality anomalies, type issues). Identifies problematic columns without loading data.",
991
+ description: "Rank columns by data quality risk: nulls, cardinality, type issues.",
844
992
  inputSchema: {
845
993
  type: "object",
846
994
  properties: {
847
995
  dataset: { type: "string", description: "Dataset ID" },
848
- projectPath: { type: "string", description: "Project root path (optional)" }
996
+ projectPath: { type: "string" }
849
997
  },
850
998
  required: ["dataset"]
851
999
  }
852
1000
  },
1001
+ {
1002
+ name: "kirograph_data_drift",
1003
+ description: "Schema drift between last two index runs: columns, types, row counts.",
1004
+ inputSchema: {
1005
+ type: "object",
1006
+ properties: {
1007
+ dataset: { type: "string", description: "Dataset ID (from kirograph_data_list)" },
1008
+ projectPath: { type: "string" }
1009
+ },
1010
+ required: ["dataset"]
1011
+ }
1012
+ },
1013
+ {
1014
+ name: "kirograph_data_history",
1015
+ description: "History of schema snapshots: timestamps, row/column counts, hashes.",
1016
+ inputSchema: {
1017
+ type: "object",
1018
+ properties: {
1019
+ dataset: { type: "string", description: "Dataset ID (from kirograph_data_list)" },
1020
+ limit: { type: "number", description: "Max snapshots to return (default: 10)", default: 10 },
1021
+ projectPath: { type: "string" }
1022
+ },
1023
+ required: ["dataset"]
1024
+ }
1025
+ },
1026
+ // ── Watchmen tools (require enableMemory=true + enableWatchmen=true) ──────────
1027
+ {
1028
+ name: "kirograph_watchmen_status",
1029
+ description: "Show watchmen status: pending observation count, synthesis threshold, and which brief files would be written on next synthesis.",
1030
+ inputSchema: {
1031
+ type: "object",
1032
+ properties: {
1033
+ projectPath: { type: "string" }
1034
+ }
1035
+ }
1036
+ },
1037
+ {
1038
+ name: "kirograph_watchmen_synthesize",
1039
+ description: 'Run watchmen synthesis immediately using the local model (requires watchmenSynthesisMode: "local"). Processes pending observations into a workspace brief.',
1040
+ inputSchema: {
1041
+ type: "object",
1042
+ properties: {
1043
+ force: { type: "boolean", description: "Run even if below threshold (default: false)", default: false },
1044
+ projectPath: { type: "string" }
1045
+ }
1046
+ }
1047
+ },
1048
+ {
1049
+ name: "kirograph_watchmen_reset",
1050
+ description: "Reset the watchmen synthesis counter by storing a summary observation, without running full synthesis.",
1051
+ inputSchema: {
1052
+ type: "object",
1053
+ properties: {
1054
+ projectPath: { type: "string" }
1055
+ }
1056
+ }
1057
+ },
1058
+ // ── Affected tests ────────────────────────────────────────────────────────────
1059
+ {
1060
+ name: "kirograph_affected",
1061
+ description: "Find test files affected by a set of changed source files, by traversing the dependency graph.",
1062
+ inputSchema: {
1063
+ type: "object",
1064
+ properties: {
1065
+ files: {
1066
+ type: "array",
1067
+ items: { type: "string" },
1068
+ description: "List of changed source file paths (relative to project root)"
1069
+ },
1070
+ depth: { type: "number", description: "Max dependency traversal depth (default: 5)", default: 5 },
1071
+ testPattern: { type: "string", description: "Custom glob to identify test files (optional)" },
1072
+ projectPath: { type: "string" }
1073
+ },
1074
+ required: ["files"]
1075
+ }
1076
+ },
853
1077
  // ── Security tools (require enableSecurity=true) ────────────────────────────
854
1078
  {
855
1079
  name: "kirograph_security",
856
- description: "Security overview: vulnerability counts, affected/not_affected verdicts, stale data warnings. Requires enableSecurity=true and enableArchitecture=true.",
1080
+ description: "Security overview: vuln counts, verdicts, stale data warnings.",
857
1081
  inputSchema: {
858
1082
  type: "object",
859
1083
  properties: {
860
- projectPath: { type: "string", description: "Project root path (optional)" }
1084
+ projectPath: { type: "string" }
861
1085
  }
862
1086
  }
863
1087
  },
864
1088
  {
865
1089
  name: "kirograph_vulns",
866
- description: "List vulnerabilities with reachability verdicts, severity, and affected components. Supports on-demand refresh.",
1090
+ description: "List vulns with reachability verdicts, severity, affected components.",
867
1091
  inputSchema: {
868
1092
  type: "object",
869
1093
  properties: {
@@ -871,13 +1095,13 @@ const tools = [
871
1095
  verdict: { type: "string", description: "Filter by reachability verdict", enum: ["affected", "not_affected", "under_investigation"] },
872
1096
  limit: { type: "number", description: "Max results (default: 20)", default: 20 },
873
1097
  refresh: { type: "boolean", description: "Trigger fresh vulnerability enrichment before listing (default: false)", default: false },
874
- projectPath: { type: "string", description: "Project root path (optional)" }
1098
+ projectPath: { type: "string" }
875
1099
  }
876
1100
  }
877
1101
  },
878
1102
  {
879
1103
  name: "kirograph_vuln_add",
880
- description: "Manually register a CVE against a dependency without querying external databases.",
1104
+ description: "Manually register a CVE against a dependency.",
881
1105
  inputSchema: {
882
1106
  type: "object",
883
1107
  properties: {
@@ -886,178 +1110,178 @@ const tools = [
886
1110
  severity: { type: "number", description: "CVSS v3.1 base score (optional)" },
887
1111
  summary: { type: "string", description: "Vulnerability summary (optional)" },
888
1112
  fixedVersion: { type: "string", description: "Version that fixes the vulnerability (optional)" },
889
- projectPath: { type: "string", description: "Project root path (optional)" }
1113
+ projectPath: { type: "string" }
890
1114
  },
891
1115
  required: ["cveId", "package"]
892
1116
  }
893
1117
  },
894
1118
  {
895
1119
  name: "kirograph_vuln_suppress",
896
- description: "Suppress a CVE so it no longer appears in vulnerability reports (mark as false positive or accepted risk). Suppressions are stored in .kirograph/security-suppressions.json.",
1120
+ description: "Suppress a CVE from vulnerability reports.",
897
1121
  inputSchema: {
898
1122
  type: "object",
899
1123
  properties: {
900
1124
  cveId: { type: "string", description: 'CVE identifier to suppress (e.g., "CVE-2024-1234")' },
901
1125
  reason: { type: "string", description: "Reason for suppression (optional)" },
902
1126
  expires: { type: "string", description: 'Expiry date in ISO format after which the suppression is removed (e.g. "2026-12-31", optional)' },
903
- projectPath: { type: "string", description: "Project root path (optional)" }
1127
+ projectPath: { type: "string" }
904
1128
  },
905
1129
  required: ["cveId"]
906
1130
  }
907
1131
  },
908
1132
  {
909
1133
  name: "kirograph_sbom",
910
- description: "Generate and return CycloneDX 1.5 SBOM JSON for the project.",
1134
+ description: "Generate CycloneDX 1.5 SBOM JSON for the project.",
911
1135
  inputSchema: {
912
1136
  type: "object",
913
1137
  properties: {
914
- projectPath: { type: "string", description: "Project root path (optional)" }
1138
+ projectPath: { type: "string" }
915
1139
  }
916
1140
  }
917
1141
  },
918
1142
  {
919
1143
  name: "kirograph_vex",
920
- description: "Generate and return CycloneDX 1.5 VEX JSON with reachability verdicts.",
1144
+ description: "Generate CycloneDX 1.5 VEX JSON with reachability verdicts.",
921
1145
  inputSchema: {
922
1146
  type: "object",
923
1147
  properties: {
924
- projectPath: { type: "string", description: "Project root path (optional)" }
1148
+ projectPath: { type: "string" }
925
1149
  }
926
1150
  }
927
1151
  },
928
1152
  {
929
1153
  name: "kirograph_reachability",
930
- description: "Check reachability for a specific dependency or vulnerability. Returns verdict, paths, and impact summary.",
1154
+ description: "Check reachability for a dependency or CVE.",
931
1155
  inputSchema: {
932
1156
  type: "object",
933
1157
  properties: {
934
1158
  target: { type: "string", description: "Dependency name or CVE ID to check reachability for" },
935
- projectPath: { type: "string", description: "Project root path (optional)" }
1159
+ projectPath: { type: "string" }
936
1160
  },
937
1161
  required: ["target"]
938
1162
  }
939
1163
  },
940
1164
  {
941
1165
  name: "kirograph_staleness",
942
- description: "Check dependency freshness \u2014 identifies packages significantly behind their latest published version.",
1166
+ description: "Check dependency freshness against latest published versions.",
943
1167
  inputSchema: {
944
1168
  type: "object",
945
1169
  properties: {
946
1170
  threshold: { type: "number", description: "Only return packages with staleness_score >= threshold (default: 0.3)", default: 0.3 },
947
1171
  refresh: { type: "boolean", description: "Fetch latest version info from registries before listing (default: false)", default: false },
948
- projectPath: { type: "string", description: "Project root path (optional)" }
1172
+ projectPath: { type: "string" }
949
1173
  }
950
1174
  }
951
1175
  },
952
1176
  {
953
1177
  name: "kirograph_licenses",
954
- description: "Show dependency licenses and check against the configured license policy (deny/warn lists). Supports wildcard patterns (e.g. GPL-* matches GPL-2.0, GPL-3.0-only).",
1178
+ description: "Show licenses and check against configured deny/warn policy.",
955
1179
  inputSchema: {
956
1180
  type: "object",
957
1181
  properties: {
958
1182
  policy: { type: "boolean", description: "Return only policy violations (default: false)", default: false },
959
- projectPath: { type: "string", description: "Project root path (optional)" }
1183
+ projectPath: { type: "string" }
960
1184
  }
961
1185
  }
962
1186
  },
963
1187
  {
964
1188
  name: "kirograph_attack_surface",
965
- description: "Map the attack surface: all HTTP routes and their paths to vulnerable dependencies, with hop count, authentication status, and risk score.",
1189
+ description: "Map HTTP routes to vulnerable deps with hop count and auth status.",
966
1190
  inputSchema: {
967
1191
  type: "object",
968
1192
  properties: {
969
1193
  limit: { type: "number", description: "Max routes to return (default: 20)", default: 20 },
970
1194
  publicOnly: { type: "boolean", description: "Only return public/unauthenticated routes", default: false },
971
- projectPath: { type: "string", description: "Project root path (optional)" }
1195
+ projectPath: { type: "string" }
972
1196
  }
973
1197
  }
974
1198
  },
975
1199
  {
976
1200
  name: "kirograph_secrets",
977
- description: "Scan for hardcoded secrets and credentials, enriched with call-graph blast radius showing which entry points reach the secret.",
1201
+ description: "Scan for hardcoded secrets with call-graph blast radius.",
978
1202
  inputSchema: {
979
1203
  type: "object",
980
1204
  properties: {
981
1205
  includeTests: { type: "boolean", description: "Include test files in scan", default: false },
982
1206
  severity: { type: "string", description: "Filter by severity: critical, high, medium, low", enum: ["critical", "high", "medium", "low"] },
983
- projectPath: { type: "string", description: "Project root path (optional)" }
1207
+ projectPath: { type: "string" }
984
1208
  }
985
1209
  }
986
1210
  },
987
1211
  {
988
1212
  name: "kirograph_security_flows",
989
- description: "SAST-lite: detect dangerous data flows (SQL injection, dangerous eval, unsafe deserialization, path traversal, weak crypto). Each finding tagged with OWASP Top 10 category.",
1213
+ description: "SAST-lite: detect SQL injection, eval, deserialize, path, crypto flows.",
990
1214
  inputSchema: {
991
1215
  type: "object",
992
1216
  properties: {
993
1217
  type: { type: "string", description: "Filter by type: sql, eval, deserialize, path, crypto, all", default: "all" },
994
- projectPath: { type: "string", description: "Project root path (optional)" }
1218
+ projectPath: { type: "string" }
995
1219
  }
996
1220
  }
997
1221
  },
998
1222
  {
999
1223
  name: "kirograph_supply_chain",
1000
- description: "Supply chain health: OpenSSF Scorecard scores, maintainer count, abandoned package detection for project dependencies.",
1224
+ description: "OpenSSF Scorecard scores, maintainer count, abandoned package detection.",
1001
1225
  inputSchema: {
1002
1226
  type: "object",
1003
1227
  properties: {
1004
1228
  threshold: { type: "string", description: "Minimum risk level: critical, high, medium", enum: ["critical", "high", "medium"] },
1005
1229
  refresh: { type: "boolean", description: "Re-fetch from APIs", default: false },
1006
- projectPath: { type: "string", description: "Project root path (optional)" }
1230
+ projectPath: { type: "string" }
1007
1231
  }
1008
1232
  }
1009
1233
  },
1010
1234
  {
1011
1235
  name: "kirograph_dep_confusion",
1012
- description: "Detect dependency confusion vulnerabilities: internal package names that exist in public registries (typosquatting/supply chain attack vectors).",
1236
+ description: "Detect dependency confusion: internal names in public registries.",
1013
1237
  inputSchema: {
1014
1238
  type: "object",
1015
1239
  properties: {
1016
- projectPath: { type: "string", description: "Project root path (optional)" }
1240
+ projectPath: { type: "string" }
1017
1241
  }
1018
1242
  }
1019
1243
  },
1020
1244
  {
1021
1245
  name: "kirograph_remediation",
1022
- description: "Remediation SLA tracking: which vulnerabilities are overdue for fixing based on severity thresholds (critical=7d, high=30d, medium=90d).",
1246
+ description: "Remediation SLA tracking: overdue vulns by severity thresholds.",
1023
1247
  inputSchema: {
1024
1248
  type: "object",
1025
1249
  properties: {
1026
1250
  overdueOnly: { type: "boolean", description: "Show only overdue items", default: false },
1027
- projectPath: { type: "string", description: "Project root path (optional)" }
1251
+ projectPath: { type: "string" }
1028
1252
  }
1029
1253
  }
1030
1254
  },
1031
1255
  // ── Pattern tools (require enablePatterns=true) ─────────────────────────────
1032
1256
  {
1033
1257
  name: "kirograph_pattern_coverage",
1034
- description: "OWASP Top 10 coverage report: shows which vulnerability categories are covered by bundled pattern rules and how many matches exist in each.",
1258
+ description: "OWASP Top 10 coverage report: categories covered and match counts.",
1035
1259
  inputSchema: {
1036
1260
  type: "object",
1037
1261
  properties: {
1038
- projectPath: { type: "string", description: "Project root path (optional)" }
1262
+ projectPath: { type: "string" }
1039
1263
  }
1040
1264
  }
1041
1265
  },
1042
1266
  {
1043
1267
  name: "kirograph_pattern_save_baseline",
1044
- description: "Save current pattern match counts as a baseline for future diffing.",
1268
+ description: "Save current pattern match counts as a baseline.",
1045
1269
  inputSchema: {
1046
1270
  type: "object",
1047
1271
  properties: {
1048
1272
  label: { type: "string", description: 'Baseline label (default: "default")', default: "default" },
1049
- projectPath: { type: "string", description: "Project root path (optional)" }
1273
+ projectPath: { type: "string" }
1050
1274
  }
1051
1275
  }
1052
1276
  },
1053
1277
  {
1054
1278
  name: "kirograph_pattern_diff",
1055
- description: "Diff current pattern matches against a saved baseline. Shows new findings, resolved findings, and unchanged counts.",
1279
+ description: "Diff pattern matches against a saved baseline.",
1056
1280
  inputSchema: {
1057
1281
  type: "object",
1058
1282
  properties: {
1059
1283
  label: { type: "string", description: 'Baseline label (default: "default")', default: "default" },
1060
- projectPath: { type: "string", description: "Project root path (optional)" }
1284
+ projectPath: { type: "string" }
1061
1285
  }
1062
1286
  }
1063
1287
  }
@@ -1072,7 +1296,7 @@ const LIVE_SEARCH_TOOL_DEFINITION = {
1072
1296
  pattern: { type: "string", description: 'ast-grep inline pattern (e.g. "eval($X)")' },
1073
1297
  language: { type: "string", description: "Language to search: javascript, typescript, python, go, rust, java, etc." },
1074
1298
  limit: { type: "number", description: "Max results (default 20, max 100)", default: 20 },
1075
- projectPath: { type: "string", description: "Project root path (optional)" }
1299
+ projectPath: { type: "string" }
1076
1300
  }
1077
1301
  }
1078
1302
  };
@@ -1298,6 +1522,52 @@ ${result.content}`;
1298
1522
  }
1299
1523
  return result.content;
1300
1524
  }
1525
+ if (toolName === "kirograph_retrieve") {
1526
+ const filePath = args.path;
1527
+ if (!filePath) return "Error: path is required.";
1528
+ const projectRoot = args.projectPath || process.cwd();
1529
+ const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath);
1530
+ const { getFileReadCache } = await Promise.resolve().then(() => require("./cache.js"));
1531
+ const cache = getFileReadCache();
1532
+ const cached = cache.getPreviousContent(resolvedPath);
1533
+ if (cached !== void 0) {
1534
+ return cached;
1535
+ }
1536
+ if (!fs.existsSync(resolvedPath)) {
1537
+ return `Error: File not found: ${resolvedPath}`;
1538
+ }
1539
+ const result = cache.read(resolvedPath, true);
1540
+ return result.content;
1541
+ }
1542
+ if (toolName === "kirograph_compress") {
1543
+ const text = args.text;
1544
+ if (!text) return "Error: text is required.";
1545
+ const rawLevel = args.level ?? "full";
1546
+ const command = args.command;
1547
+ const { estimateTokens: est } = await Promise.resolve().then(() => require("../compression/index.js"));
1548
+ const originalTokens = est(text);
1549
+ let compressed;
1550
+ let strategy;
1551
+ if (command) {
1552
+ const shellLevel = rawLevel === "ultra" ? "ultra" : rawLevel === "lite" || rawLevel === "normal" ? "normal" : "aggressive";
1553
+ const { compress: compress2 } = await Promise.resolve().then(() => require("../compression/index.js"));
1554
+ const result = compress2(command, text, { level: shellLevel });
1555
+ compressed = result.output;
1556
+ strategy = `rtk:${result.commandFamily}:${shellLevel}`;
1557
+ } else {
1558
+ const cavemanLevel = rawLevel === "normal" || rawLevel === "lite" ? "lite" : rawLevel === "aggressive" ? "full" : rawLevel;
1559
+ const { compressObservation } = await Promise.resolve().then(() => require("../memory/compress.js"));
1560
+ const result = compressObservation(text, cavemanLevel);
1561
+ compressed = result.compressed;
1562
+ strategy = `caveman:${cavemanLevel}`;
1563
+ }
1564
+ const compressedTokens = est(compressed);
1565
+ const savings = originalTokens > 0 ? Math.round((originalTokens - compressedTokens) / originalTokens * 100) : 0;
1566
+ const footer = savings > 5 ? `
1567
+
1568
+ [${savings}% tokens saved | ${originalTokens}\u2192${compressedTokens} | ${strategy}]` : "";
1569
+ return compressed + footer;
1570
+ }
1301
1571
  if (toolName === "kirograph_budget") {
1302
1572
  const projectRoot = args.projectPath || process.cwd();
1303
1573
  const reset = args.reset ?? false;
@@ -1340,9 +1610,10 @@ ${result.content}`;
1340
1610
  ).join("\n\n");
1341
1611
  }
1342
1612
  case "kirograph_context": {
1613
+ const detail = args.detail ?? (args.includeCode === false ? "summary" : "full");
1343
1614
  const ctx = await cg.buildContext(args.task, {
1344
1615
  maxNodes: args.maxNodes ?? 20,
1345
- includeCode: args.includeCode ?? true
1616
+ includeCode: detail === "full"
1346
1617
  });
1347
1618
  const lines = [ctx.summary, ""];
1348
1619
  if (ctx.entryPoints.length === 0) {
@@ -1351,8 +1622,11 @@ ${result.content}`;
1351
1622
  lines.push("## Entry Points");
1352
1623
  for (const n of ctx.entryPoints) {
1353
1624
  lines.push(`- ${mapKind(n.kind)} \`${n.name}\` \u2014 ${n.filePath}:${n.startLine}`);
1354
- if (ctx.codeSnippets.has(n.id)) {
1625
+ if (detail === "full" && ctx.codeSnippets.has(n.id)) {
1355
1626
  lines.push("```", ctx.codeSnippets.get(n.id), "```");
1627
+ } else if (detail === "signatures") {
1628
+ if (n.signature) lines.push(` Signature: ${n.signature}`);
1629
+ if (n.docstring) lines.push(` Docs: ${n.docstring}`);
1356
1630
  }
1357
1631
  }
1358
1632
  if (ctx.relatedNodes.length > 0) {
@@ -1385,8 +1659,7 @@ ${result.content}`;
1385
1659
  if (memResults.length > 0) {
1386
1660
  lines.push("", "## Related Memory");
1387
1661
  for (const r of memResults.slice(0, contextLimit)) {
1388
- const age = formatAge(r.observation.createdAt);
1389
- lines.push(`- [${r.observation.kind}] ${r.observation.content} (${age})`);
1662
+ lines.push(`- [${r.observation.kind}] ${r.observation.content}`);
1390
1663
  }
1391
1664
  }
1392
1665
  }
@@ -1624,14 +1897,17 @@ ${result.content}`;
1624
1897
  const results = cg.searchNodes(args.symbol, void 0, 5);
1625
1898
  if (results.length === 0) return `Symbol "${args.symbol}" not found in index.`;
1626
1899
  const node = results[0].node;
1900
+ const nodeDetail = args.detail ?? (args.includeCode ? "full" : "summary");
1627
1901
  const lines = [
1628
1902
  `${mapKind(node.kind)} \`${node.name}\``,
1629
1903
  `File: ${node.filePath}:${node.startLine}-${node.endLine}`,
1630
- `Qualified: ${node.qualifiedName}`,
1631
- node.signature ? `Signature: ${node.signature}` : "",
1632
- node.docstring ? `Docs: ${node.docstring}` : ""
1633
- ].filter(Boolean);
1634
- if (args.includeCode) {
1904
+ `Qualified: ${node.qualifiedName}`
1905
+ ];
1906
+ if (nodeDetail === "signatures" || nodeDetail === "full") {
1907
+ if (node.signature) lines.push(`Signature: ${node.signature}`);
1908
+ if (node.docstring) lines.push(`Docs: ${node.docstring}`);
1909
+ }
1910
+ if (nodeDetail === "full") {
1635
1911
  const src = cg.getNodeSource(node);
1636
1912
  if (src) lines.push("", "```", src, "```");
1637
1913
  }
@@ -2073,6 +2349,23 @@ ${result.content}`;
2073
2349
  }
2074
2350
  return lines.join("\n");
2075
2351
  }
2352
+ case "kirograph_snapshot_save": {
2353
+ const sm = cg.createSnapshotManager();
2354
+ const snapshot = sm.save(args.label);
2355
+ return `Snapshot saved: "${snapshot.label}" \u2014 ${snapshot.nodeCount} symbols, ${snapshot.edgeCount} edges`;
2356
+ }
2357
+ case "kirograph_snapshot_list": {
2358
+ const sm = cg.createSnapshotManager();
2359
+ const snapshots = sm.list();
2360
+ if (snapshots.length === 0) return "No snapshots yet. Use kirograph_snapshot_save to save one first.";
2361
+ const lines = [`Saved snapshots (${snapshots.length}):
2362
+ `];
2363
+ for (const s of snapshots) {
2364
+ const date = new Date(s.timestamp).toISOString().slice(0, 19).replace("T", " ");
2365
+ lines.push(`- ${s.label} (${date}, ${s.nodeCount} symbols, ${s.edgeCount} edges)`);
2366
+ }
2367
+ return lines.join("\n");
2368
+ }
2076
2369
  case "kirograph_flows": {
2077
2370
  const { getExecutionFlows, traceFlow, detectEntryPoints } = await Promise.resolve().then(() => require("../graph/flows.js"));
2078
2371
  const db = cg.getDatabase();
@@ -2419,6 +2712,102 @@ ${result.content}`;
2419
2712
  (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
2713
  ).join("\n") + "\n\nUse kirograph_mem_compare to establish relations.";
2421
2714
  }
2715
+ case "kirograph_mem_prune": {
2716
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2717
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2718
+ const config = await loadConfig(projectRoot);
2719
+ if (!config.enableMemory) return "Memory is not enabled.";
2720
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2721
+ const db = cg.getDatabase();
2722
+ db.applyMemorySchema();
2723
+ const mem = new MemoryManager(config, db.getRawDb());
2724
+ mem.initialize();
2725
+ const olderThan = args.olderThan ?? "90d";
2726
+ const durationMs = (() => {
2727
+ const m = olderThan.match(/^(\d+)(d|m|w|h)$/);
2728
+ if (!m) return 90 * 864e5;
2729
+ const v = parseInt(m[1]);
2730
+ switch (m[2]) {
2731
+ case "h":
2732
+ return v * 36e5;
2733
+ case "d":
2734
+ return v * 864e5;
2735
+ case "w":
2736
+ return v * 7 * 864e5;
2737
+ case "m":
2738
+ return v * 30 * 864e5;
2739
+ default:
2740
+ return 90 * 864e5;
2741
+ }
2742
+ })();
2743
+ const deleted = mem.prune(durationMs);
2744
+ return `Pruned ${deleted} observation(s) older than ${olderThan}.`;
2745
+ }
2746
+ case "kirograph_mem_lint": {
2747
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2748
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2749
+ const config = await loadConfig(projectRoot);
2750
+ if (!config.enableMemory) return "Memory is not enabled.";
2751
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2752
+ const db = cg.getDatabase();
2753
+ db.applyMemorySchema();
2754
+ const mem = new MemoryManager(config, db.getRawDb());
2755
+ mem.initialize();
2756
+ const result = mem.lint();
2757
+ const lines = ["Memory Lint Results:"];
2758
+ lines.push(` Stale links: ${result.staleLinks}`);
2759
+ lines.push(` Model mismatch: ${result.modelMismatch ? "yes" : "no"}`);
2760
+ lines.push(` Stale sessions: ${result.staleSessions} (auto-closed)`);
2761
+ if (args.fix && result.staleLinks > 0) {
2762
+ const removed = mem.removeStaleLinks();
2763
+ lines.push(`
2764
+ \u2713 Removed ${removed} stale link(s)`);
2765
+ } else if (result.staleLinks > 0) {
2766
+ lines.push(`
2767
+ Pass fix: true to remove stale links.`);
2768
+ }
2769
+ if (result.modelMismatch) {
2770
+ lines.push(`
2771
+ Run 'kirograph mem reembed' to fix model mismatch.`);
2772
+ }
2773
+ return lines.join("\n");
2774
+ }
2775
+ case "kirograph_mem_conflicts_list": {
2776
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2777
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2778
+ const config = await loadConfig(projectRoot);
2779
+ if (!config.enableMemory) return "Memory is not enabled.";
2780
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2781
+ const db = cg.getDatabase();
2782
+ db.applyMemorySchema();
2783
+ const mem = new MemoryManager(config, db.getRawDb());
2784
+ mem.initialize();
2785
+ const limit = clampLimit(args.limit, 20);
2786
+ const pending = mem.getPendingRelations(limit);
2787
+ if (pending.length === 0) return "No pending conflict relations.";
2788
+ return `Pending relations (${pending.length}):
2789
+
2790
+ ` + pending.map(
2791
+ (r) => `[${r.id}] ${r.relation}
2792
+ A: ${r.observationA}
2793
+ B: ${r.observationB}${r.reason ? `
2794
+ Reason: ${r.reason}` : ""}`
2795
+ ).join("\n\n") + "\n\nUse kirograph_mem_conflicts_ignore to dismiss, or kirograph_mem_judge to finalize.";
2796
+ }
2797
+ case "kirograph_mem_conflicts_ignore": {
2798
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
2799
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
2800
+ const config = await loadConfig(projectRoot);
2801
+ if (!config.enableMemory) return "Memory is not enabled.";
2802
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
2803
+ const db = cg.getDatabase();
2804
+ db.applyMemorySchema();
2805
+ const mem = new MemoryManager(config, db.getRawDb());
2806
+ mem.initialize();
2807
+ const relationId = args.relationId;
2808
+ mem.ignoreRelation(relationId);
2809
+ return `Relation "${relationId}" ignored.`;
2810
+ }
2422
2811
  // ── Docs tools ────────────────────────────────────────────────────────────
2423
2812
  case "kirograph_docs_toc": {
2424
2813
  const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
@@ -2772,6 +3161,138 @@ ${lines.join("\n")}`;
2772
3161
  return `Quality report for "${args.dataset}" (${quality.length} columns with issues):
2773
3162
 
2774
3163
  ${lines.join("\n")}`;
3164
+ }
3165
+ case "kirograph_data_drift": {
3166
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3167
+ const projectRoot = cg.getProjectRoot();
3168
+ const config = await loadConfig(projectRoot);
3169
+ if (!config.enableData) return "Data indexing is not enabled. Set enableData: true in .kirograph/config.json and run kirograph index.";
3170
+ const { DataQueries } = await Promise.resolve().then(() => require("../data/queries.js"));
3171
+ const db = cg.getDatabase();
3172
+ db.applyDataSchema();
3173
+ const dq = new DataQueries(db.getRawDb());
3174
+ const drift = dq.detectDrift(args.dataset);
3175
+ if (drift === null) return `Dataset "${args.dataset}" not found.`;
3176
+ if (!drift.hasDrift) {
3177
+ const rowDelta = drift.rowCountDelta !== 0 ? ` (rows: ${drift.rowCountDelta > 0 ? "+" : ""}${drift.rowCountDelta})` : "";
3178
+ return `No schema drift detected for "${args.dataset}".${rowDelta}`;
3179
+ }
3180
+ const driftLines = [`Schema drift detected for "${args.dataset}":`];
3181
+ if (drift.addedColumns?.length) driftLines.push(` Added columns: ${drift.addedColumns.join(", ")}`);
3182
+ if (drift.removedColumns?.length) driftLines.push(` Removed columns: ${drift.removedColumns.join(", ")}`);
3183
+ if (drift.typeChanges?.length) {
3184
+ for (const tc of drift.typeChanges) {
3185
+ driftLines.push(` Type change: ${tc.column} ${tc.from} \u2192 ${tc.to}`);
3186
+ }
3187
+ }
3188
+ if (drift.rowCountDelta !== 0) driftLines.push(` Row count delta: ${drift.rowCountDelta > 0 ? "+" : ""}${drift.rowCountDelta}`);
3189
+ return driftLines.join("\n");
3190
+ }
3191
+ case "kirograph_data_history": {
3192
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3193
+ const projectRoot = cg.getProjectRoot();
3194
+ const config = await loadConfig(projectRoot);
3195
+ if (!config.enableData) return "Data indexing is not enabled. Set enableData: true in .kirograph/config.json and run kirograph index.";
3196
+ const { DataQueries } = await Promise.resolve().then(() => require("../data/queries.js"));
3197
+ const db = cg.getDatabase();
3198
+ db.applyDataSchema();
3199
+ const dq = new DataQueries(db.getRawDb());
3200
+ const limit = Math.min(Number(args.limit ?? 10), 50);
3201
+ const history = dq.getHistory(args.dataset, limit);
3202
+ if (history === null) return `Dataset "${args.dataset}" not found.`;
3203
+ if (history.length === 0) return `No history snapshots for "${args.dataset}".`;
3204
+ const histLines = [`Schema history for "${args.dataset}" (${history.length} snapshot(s)):
3205
+ `];
3206
+ for (const snap of history) {
3207
+ const date = new Date(snap.snapshotAt).toISOString().replace("T", " ").slice(0, 19);
3208
+ const colNames = snap.columns.map((c) => c.name).join(", ");
3209
+ histLines.push(`${date} rows: ${snap.rowCount.toLocaleString()} cols: ${snap.columnCount} schema: ${colNames}`);
3210
+ }
3211
+ return histLines.join("\n");
3212
+ }
3213
+ // ── Watchmen tools (require enableMemory + enableWatchmen) ────────────────
3214
+ case "kirograph_watchmen_status": {
3215
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3216
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
3217
+ const config = await loadConfig(projectRoot);
3218
+ if (!config.enableMemory) return "Memory is not enabled. Set enableMemory: true in .kirograph/config.json";
3219
+ if (!config.enableWatchmen) return "Watchmen is not enabled. Set enableWatchmen: true in .kirograph/config.json";
3220
+ const { WatchmenChecker } = await Promise.resolve().then(() => require("../watchmen/index.js"));
3221
+ const { MemoryDatabase } = await Promise.resolve().then(() => require("../memory/database.js"));
3222
+ const db = cg.getDatabase();
3223
+ db.applyMemorySchema();
3224
+ const memDb = new MemoryDatabase(db.getRawDb());
3225
+ memDb.initialize();
3226
+ const checker = new WatchmenChecker(config.watchmenThreshold);
3227
+ const { ready, pendingCount } = checker.shouldSynthesize(memDb);
3228
+ const { targetFiles } = checker.buildReadyResponse("", pendingCount, projectRoot);
3229
+ const statusLines = [
3230
+ `Watchmen Status:`,
3231
+ ` Pending observations: ${pendingCount} / ${config.watchmenThreshold} threshold`,
3232
+ ` Ready to synthesize: ${ready ? "yes" : "no"}`,
3233
+ ` Target files:`
3234
+ ];
3235
+ for (const f of targetFiles) statusLines.push(` \xB7 ${f}`);
3236
+ return statusLines.join("\n");
3237
+ }
3238
+ case "kirograph_watchmen_synthesize": {
3239
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3240
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
3241
+ const config = await loadConfig(projectRoot);
3242
+ if (!config.enableMemory || !config.enableWatchmen) return "Watchmen is not enabled (enableMemory + enableWatchmen required).";
3243
+ if (config.watchmenSynthesisMode !== "local") return 'watchmenSynthesisMode is not "local" \u2014 use the agent hook instead.';
3244
+ const { WatchmenChecker } = await Promise.resolve().then(() => require("../watchmen/index.js"));
3245
+ const { MemoryDatabase } = await Promise.resolve().then(() => require("../memory/database.js"));
3246
+ const { runLocalSynthesis } = await Promise.resolve().then(() => require("../watchmen/synthesize.js"));
3247
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
3248
+ const db = cg.getDatabase();
3249
+ db.applyMemorySchema();
3250
+ const memDb = new MemoryDatabase(db.getRawDb());
3251
+ memDb.initialize();
3252
+ const checker = new WatchmenChecker(config.watchmenThreshold);
3253
+ const { ready, pendingCount } = checker.shouldSynthesize(memDb);
3254
+ if (!ready && !args.force) {
3255
+ return `Watchmen: ${pendingCount}/${config.watchmenThreshold} observations \u2014 threshold not reached. Pass force: true to run anyway.`;
3256
+ }
3257
+ const observations = memDb.getObservationsSinceLastSummary(50);
3258
+ if (observations.length === 0) return "No observations to synthesize.";
3259
+ const readyResult = checker.buildReadyResponse("", pendingCount, projectRoot);
3260
+ const result = await runLocalSynthesis(observations, readyResult, config.watchmenLocalModel, projectRoot, true);
3261
+ const mgr = new MemoryManager(config, db.getRawDb(), projectRoot);
3262
+ mgr.initialize();
3263
+ await mgr.store({ content: result.summaryObservation, kind: "summary", source: "agent" });
3264
+ const synthLines = [`Watchmen synthesis complete.`];
3265
+ if (result.filesWritten.length) synthLines.push(`Brief written to: ${result.filesWritten.join(", ")}`);
3266
+ if (result.skillsWritten.length) synthLines.push(`Skills written: ${result.skillsWritten.join(", ")}`);
3267
+ if (result.skillsPruned.length) synthLines.push(`Pruned stale: ${result.skillsPruned.join(", ")}`);
3268
+ return synthLines.join("\n");
3269
+ }
3270
+ case "kirograph_watchmen_reset": {
3271
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
3272
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
3273
+ const config = await loadConfig(projectRoot);
3274
+ if (!config.enableMemory) return "Memory is not enabled.";
3275
+ if (!config.enableWatchmen) return "Watchmen is not enabled.";
3276
+ const { MemoryManager } = await Promise.resolve().then(() => require("../memory/index.js"));
3277
+ const db = cg.getDatabase();
3278
+ db.applyMemorySchema();
3279
+ const mgr = new MemoryManager(config, db.getRawDb(), projectRoot);
3280
+ mgr.initialize();
3281
+ const id = await mgr.store({ content: `Watchmen counter reset via MCP at ${(/* @__PURE__ */ new Date()).toISOString()}.`, kind: "summary", source: "manual" });
3282
+ return id ? "Watchmen counter reset \u2014 pending observations set back to 0." : "Already at zero \u2014 nothing to reset.";
3283
+ }
3284
+ // ── Affected tests ────────────────────────────────────────────────────────
3285
+ case "kirograph_affected": {
3286
+ const files = args.files ?? [];
3287
+ if (files.length === 0) return 'No files provided. Pass file paths in the "files" array.';
3288
+ const affected = cg.getAffectedTests(files, {
3289
+ depth: args.depth ?? 5,
3290
+ testPattern: args.testPattern
3291
+ });
3292
+ if (affected.length === 0) return `No affected test files found for the provided ${files.length} changed file(s).`;
3293
+ return `Affected test files (${affected.length}) for ${files.length} changed file(s):
3294
+
3295
+ ` + affected.map((f) => `- ${f}`).join("\n");
2775
3296
  }
2776
3297
  // ── Security tools (require enableSecurity=true) ──────────────────────────
2777
3298
  case "kirograph_security": {
@@ -3840,6 +4361,77 @@ Produce WIKI_DIFF blocks following SCHEMA.md, then call kirograph_wiki_apply_dif
3840
4361
  }
3841
4362
  return lines.join("\n");
3842
4363
  }
4364
+ case "kirograph_wiki_synthesize": {
4365
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
4366
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
4367
+ const config = await loadConfig(projectRoot);
4368
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
4369
+ if (config.wikiSynthesisMode !== "local") return 'wikiSynthesisMode is not "local". This tool is for local-model synthesis only.';
4370
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
4371
+ const db = cg.getDatabase();
4372
+ db.applyWikiSchema();
4373
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph", {
4374
+ autoResolveConflicts: config.wikiAutoResolveConflicts ?? false
4375
+ });
4376
+ wiki.initialize();
4377
+ const queueCount = wiki.getQueueCount();
4378
+ if (queueCount === 0) return "Wiki queue is empty \u2014 nothing to synthesize.";
4379
+ const result = await wiki.synthesize(config.wikiLocalModel, true);
4380
+ const synthLines = [`Wiki synthesis complete \u2014 processed ${result.processed} source(s).`];
4381
+ if (result.created.length) synthLines.push(`Created: ${result.created.join(", ")}`);
4382
+ if (result.updated.length) synthLines.push(`Updated: ${result.updated.join(", ")}`);
4383
+ if (result.errors.length) synthLines.push(`Errors: ${result.errors.join("; ")}`);
4384
+ return synthLines.join("\n");
4385
+ }
4386
+ case "kirograph_wiki_init": {
4387
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
4388
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
4389
+ const config = await loadConfig(projectRoot);
4390
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
4391
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
4392
+ const db = cg.getDatabase();
4393
+ db.applyWikiSchema();
4394
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph", {
4395
+ autoResolveConflicts: config.wikiAutoResolveConflicts ?? false
4396
+ });
4397
+ wiki.initWiki();
4398
+ return "Wiki initialized. SCHEMA.md and MANIFEST.md created in .kirograph/wiki/.";
4399
+ }
4400
+ case "kirograph_wiki_reindex": {
4401
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
4402
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
4403
+ const config = await loadConfig(projectRoot);
4404
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
4405
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
4406
+ const db = cg.getDatabase();
4407
+ db.applyWikiSchema();
4408
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
4409
+ wiki.initialize();
4410
+ const count = wiki.reindex();
4411
+ return `Reindexed ${count} wiki page(s) from .kirograph/wiki/*.md.`;
4412
+ }
4413
+ case "kirograph_wiki_status": {
4414
+ const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
4415
+ const projectRoot = args.projectPath ?? cg.getProjectRoot();
4416
+ const config = await loadConfig(projectRoot);
4417
+ if (!config.enableWiki) return "Wiki is not enabled. Set enableWiki: true in .kirograph/config.json";
4418
+ const { KiroGraphWiki } = await Promise.resolve().then(() => require("../wiki/index.js"));
4419
+ const db = cg.getDatabase();
4420
+ db.applyWikiSchema();
4421
+ const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + "/.kirograph");
4422
+ wiki.initialize();
4423
+ const stats = wiki.getStats();
4424
+ const oldest = stats.oldestPage ? new Date(stats.oldestPage).toISOString().slice(0, 10) : "n/a";
4425
+ const newest = stats.newestPage ? new Date(stats.newestPage).toISOString().slice(0, 10) : "n/a";
4426
+ return [
4427
+ "Wiki Status:",
4428
+ ` Pages: ${stats.pageCount}`,
4429
+ ` Total sources: ${stats.totalSources}`,
4430
+ ` Oldest page: ${oldest}`,
4431
+ ` Newest page: ${newest}`,
4432
+ ` Wiki dir: .kirograph/wiki/`
4433
+ ].join("\n");
4434
+ }
3843
4435
  default:
3844
4436
  return `Unknown tool: ${toolName}`;
3845
4437
  }