@plumpslabs/kuma 2.3.29 → 2.3.31

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.
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ __export(pathValidator_exports, {
19
19
  getProjectRoot: () => getProjectRoot,
20
20
  isReadable: () => isReadable,
21
21
  isWritable: () => isWritable,
22
+ normalizeNodeId: () => normalizeNodeId,
23
+ normalizeScope: () => normalizeScope,
22
24
  validateFileExtension: () => validateFileExtension,
23
25
  validateFilePath: () => validateFilePath
24
26
  });
@@ -192,6 +194,23 @@ function getKumaDir() {
192
194
  function getKumaBackupsDir() {
193
195
  return path.join(getKumaDir(), "backups");
194
196
  }
197
+ function normalizeScope(raw, projectRoot) {
198
+ if (!raw || !raw.trim()) return null;
199
+ const root = projectRoot ?? getProjectRoot();
200
+ let scope = raw.trim();
201
+ const lineMatch = scope.match(/^(.+):(\d+)$/);
202
+ if (lineMatch) scope = lineMatch[1];
203
+ scope = scope.replace(/\\/g, "/");
204
+ if (!scope.includes("/") && !scope.includes(".")) return scope;
205
+ const resolved = path.resolve(root, scope);
206
+ const relative = path.relative(root, resolved);
207
+ if (relative.startsWith("..")) return scope.replace(/^\.\//, "");
208
+ return relative.replace(/\\/g, "/");
209
+ }
210
+ function normalizeNodeId(type, rawName, projectRoot) {
211
+ const normalized = normalizeScope(rawName, projectRoot);
212
+ return normalized ? `${type}::${normalized}` : `${type}::${rawName}`;
213
+ }
195
214
  var init_pathValidator = __esm({
196
215
  "src/utils/pathValidator.ts"() {
197
216
  "use strict";
@@ -207,6 +226,7 @@ __export(kumaDb_exports, {
207
226
  checkPortability: () => checkPortability,
208
227
  ensureGitignore: () => ensureGitignore,
209
228
  flushDb: () => flushDb,
229
+ generateNodeId: () => generateNodeId,
210
230
  getBenchmarkDiff: () => getBenchmarkDiff,
211
231
  getChanges: () => getChanges,
212
232
  getDb: () => getDb,
@@ -218,6 +238,7 @@ __export(kumaDb_exports, {
218
238
  listDecisionLog: () => listDecisionLog,
219
239
  listResearchCache: () => listResearchCache,
220
240
  listTodos: () => listTodos,
241
+ rebuildFtsIndex: () => rebuildFtsIndex,
221
242
  recordChange: () => recordChange,
222
243
  recordDecisionLog: () => recordDecisionLog,
223
244
  resetDbInstance: () => resetDbInstance,
@@ -236,6 +257,11 @@ __export(kumaDb_exports, {
236
257
  import initSqlJs from "sql.js";
237
258
  import fs2 from "fs";
238
259
  import path2 from "path";
260
+ import crypto from "crypto";
261
+ function generateNodeId(type, name) {
262
+ const uuid = crypto.randomUUID().slice(0, 8);
263
+ return `${type}::${uuid}::${name}`;
264
+ }
239
265
  function resetDbInstance() {
240
266
  dbInstance = null;
241
267
  initPromise = null;
@@ -304,9 +330,26 @@ function createSchema(db) {
304
330
  name TEXT NOT NULL,
305
331
  file_path TEXT,
306
332
  metadata TEXT DEFAULT '{}',
333
+ severity TEXT DEFAULT 'medium',
334
+ confidence REAL DEFAULT 0.8,
335
+ last_verified_at INTEGER,
307
336
  created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
308
337
  updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
309
338
  )`);
339
+ try {
340
+ const schema = db.exec(`SELECT sql FROM sqlite_master WHERE type='table' AND name='nodes'`);
341
+ const nodesSql = schema[0]?.values?.[0]?.[0] || "";
342
+ if (!nodesSql.includes("severity")) {
343
+ db.run(`ALTER TABLE nodes ADD COLUMN severity TEXT DEFAULT 'medium'`);
344
+ }
345
+ if (!nodesSql.includes("confidence")) {
346
+ db.run(`ALTER TABLE nodes ADD COLUMN confidence REAL DEFAULT 0.8`);
347
+ }
348
+ if (!nodesSql.includes("last_verified_at")) {
349
+ db.run(`ALTER TABLE nodes ADD COLUMN last_verified_at INTEGER`);
350
+ }
351
+ } catch {
352
+ }
310
353
  db.run(`CREATE TABLE IF NOT EXISTS edges (
311
354
  id INTEGER PRIMARY KEY AUTOINCREMENT,
312
355
  source_id TEXT NOT NULL REFERENCES nodes(id),
@@ -1304,6 +1347,14 @@ async function getLatestVerifications(limit = 10) {
1304
1347
  return [];
1305
1348
  }
1306
1349
  }
1350
+ function rebuildFtsIndex() {
1351
+ try {
1352
+ const db = dbInstance;
1353
+ if (!db) return;
1354
+ db.run(`INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')`);
1355
+ } catch {
1356
+ }
1357
+ }
1307
1358
  var DB_FILENAME, dbInstance, initPromise, _saveTimeout, _pendingDb, SAVE_DEBOUNCE_MS;
1308
1359
  var init_kumaDb = __esm({
1309
1360
  "src/engine/kumaDb.ts"() {
@@ -1928,26 +1979,31 @@ No unresolved issues.`;
1928
1979
  }
1929
1980
  detectLoop() {
1930
1981
  this.ensureInit();
1931
- const recentCalls = this.state.toolCalls.slice(-10);
1932
- if (recentCalls.length < 6) return { isLooping: false };
1982
+ const recentCalls = this.state.toolCalls.slice(-20);
1983
+ if (recentCalls.length < 12) return { isLooping: false };
1984
+ const CLEANUP_TOOLS = /* @__PURE__ */ new Set(["kuma_memory_delete", "kuma_memory", "delete_node"]);
1933
1985
  const toolCounts = /* @__PURE__ */ new Map();
1934
1986
  for (const call of recentCalls) {
1935
1987
  const existing = toolCounts.get(call.toolName);
1936
1988
  const action = call.params.action || "";
1989
+ const paramKey = JSON.stringify({ action, scope: call.params.scope, target: call.params.target });
1937
1990
  if (existing) {
1938
1991
  existing.count++;
1939
1992
  existing.actions.add(action);
1993
+ existing.params.add(paramKey);
1940
1994
  } else {
1941
- toolCounts.set(call.toolName, { count: 1, actions: /* @__PURE__ */ new Set([action]) });
1995
+ toolCounts.set(call.toolName, { count: 1, actions: /* @__PURE__ */ new Set([action]), params: /* @__PURE__ */ new Set([paramKey]) });
1942
1996
  }
1943
1997
  }
1944
- for (const [toolName, { count, actions }] of toolCounts) {
1945
- if (count >= 4) {
1998
+ for (const [toolName, { count, actions, params }] of toolCounts) {
1999
+ if (count >= 10) {
2000
+ if (CLEANUP_TOOLS.has(toolName)) continue;
2001
+ if (params.size >= 5) continue;
1946
2002
  if (actions.size >= 3) continue;
1947
2003
  return {
1948
2004
  isLooping: true,
1949
2005
  toolName,
1950
- message: `Detected potential loop: "${toolName}" called ${count} times in last ${recentCalls.length} tool calls. Consider a different approach.`
2006
+ message: `Detected potential runaway loop: "${toolName}" called ${count} times in last ${recentCalls.length} tool calls with no variation. Consider a different approach.`
1951
2007
  };
1952
2008
  }
1953
2009
  }
@@ -2097,10 +2153,19 @@ No unresolved issues.`;
2097
2153
  });
2098
2154
 
2099
2155
  // src/engine/kumaSelfHeal.ts
2156
+ var kumaSelfHeal_exports = {};
2157
+ __export(kumaSelfHeal_exports, {
2158
+ autoHeal: () => autoHeal,
2159
+ detectStaleNodes: () => detectStaleNodes,
2160
+ formatGotchaStalenessReport: () => formatGotchaStalenessReport,
2161
+ formatHealReport: () => formatHealReport,
2162
+ healOnQuery: () => healOnQuery,
2163
+ verifyGotchaStaleness: () => verifyGotchaStaleness
2164
+ });
2100
2165
  import { execSync as execSync2 } from "child_process";
2101
2166
  import fs4 from "fs";
2102
2167
  import path4 from "path";
2103
- import crypto from "crypto";
2168
+ import crypto2 from "crypto";
2104
2169
  function contentFingerprint(filePath) {
2105
2170
  try {
2106
2171
  const fullPath = path4.join(getProjectRoot(), filePath);
@@ -2118,7 +2183,7 @@ function contentFingerprint(filePath) {
2118
2183
  tail = Buffer.alloc(1024);
2119
2184
  fs4.readSync(fd, tail, 0, 1024, size - 1024);
2120
2185
  }
2121
- const hash = crypto.createHash("md5");
2186
+ const hash = crypto2.createHash("md5");
2122
2187
  hash.update(head);
2123
2188
  hash.update(tail);
2124
2189
  hash.update(String(size));
@@ -2191,6 +2256,11 @@ async function detectStaleNodes() {
2191
2256
  issue: contentMatch ? "path-changed" : "file-missing"
2192
2257
  });
2193
2258
  }
2259
+ } else {
2260
+ try {
2261
+ db.run(`UPDATE nodes SET last_verified_at = strftime('%s','now') WHERE id = ?`, [row.id]);
2262
+ } catch {
2263
+ }
2194
2264
  }
2195
2265
  }
2196
2266
  stmt.free();
@@ -2200,8 +2270,9 @@ async function detectStaleNodes() {
2200
2270
  return stale;
2201
2271
  }
2202
2272
  function findRenamedPath(oldPath) {
2273
+ const root = getProjectRoot();
2274
+ const basename = path4.basename(oldPath);
2203
2275
  try {
2204
- const root = getProjectRoot();
2205
2276
  const output = execSync2(
2206
2277
  `git log --follow --diff-filter=R --name-only --format="" -1 -- "${oldPath}"`,
2207
2278
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 5e3 }
@@ -2210,10 +2281,23 @@ function findRenamedPath(oldPath) {
2210
2281
  const lines = output.split("\n").filter(Boolean);
2211
2282
  return lines[lines.length - 1] || null;
2212
2283
  }
2213
- return null;
2214
2284
  } catch {
2215
- return null;
2216
2285
  }
2286
+ try {
2287
+ const result = execSync2(
2288
+ `find . -name "${basename}" -type f -not -path "./node_modules/*" -not -path "./.git/*" -not -path "./dist/*" 2>/dev/null | head -5`,
2289
+ { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 5e3 }
2290
+ ).trim();
2291
+ if (result) {
2292
+ const candidates = result.split("\n").filter(Boolean);
2293
+ for (const c of candidates) {
2294
+ const relative = c.startsWith("./") ? c.slice(2) : c;
2295
+ if (relative !== oldPath) return relative;
2296
+ }
2297
+ }
2298
+ } catch {
2299
+ }
2300
+ return null;
2217
2301
  }
2218
2302
  async function cascadeStaleEdges(nodeIds) {
2219
2303
  try {
@@ -2254,12 +2338,26 @@ async function healStaleNode(entry) {
2254
2338
  saveDb();
2255
2339
  return true;
2256
2340
  }
2257
- db.run(
2258
- `UPDATE nodes SET metadata = json_set(COALESCE(NULLIF(metadata,''), '{}'), '$.stale', 1) WHERE id = ?`,
2259
- [entry.nodeId]
2260
- );
2341
+ if (entry.type === "gotcha") {
2342
+ db.run("DELETE FROM edges WHERE source_id = ? OR target_id = ?", [entry.nodeId, entry.nodeId]);
2343
+ db.run("DELETE FROM nodes WHERE id = ?", [entry.nodeId]);
2344
+ const parts = entry.nodeId.split("::");
2345
+ if (parts.length >= 3) {
2346
+ db.run("DELETE FROM known_gotchas WHERE file_path = ? AND description LIKE ?", [parts[1], `${parts.slice(2).join("::")}%`]);
2347
+ }
2348
+ } else if (entry.type === "research") {
2349
+ db.run(
2350
+ `UPDATE nodes SET metadata = json_set(COALESCE(NULLIF(metadata,''), '{}'), '$.stale', 1, '$.stale_reason', 'file_missing') WHERE id = ?`,
2351
+ [entry.nodeId]
2352
+ );
2353
+ } else {
2354
+ db.run(
2355
+ `UPDATE nodes SET metadata = json_set(COALESCE(NULLIF(metadata,''), '{}'), '$.stale', 1, '$.stale_reason', 'file_missing') WHERE id = ?`,
2356
+ [entry.nodeId]
2357
+ );
2358
+ }
2261
2359
  saveDb();
2262
- return false;
2360
+ return entry.type === "gotcha";
2263
2361
  } catch (err) {
2264
2362
  console.error(`[KumaSelfHeal] Failed to heal node ${entry.nodeId}: ${err}`);
2265
2363
  return false;
@@ -2269,14 +2367,19 @@ async function autoHeal() {
2269
2367
  const stale = await detectStaleNodes();
2270
2368
  let healed = 0;
2271
2369
  let missing = 0;
2370
+ let removed = 0;
2272
2371
  for (const entry of stale) {
2273
2372
  const success = await healStaleNode(entry);
2274
- if (success) healed++;
2275
- else missing++;
2373
+ if (success) {
2374
+ healed++;
2375
+ removed++;
2376
+ } else {
2377
+ missing++;
2378
+ }
2276
2379
  }
2277
- const staleNodeIds = stale.filter((e) => !e.newPath).map((e) => e.nodeId);
2380
+ const staleNodeIds = stale.filter((e) => !e.newPath && e.type !== "gotcha").map((e) => e.nodeId);
2278
2381
  const cascadedEdges = await cascadeStaleEdges(staleNodeIds);
2279
- return { healed, missing, total: stale.length, cascadedEdges };
2382
+ return { healed, missing, total: stale.length, cascadedEdges, removed };
2280
2383
  }
2281
2384
  async function healOnQuery(filePaths) {
2282
2385
  if (filePaths.length === 0) return { healed: 0 };
@@ -2315,6 +2418,7 @@ function formatHealReport(result) {
2315
2418
  "",
2316
2419
  `\u{1F4CA} ${result.total} stale entr${result.total > 1 ? "ies" : "y"} found`,
2317
2420
  `\u2705 ${result.healed} healed (file renamed \u2192 path updated)`,
2421
+ result.removed > 0 ? `\u{1F5D1}\uFE0F ${result.removed} removed (obsolete gotcha referencing deleted file)` : "",
2318
2422
  `\u274C ${result.missing} missing (file deleted \u2014 marked as stale)`,
2319
2423
  result.cascadedEdges > 0 ? `\u{1F517} ${result.cascadedEdges} cascade edges updated` : "",
2320
2424
  ""
@@ -2322,12 +2426,81 @@ function formatHealReport(result) {
2322
2426
  if (result.healed > 0) {
2323
2427
  lines.push("\u{1F4A1} Healed entries had their file paths updated via git rename detection or content hash matching.");
2324
2428
  }
2429
+ if (result.removed > 0) {
2430
+ lines.push("\u{1F5D1}\uFE0F Obsolete gotchas (referencing deleted files) were removed from both graph and table.");
2431
+ }
2325
2432
  if (result.missing > 0) {
2326
2433
  lines.push("\u26A0\uFE0F Missing entries were marked as stale. The Knowledge Graph will deprioritize them.");
2327
- lines.push("\u{1F4A1} Run incremental healing after making changes to keep the graph fresh.");
2328
2434
  }
2329
2435
  return lines.join("\n");
2330
2436
  }
2437
+ async function verifyGotchaStaleness() {
2438
+ const db = await getDb();
2439
+ const stale = [];
2440
+ try {
2441
+ const rows = db.exec(`
2442
+ SELECT id, metadata FROM nodes WHERE type = 'gotcha'
2443
+ `);
2444
+ if (rows.length === 0) return stale;
2445
+ const columns = rows[0].columns;
2446
+ const filePathIdx = columns.indexOf("metadata");
2447
+ for (const row of rows[0].values) {
2448
+ const nodeId2 = row[0];
2449
+ let metadata = {};
2450
+ try {
2451
+ metadata = JSON.parse(row[filePathIdx]);
2452
+ } catch {
2453
+ }
2454
+ const filePath = metadata.file_path;
2455
+ if (!filePath || filePath.startsWith("search::") || filePath.startsWith("api_route::")) continue;
2456
+ const fullPath = path4.join(getProjectRoot(), filePath);
2457
+ if (!fs4.existsSync(fullPath)) {
2458
+ stale.push({ gotchaId: nodeId2, file_path: filePath, issue: "file_missing" });
2459
+ } else {
2460
+ const desc = metadata.description || "";
2461
+ const symbolMatch = desc.match(/\b(function|class|method|const|let|var|export)\s+(\w+)/i);
2462
+ if (symbolMatch) {
2463
+ const symbolName = symbolMatch[2];
2464
+ try {
2465
+ const content = fs4.readFileSync(fullPath, "utf-8");
2466
+ if (!content.includes(symbolName)) {
2467
+ stale.push({ gotchaId: nodeId2, file_path: filePath, issue: "symbol_missing" });
2468
+ }
2469
+ } catch {
2470
+ }
2471
+ }
2472
+ }
2473
+ }
2474
+ } catch {
2475
+ }
2476
+ return stale;
2477
+ }
2478
+ function formatGotchaStalenessReport(stale) {
2479
+ if (stale.length === 0) {
2480
+ return "\u2705 **Gotcha Staleness Check** \u2014 All gotcha references are valid.";
2481
+ }
2482
+ const fileMissing = stale.filter((s) => s.issue === "file_missing");
2483
+ const symbolMissing = stale.filter((s) => s.issue === "symbol_missing");
2484
+ const lines = [
2485
+ `\u26A0\uFE0F **Gotcha Staleness Report**`,
2486
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
2487
+ "",
2488
+ `\u{1F4CA} ${stale.length} stale gotcha${stale.length > 1 ? "s" : ""} found`,
2489
+ fileMissing.length > 0 ? ` \u{1F5D1}\uFE0F ${fileMissing.length} reference${fileMissing.length > 1 ? "s" : ""} deleted file` : "",
2490
+ symbolMissing.length > 0 ? ` \u{1F50D} ${symbolMissing.length} reference${symbolMissing.length > 1 ? "s" : ""} missing symbol` : "",
2491
+ ""
2492
+ ];
2493
+ for (const s of fileMissing.slice(0, 5)) {
2494
+ lines.push(` \u{1F5D1}\uFE0F \`${s.gotchaId}\` \u2192 file missing: \`${s.file_path}\``);
2495
+ }
2496
+ for (const s of symbolMissing.slice(0, 5)) {
2497
+ lines.push(` \u{1F50D} \`${s.gotchaId}\` \u2192 symbol missing in \`${s.file_path}\``);
2498
+ }
2499
+ if (stale.length > 5) lines.push(` ... and ${stale.length - 5} more`);
2500
+ lines.push("");
2501
+ lines.push("\u{1F4A1} Use `kuma_memory({ action: 'delete_node', target: '<gotchaId>' })` to remove obsolete gotchas.");
2502
+ return lines.join("\n");
2503
+ }
2331
2504
  var init_kumaSelfHeal = __esm({
2332
2505
  "src/engine/kumaSelfHeal.ts"() {
2333
2506
  "use strict";
@@ -2363,7 +2536,10 @@ __export(kumaGraph_exports, {
2363
2536
  upsertNode: () => upsertNode
2364
2537
  });
2365
2538
  function nodeId(type, name) {
2366
- return `${type}::${name}`;
2539
+ if (type === "file") {
2540
+ return `file::${name}`;
2541
+ }
2542
+ return generateNodeId(type, name);
2367
2543
  }
2368
2544
  async function pruneGraphIfNeeded() {
2369
2545
  try {
@@ -2424,16 +2600,20 @@ async function upsertNode(node) {
2424
2600
  const db = await getDb();
2425
2601
  const id = node.id || nodeId(node.type, node.name);
2426
2602
  const metadata = JSON.stringify(node.metadata ?? {});
2603
+ const severity = node.metadata?.severity || "medium";
2604
+ const confidence = node.metadata?.confidence || 0.8;
2427
2605
  db.run(`
2428
- INSERT INTO nodes (id, type, name, file_path, metadata, updated_at)
2429
- VALUES (?, ?, ?, ?, ?, strftime('%s','now'))
2606
+ INSERT INTO nodes (id, type, name, file_path, metadata, severity, confidence, updated_at)
2607
+ VALUES (?, ?, ?, ?, ?, ?, ?, strftime('%s','now'))
2430
2608
  ON CONFLICT(id) DO UPDATE SET
2431
2609
  type = excluded.type,
2432
2610
  name = excluded.name,
2433
2611
  file_path = COALESCE(excluded.file_path, nodes.file_path),
2434
2612
  metadata = excluded.metadata,
2613
+ severity = excluded.severity,
2614
+ confidence = excluded.confidence,
2435
2615
  updated_at = strftime('%s','now')
2436
- `, [id, node.type, node.name, node.filePath || null, metadata]);
2616
+ `, [id, node.type, node.name, node.filePath || null, metadata, severity, confidence]);
2437
2617
  try {
2438
2618
  db.run(`INSERT INTO nodes_fts (rowid, name, metadata) VALUES (last_insert_rowid(), ?, ?)`, [node.name, metadata]);
2439
2619
  } catch {
@@ -3087,14 +3267,7 @@ async function searchGraph(query, limit = 20) {
3087
3267
  }
3088
3268
  stmt2.free();
3089
3269
  if (results2.length > 0) {
3090
- const lines2 = [
3091
- `\u{1F50D} **Graph Search** \u2014 ${results2.length} result(s) for "${query}"`,
3092
- ""
3093
- ];
3094
- for (const r of results2) {
3095
- lines2.push(` \u2022 **${r.name}** (${r.type})${r.file_path ? ` \u2014 ${r.file_path}` : ""}`);
3096
- }
3097
- return lines2.join("\n");
3270
+ return formatSearchResults(results2, query, db);
3098
3271
  }
3099
3272
  } catch {
3100
3273
  }
@@ -3113,18 +3286,72 @@ async function searchGraph(query, limit = 20) {
3113
3286
  if (results.length === 0) {
3114
3287
  return await codebaseSearchFallback(query, limit);
3115
3288
  }
3116
- const lines = [
3117
- `\u{1F50D} **Graph Search** \u2014 ${results.length} result(s) for "${query}"`,
3118
- ""
3119
- ];
3120
- for (const r of results) {
3121
- lines.push(` \u2022 **${r.name}** (${r.type})${r.file_path ? ` \u2014 ${r.file_path}` : ""}`);
3122
- }
3123
- return lines.join("\n");
3289
+ return formatSearchResults(results, query, db);
3124
3290
  } catch (err) {
3125
3291
  return `Error searching graph: ${err}`;
3126
3292
  }
3127
3293
  }
3294
+ function formatSearchResults(results, query, db) {
3295
+ const lines = [
3296
+ `\u{1F50D} **Graph Search** \u2014 ${results.length} result(s) for "${query}"`,
3297
+ ""
3298
+ ];
3299
+ const topResults = results.slice(0, 10);
3300
+ for (const r of topResults) {
3301
+ const nodeType = r.type;
3302
+ const icon = nodeType === "gotcha" ? "\u26A0\uFE0F" : nodeType === "decision" ? "\u{1F4CC}" : nodeType === "arch_flow" ? "\u{1F3DB}\uFE0F" : nodeType === "flow_explanation" ? "\u{1F4DD}" : nodeType === "research" ? "\u{1F52C}" : "\u{1F4C4}";
3303
+ lines.push(`${icon} **${r.name}** (${r.type})${r.file_path ? ` \u2014 ${r.file_path}` : ""}`);
3304
+ try {
3305
+ const stmtEdges = db.prepare(`
3306
+ SELECT n2.id, n2.type, n2.name, e.type as edge_type
3307
+ FROM edges e
3308
+ JOIN nodes n2 ON n2.id = e.target_id
3309
+ WHERE e.source_id = ?
3310
+ LIMIT 5
3311
+ `);
3312
+ stmtEdges.bind([r.id]);
3313
+ const connected = [];
3314
+ while (stmtEdges.step()) {
3315
+ connected.push(stmtEdges.getAsObject());
3316
+ }
3317
+ stmtEdges.free();
3318
+ if (connected.length > 0) {
3319
+ lines.push(` \u21B3 connected to: ${connected.map((c) => `${c.name} (${c.type})`).join(", ")}`);
3320
+ }
3321
+ } catch {
3322
+ }
3323
+ if (nodeType === "feature_domain" || nodeType === "arch_flow") {
3324
+ try {
3325
+ const domainName = r.name.replace("feature_domain::", "").replace("arch_flow::", "");
3326
+ const stmtProse = db.prepare(`
3327
+ SELECT metadata FROM nodes WHERE type = 'flow_explanation' AND name LIKE ?
3328
+ LIMIT 1
3329
+ `);
3330
+ stmtProse.bind([`%${domainName}%`]);
3331
+ if (stmtProse.step()) {
3332
+ const metaRow = stmtProse.getAsObject();
3333
+ stmtProse.free();
3334
+ let meta = {};
3335
+ try {
3336
+ meta = JSON.parse(metaRow.metadata);
3337
+ } catch {
3338
+ }
3339
+ if (meta.prose) {
3340
+ lines.push(` \u{1F4DD} ${meta.prose}`);
3341
+ }
3342
+ } else {
3343
+ stmtProse.free();
3344
+ }
3345
+ } catch {
3346
+ }
3347
+ }
3348
+ lines.push("");
3349
+ }
3350
+ if (results.length > 10) {
3351
+ lines.push(`... and ${results.length - 10} more results`);
3352
+ }
3353
+ return lines.join("\n");
3354
+ }
3128
3355
  async function codebaseSearchFallback(query, limit = 20) {
3129
3356
  try {
3130
3357
  const fg = (await import("fast-glob")).default;
@@ -5323,14 +5550,14 @@ __export(kumaDriftDetector_exports, {
5323
5550
  });
5324
5551
  import fs9 from "fs";
5325
5552
  import path9 from "path";
5326
- import crypto2 from "crypto";
5553
+ import crypto3 from "crypto";
5327
5554
  function hashFile(filePath) {
5328
5555
  try {
5329
5556
  const root = getProjectRoot();
5330
5557
  const fullPath = path9.resolve(root, filePath);
5331
5558
  if (!fs9.existsSync(fullPath)) return null;
5332
5559
  const content = fs9.readFileSync(fullPath, "utf-8");
5333
- return crypto2.createHash("sha256").update(content).digest("hex");
5560
+ return crypto3.createHash("sha256").update(content).digest("hex");
5334
5561
  } catch {
5335
5562
  return null;
5336
5563
  }
@@ -5436,7 +5663,7 @@ async function detectDrift() {
5436
5663
  }
5437
5664
  function computeScopeHash(scope) {
5438
5665
  try {
5439
- const hash = crypto2.createHash("sha256");
5666
+ const hash = crypto3.createHash("sha256");
5440
5667
  const root = getProjectRoot();
5441
5668
  hash.update(scope);
5442
5669
  let found = false;
@@ -6343,6 +6570,7 @@ __export(kumaGotchas_exports, {
6343
6570
  addGotcha: () => addGotcha,
6344
6571
  checkGotchasForFile: () => checkGotchasForFile,
6345
6572
  listGotchas: () => listGotchas,
6573
+ syncGotchasGraph: () => syncGotchasGraph,
6346
6574
  syncGotchasToDb: () => syncGotchasToDb
6347
6575
  });
6348
6576
  async function ensureGotchasSchema() {
@@ -6376,32 +6604,58 @@ async function addGotcha(entry) {
6376
6604
  entry.workaround ? `- **Workaround**: ${entry.workaround}` : "",
6377
6605
  `- **Added**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
6378
6606
  ].filter(Boolean).join("\n");
6379
- db.run(
6380
- `INSERT INTO known_gotchas (file_path, description, severity, workaround) VALUES (?, ?, ?, ?)`,
6381
- [entry.filePath, entry.description, severity, entry.workaround || null]
6607
+ const checkStmt = db.prepare(
6608
+ `SELECT id FROM known_gotchas WHERE file_path = ? AND description LIKE ?`
6382
6609
  );
6383
- const mdResult = appendToLayer("gotcha", formatted);
6384
- try {
6385
- const { upsertNode: upsertNode2, addEdge: addEdge2, nodeId: nodeId2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
6386
- const fileNodeId = nodeId2("file", entry.filePath);
6387
- const gotchaNodeId = `gotcha::${entry.filePath}::${entry.description.substring(0, 30)}`;
6388
- await upsertNode2({ id: fileNodeId, type: "file", name: entry.filePath });
6389
- await upsertNode2({
6390
- id: gotchaNodeId,
6391
- type: "gotcha",
6392
- name: `gotcha:${entry.description.substring(0, 40)}`,
6393
- filePath: entry.filePath,
6394
- metadata: { severity, workaround: entry.workaround }
6395
- });
6396
- await addEdge2({ sourceId: fileNodeId, targetId: gotchaNodeId, type: "depends_on" });
6397
- } catch {
6610
+ checkStmt.bind([entry.filePath, `${entry.description.substring(0, 30)}%`]);
6611
+ const existing = checkStmt.step();
6612
+ let existingId = null;
6613
+ if (existing) {
6614
+ const row = checkStmt.getAsObject();
6615
+ existingId = row.id;
6616
+ }
6617
+ checkStmt.free();
6618
+ if (existingId !== null) {
6619
+ db.run(
6620
+ `UPDATE known_gotchas SET severity = ?, workaround = ?, updated_at = strftime('%s','now') WHERE id = ?`,
6621
+ [severity, entry.workaround || null, existingId]
6622
+ );
6623
+ } else {
6624
+ db.run(
6625
+ `INSERT INTO known_gotchas (file_path, description, severity, workaround) VALUES (?, ?, ?, ?)`,
6626
+ [entry.filePath, entry.description, severity, entry.workaround || null]
6627
+ );
6398
6628
  }
6629
+ const mdResult = appendToLayer("gotcha", formatted);
6399
6630
  saveDb();
6631
+ rebuildFtsIndex();
6400
6632
  sessionMemory.recordToolCall("kuma_gotcha_add", {
6401
6633
  filePath: entry.filePath,
6402
6634
  severity
6403
6635
  });
6404
- return `\u2705 **Gotcha recorded**: ${entry.filePath} \u2014 ${entry.description}
6636
+ let linkedCount = 0;
6637
+ try {
6638
+ const { addEdge: addEdge2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
6639
+ const stmt = db.prepare(`
6640
+ SELECT id FROM nodes WHERE type = 'arch_flow'
6641
+ AND (metadata LIKE ? OR file_path = ?)
6642
+ LIMIT 5
6643
+ `);
6644
+ stmt.bind([`%${entry.filePath}%`, entry.filePath]);
6645
+ while (stmt.step()) {
6646
+ const row = stmt.getAsObject();
6647
+ const gotchaNodeId = `gotcha::${entry.filePath}::${entry.description.substring(0, 30)}`;
6648
+ try {
6649
+ await addEdge2({ sourceId: gotchaNodeId, targetId: row.id, type: "triggers" });
6650
+ linkedCount++;
6651
+ } catch {
6652
+ }
6653
+ }
6654
+ stmt.free();
6655
+ } catch {
6656
+ }
6657
+ const action = existingId !== null ? "updated" : "recorded";
6658
+ return `\u2705 **Gotcha ${action}**: ${entry.filePath} \u2014 ${entry.description}${linkedCount > 0 ? ` (linked to ${linkedCount} flow(s))` : ""}
6405
6659
  ${mdResult}`;
6406
6660
  } catch (err) {
6407
6661
  return `\u274C Failed to add gotcha: ${err}`;
@@ -6490,6 +6744,37 @@ async function syncGotchasToDb() {
6490
6744
  return { synced: 0 };
6491
6745
  }
6492
6746
  }
6747
+ async function syncGotchasGraph() {
6748
+ try {
6749
+ await ensureGotchasSchema();
6750
+ const { upsertNode: upsertNode2, addEdge: addEdge2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
6751
+ const db = await getDb();
6752
+ let created = 0;
6753
+ const stmt = db.prepare(
6754
+ `SELECT file_path, description, severity, workaround FROM known_gotchas`
6755
+ );
6756
+ const rows = [];
6757
+ while (stmt.step()) rows.push(stmt.getAsObject());
6758
+ stmt.free();
6759
+ for (const g of rows) {
6760
+ const fileNodeId = `file::${g.file_path}`;
6761
+ const gotchaNodeId = `gotcha::${g.file_path}::${g.description.substring(0, 30)}`;
6762
+ await upsertNode2({ id: fileNodeId, type: "file", name: g.file_path });
6763
+ await upsertNode2({
6764
+ id: gotchaNodeId,
6765
+ type: "gotcha",
6766
+ name: `gotcha:${g.description.substring(0, 40)}`,
6767
+ filePath: g.file_path,
6768
+ metadata: { severity: g.severity, workaround: g.workaround || void 0 }
6769
+ });
6770
+ await addEdge2({ sourceId: fileNodeId, targetId: gotchaNodeId, type: "depends_on" });
6771
+ created++;
6772
+ }
6773
+ return { created };
6774
+ } catch {
6775
+ return { created: 0 };
6776
+ }
6777
+ }
6493
6778
  var init_kumaGotchas = __esm({
6494
6779
  "src/engine/kumaGotchas.ts"() {
6495
6780
  "use strict";
@@ -6729,6 +7014,124 @@ var init_kumaGitHarvester = __esm({
6729
7014
  }
6730
7015
  });
6731
7016
 
7017
+ // src/engine/kumaSessionMiner.ts
7018
+ var kumaSessionMiner_exports = {};
7019
+ __export(kumaSessionMiner_exports, {
7020
+ formatMinedInsights: () => formatMinedInsights,
7021
+ mineSessionInsights: () => mineSessionInsights
7022
+ });
7023
+ function mineSessionInsights() {
7024
+ const insights = [];
7025
+ const toolCalls = sessionMemory.getToolCallHistory(50);
7026
+ if (toolCalls.length < 3) return insights;
7027
+ const editCounts = /* @__PURE__ */ new Map();
7028
+ for (const call of toolCalls) {
7029
+ if (call.toolName === "browser_edit" || call.toolName === "write" || call.toolName === "edit") {
7030
+ const file = call.params.filePath || call.params.path || "";
7031
+ if (file) editCounts.set(file, (editCounts.get(file) || 0) + 1);
7032
+ }
7033
+ }
7034
+ for (const [file, count] of editCounts) {
7035
+ if (count >= 3) {
7036
+ insights.push({
7037
+ type: "gotcha",
7038
+ scope: file,
7039
+ content: `File required ${count} edits in one session \u2014 may indicate underlying complexity or recurring issue`,
7040
+ confidence: 0.6,
7041
+ source: `edit_count:${count}:${file}`
7042
+ });
7043
+ }
7044
+ }
7045
+ for (let i = 0; i < toolCalls.length - 1; i++) {
7046
+ const curr = toolCalls[i];
7047
+ const next = toolCalls[i + 1];
7048
+ if (curr.toolName === "bash" && next.toolName === "edit") {
7049
+ const cmd = curr.params.command || "";
7050
+ const file = next.params.filePath || "";
7051
+ if ((cmd.includes("test") || cmd.includes("lint") || cmd.includes("typecheck")) && file) {
7052
+ insights.push({
7053
+ type: "gotcha",
7054
+ scope: file,
7055
+ content: `Test/lint failed then file was edited \u2014 likely a bug fix`,
7056
+ confidence: 0.7,
7057
+ source: `error_fix_sequence`
7058
+ });
7059
+ }
7060
+ }
7061
+ }
7062
+ let pendingReads = [];
7063
+ for (const call of toolCalls) {
7064
+ if (call.toolName === "read" || call.toolName === "browser_read") {
7065
+ const file = call.params.filePath || "";
7066
+ if (file) pendingReads.push(file);
7067
+ } else if (call.toolName === "edit" && pendingReads.length >= 2) {
7068
+ const file = call.params.filePath || "";
7069
+ if (file) {
7070
+ insights.push({
7071
+ type: "arch_flow",
7072
+ scope: file,
7073
+ content: `domain: ${file.split("/").pop()?.replace(/\.\w+$/, "") || "Unknown"} | hops: ${pendingReads.slice(0, 5).map((f) => f.split("/").pop() || f).join(" \u2192 ")}`,
7074
+ confidence: 0.5,
7075
+ source: `research_flow:${pendingReads.length}_reads`
7076
+ });
7077
+ }
7078
+ pendingReads = [];
7079
+ } else {
7080
+ pendingReads = [];
7081
+ }
7082
+ }
7083
+ for (let i = 0; i < toolCalls.length - 2; i++) {
7084
+ const a = toolCalls[i];
7085
+ const b = toolCalls[i + 1];
7086
+ const c = toolCalls[i + 2];
7087
+ if ((a.toolName === "read" || a.toolName === "browser_read") && (b.toolName === "read" || b.toolName === "browser_read") && c.toolName === "edit") {
7088
+ const fileA = a.params.filePath || "";
7089
+ const fileB = b.params.filePath || "";
7090
+ const fileC = c.params.filePath || "";
7091
+ if (fileA !== fileB && fileC) {
7092
+ insights.push({
7093
+ type: "decision",
7094
+ scope: fileC,
7095
+ content: `Read ${fileA.split("/").pop()} and ${fileB.split("/").pop()} before editing ${fileC.split("/").pop()} \u2014 compared options`,
7096
+ confidence: 0.5,
7097
+ source: `compare_then_choose`
7098
+ });
7099
+ }
7100
+ }
7101
+ }
7102
+ return insights;
7103
+ }
7104
+ function formatMinedInsights(insights) {
7105
+ if (insights.length === 0) {
7106
+ return "\u{1F50D} **Session Mining** \u2014 No patterns detected. Session was clean.";
7107
+ }
7108
+ const lines = [
7109
+ `\u{1F50D} **Session Mining** \u2014 ${insights.length} insight${insights.length > 1 ? "s" : ""} found`,
7110
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
7111
+ "",
7112
+ "Review and approve/skip each suggestion:",
7113
+ ""
7114
+ ];
7115
+ for (let i = 0; i < insights.length; i++) {
7116
+ const ins = insights[i];
7117
+ const icon = ins.type === "gotcha" ? "\u26A0\uFE0F" : ins.type === "decision" ? "\u{1F4CC}" : "\u{1F500}";
7118
+ const conf = Math.round(ins.confidence * 100);
7119
+ lines.push(`${i + 1}. ${icon} **${ins.type}** (${conf}% confidence)`);
7120
+ lines.push(` Scope: ${ins.scope}`);
7121
+ lines.push(` ${ins.content}`);
7122
+ lines.push(` \u2192 \`kuma_memory({ action: "${ins.type}", ${ins.type === "arch_flow" ? "content" : 'scope: \\"${ins.scope}\\", content'}: "${ins.content.substring(0, 60)}..." })\``);
7123
+ lines.push("");
7124
+ }
7125
+ lines.push("\u{1F4A1} Approve by recording the ones you agree with. Skip the rest.");
7126
+ return lines.join("\n");
7127
+ }
7128
+ var init_kumaSessionMiner = __esm({
7129
+ "src/engine/kumaSessionMiner.ts"() {
7130
+ "use strict";
7131
+ init_sessionMemory();
7132
+ }
7133
+ });
7134
+
6732
7135
  // src/engine/safetyAudit.ts
6733
7136
  var safetyAudit_exports = {};
6734
7137
  __export(safetyAudit_exports, {
@@ -7975,7 +8378,7 @@ function detectTestRunner(root = process.cwd()) {
7975
8378
  } catch {
7976
8379
  }
7977
8380
  }
7978
- return { runner: "unknown", baseCommand: "npm test || echo 'No test runner configured'" };
8381
+ return { runner: "unknown", baseCommand: "" };
7979
8382
  }
7980
8383
  function checkAllowed(root) {
7981
8384
  if (_localRunning) return "\u23F3 Verification already in progress (this instance).";
@@ -8029,6 +8432,25 @@ async function runAutoVerification(options = {}) {
8029
8432
  }
8030
8433
  _localRunning = true;
8031
8434
  const { runner, baseCommand } = detectTestRunner(root);
8435
+ if (runner === "unknown" || !baseCommand) {
8436
+ _localRunning = false;
8437
+ releaseFileLock(root);
8438
+ const durationMs = Date.now() - startTime;
8439
+ const output = "\u2139\uFE0F No test framework detected. Install Jest, Vitest, pytest, or similar to enable verification.";
8440
+ await saveVerification(scope, "none", "none", true, output, durationMs);
8441
+ return [
8442
+ `\u26AA **Verification: NO TESTS**`,
8443
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
8444
+ "",
8445
+ `\u{1F6E0}\uFE0F **Runner**: \`none\` (no test framework detected)`,
8446
+ `\u{1F3AF} **Scope**: \`${scope}\``,
8447
+ `\u23F1\uFE0F **Duration**: ${durationMs}ms`,
8448
+ "",
8449
+ "\u2139\uFE0F No test framework detected. Install Jest, Vitest, pytest, or similar to enable verification.",
8450
+ "",
8451
+ "\u{1F4A1} **Tip:** This is NOT a pass \u2014 it means nothing was verified. Run your tests manually or install a test framework."
8452
+ ].join("\n");
8453
+ }
8032
8454
  let testFiles = [];
8033
8455
  const modified = sessionMemory.getModifiedFiles().map((f) => f.filePath);
8034
8456
  if (options.scope) {
@@ -8078,7 +8500,18 @@ async function runAutoVerification(options = {}) {
8078
8500
  const child = exec(fullCommand, { cwd: root, timeout: timeoutMs }, async (error, stdout, stderr) => {
8079
8501
  const durationMs = Date.now() - startTime;
8080
8502
  const rawOutput = (stdout + "\n" + stderr).trim();
8081
- const passed = !error;
8503
+ let passed = !error;
8504
+ const fakePassPatterns = [
8505
+ /Missing script.*test/i,
8506
+ /No test runner configured/i,
8507
+ /No tests found/i,
8508
+ /Cannot find.*test/i,
8509
+ /0 passing/i,
8510
+ /no test specified/i
8511
+ ];
8512
+ if (passed && fakePassPatterns.some((p) => p.test(rawOutput))) {
8513
+ passed = false;
8514
+ }
8082
8515
  const truncatedOutput = rawOutput.length > 2e3 ? rawOutput.substring(rawOutput.length - 2e3) : rawOutput;
8083
8516
  await saveVerification(scope, runner, fullCommand, passed, truncatedOutput, durationMs);
8084
8517
  _localRunning = false;
@@ -8097,11 +8530,17 @@ async function runAutoVerification(options = {}) {
8097
8530
  ""
8098
8531
  ];
8099
8532
  if (!passed) {
8100
- lines.push("\u26A0\uFE0F **Verification failed!** Please fix test failures before shipping.");
8101
- if (error && error.killed) {
8102
- lines.push(`\u23F0 **Process was killed after ${timeoutMs}ms timeout**`);
8533
+ const isNoTests = fakePassPatterns.some((p) => p.test(rawOutput));
8534
+ if (isNoTests) {
8535
+ lines[0] = `\u26AA **Verification: NO TESTS**`;
8536
+ lines.push("\u2139\uFE0F No tests ran. Install Jest, Vitest, pytest, or similar to enable verification.");
8537
+ } else {
8538
+ lines.push("\u26A0\uFE0F **Verification failed!** Please fix test failures before shipping.");
8539
+ if (error && error.killed) {
8540
+ lines.push(`\u23F0 **Process was killed after ${timeoutMs}ms timeout**`);
8541
+ }
8542
+ lines.push("```text", rawOutput.substring(0, 1500), "```");
8103
8543
  }
8104
- lines.push("```text", rawOutput.substring(0, 1500), "```");
8105
8544
  } else {
8106
8545
  lines.push("\u{1F389} All scoped tests passed!");
8107
8546
  if (rawOutput) lines.push("```text", rawOutput.substring(0, 800), "```");
@@ -8244,28 +8683,23 @@ function opencodeAgentsMdTemplate() {
8244
8683
  const opencodeLines = [
8245
8684
  "Kuma MCP tools are installed (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
8246
8685
  '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
8247
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
8248
8686
  "",
8249
- "\u26A0\uFE0F **MANDATORY WORKFLOW** \u2014 Follow in order every session (including research-only):",
8687
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
8688
+ ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8689
+ " 2. *(edit/read using native tools)*",
8690
+ ' 3. `kuma_kuma_context({ action: "changes" })` \u2014 Review session',
8250
8691
  "",
8251
- ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8252
- ' 2. `kuma_kuma_safety({ action: "guard" })` \u2014 Safety check before work',
8253
- ' 3. `kuma_kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
8254
- " 4. *(edit/read using native tools)*",
8255
- ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
8256
- ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential value)',
8257
- ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 After tracing COMPLETE flow, max 5 core files',
8258
- ' 8. `kuma_kuma_memory({ action: "decision" })` \u2014 When choosing between options (preserves rationale)',
8259
- ' 9. `kuma_kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
8260
- ' 10. `kuma_kuma_context({ action: "changes" })` \u2014 Review session activity',
8692
+ "\u{1F4DD} **RECORD when needed (don't force it):**",
8693
+ ' \u2022 Found a bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
8694
+ ' \u2022 Traced a flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
8695
+ ' \u2022 Chose between options? \u2192 `kuma_kuma_memory({ action: "decision" })`',
8261
8696
  "",
8262
- "\u{1F525} **Power Curve:** `arch_flow` + `gotcha` are EXPONENTIAL \u2014 arch_flow records complete flow (max 5 core files), gotcha records bugs inline.",
8263
- " The more you record, the more overpowered the agent becomes. These are the highest-value actions.",
8264
- "\u{1F7E2} **SKIP** recording function/class/component nodes \u2014 grep/glob is faster.",
8265
- "\u{1F9E0} Knowledge graph is persistent \u2014 nodes/edges accumulate across sessions, getting richer over time.",
8697
+ "\u{1F527} **ON-DEMAND (use when appropriate):**",
8698
+ ' \u2022 `kuma_kuma_safety({ action: "guard" })` \u2014 before risky ops',
8699
+ ' \u2022 `kuma_kuma_context({ action: "research" })` \u2014 before editing unfamiliar code',
8700
+ ' \u2022 `kuma_kuma_safety({ action: "verify" })` \u2014 after edits',
8266
8701
  "",
8267
- "\u26A0\uFE0F **OpenCode platform note:** Tool names use `kuma_kuma_*` prefix",
8268
- " (server name `kuma` + already-prefixed tool name `kuma_context`).",
8702
+ "\u26A0\uFE0F **OpenCode note:** Tool names use `kuma_kuma_*` prefix.",
8269
8703
  ""
8270
8704
  ].join("\n");
8271
8705
  return [
@@ -8360,28 +8794,23 @@ function antigravitySkillTemplate() {
8360
8794
  const antigravityLines = [
8361
8795
  "Kuma MCP tools are installed (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
8362
8796
  '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
8363
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
8364
8797
  "",
8365
- "\u26A0\uFE0F **MANDATORY WORKFLOW** \u2014 Follow in order every session (including research-only):",
8798
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
8799
+ ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8800
+ " 2. *(edit/read using native tools)*",
8801
+ ' 3. `kuma_kuma_context({ action: "changes" })` \u2014 Review session',
8366
8802
  "",
8367
- ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8368
- ' 2. `kuma_kuma_safety({ action: "guard" })` \u2014 Safety check before work',
8369
- ' 3. `kuma_kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
8370
- " 4. *(edit/read using native tools)*",
8371
- ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
8372
- ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential value)',
8373
- ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 After tracing COMPLETE flow, max 5 core files',
8374
- ' 8. `kuma_kuma_memory({ action: "decision" })` \u2014 When choosing between options (preserves rationale)',
8375
- ' 9. `kuma_kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
8376
- ' 10. `kuma_kuma_context({ action: "changes" })` \u2014 Review session activity',
8803
+ "\u{1F4DD} **RECORD when needed (don't force it):**",
8804
+ ' \u2022 Found a bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
8805
+ ' \u2022 Traced a flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
8806
+ ' \u2022 Chose between options? \u2192 `kuma_kuma_memory({ action: "decision" })`',
8377
8807
  "",
8378
- "\u{1F525} **Power Curve:** `arch_flow` + `gotcha` are EXPONENTIAL \u2014 arch_flow records complete flow (max 5 core files), gotcha records bugs inline.",
8379
- " The more you record, the more overpowered the agent becomes. These are the highest-value actions.",
8380
- "\u{1F7E2} **SKIP** recording function/class/component nodes \u2014 grep/glob is faster.",
8381
- "\u{1F9E0} Knowledge graph is persistent \u2014 nodes/edges accumulate across sessions, getting richer over time.",
8808
+ "\u{1F527} **ON-DEMAND:**",
8809
+ ' \u2022 `kuma_kuma_safety({ action: "guard" })` \u2014 before risky ops',
8810
+ ' \u2022 `kuma_kuma_context({ action: "research" })` \u2014 before editing unfamiliar code',
8811
+ ' \u2022 `kuma_kuma_safety({ action: "verify" })` \u2014 after edits',
8382
8812
  "",
8383
- "\u26A0\uFE0F **Antigravity platform note:** Tool names use `kuma_kuma_*` prefix",
8384
- " (server name `kuma` + already-prefixed tool name `kuma_context`).",
8813
+ "\u26A0\uFE0F **Antigravity note:** Tool names use `kuma_kuma_*` prefix.",
8385
8814
  ""
8386
8815
  ].join("\n");
8387
8816
  return [
@@ -8410,429 +8839,161 @@ function antigravityMcpConfigTemplate() {
8410
8839
  }
8411
8840
  function generateInitMdContent() {
8412
8841
  return [
8413
- "# Kuma Init \u2014 Mandatory Workflow",
8842
+ "# Kuma Init \u2014 Workflow & Rules",
8414
8843
  "",
8415
- "_(Auto-generated by `kuma init` \u2014 edit this file directly to customize rules)_",
8844
+ "_(Auto-generated by `kuma init` \u2014 edit this file to customize)_",
8416
8845
  "",
8417
8846
  "---",
8418
8847
  "",
8419
- "> \u26A0\uFE0F **Platform Tool Names:**",
8848
+ "> **Platform Tool Names:**",
8420
8849
  '> \u2022 **OpenCode / Antigravity:** Use `kuma_kuma_*` prefix (e.g. `kuma_kuma_context({ action: "init" })`)',
8421
- '> \u2022 **Other platforms (Claude, Cursor, Cline, etc.):** Use `kuma_*` directly (e.g. `kuma_context({ action: "init" })`)',
8422
- "> \u2022 **Replace `kuma_` with `kuma_kuma_` in all examples below if you're on OpenCode.**",
8850
+ '> \u2022 **Other platforms:** Use `kuma_*` directly (e.g. `kuma_context({ action: "init" })`)',
8423
8851
  "",
8424
8852
  "---",
8425
8853
  "",
8426
- "## \u26A0\uFE0F SESSION WORKFLOW \u2014 Follow Steps in Order",
8427
- "",
8428
- "### STEP 1: Init Session (ALWAYS \u2014 Start of Session)",
8429
- "```",
8430
- 'kuma_context({ action: "init" })',
8431
- "```",
8432
- "**When:** Every session start or project switch",
8433
- "**Why:** Load project brief, restore session memory, populate knowledge graph",
8854
+ "## SESSION WORKFLOW",
8434
8855
  "",
8435
- "### STEP 2: Safety Guard (ALWAYS \u2014 Before Work)",
8436
- "```",
8437
- 'kuma_safety({ action: "guard", guardGoal: "describe your intent" })',
8438
- "```",
8439
- "**When:** After STEP 1, before research or editing",
8440
- "**Why:** Detect anti-patterns, code drift, runaway loops upfront",
8856
+ "### DEFAULT: 3-Step Lean Workflow",
8441
8857
  "",
8442
- "### STEP 3: Research (MANDATORY \u2014 Before Editing Unfamiliar Code)",
8443
- "```",
8444
- 'kuma_context({ action: "research", scope: "<target area>" })',
8445
- "```",
8446
- "**When:** Every time you plan to edit code in an unfamiliar area",
8447
- "**Why:** 5-step pipeline: cache load \u2192 staleness check \u2192 graph query \u2192 impact analysis \u2192 decision lookup",
8858
+ "**For most tasks (edits, reads, simple changes):**",
8448
8859
  "",
8449
- "> \u26D4 DO NOT EDIT FILES BEFORE STEP 3 \u2014 Unless trivial/minor change",
8860
+ '1. `kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8861
+ "2. *(edit/read using native tools)*",
8862
+ '3. `kuma_context({ action: "changes" })` \u2014 Review session',
8450
8863
  "",
8451
- "### STEP 4: EDIT/READ \u2014 Implement (Use Agent Native Tools)",
8452
- "```",
8453
- "# Use agent BUILT-IN tools to read, grep, edit, create files",
8454
- "# Kuma does NOT manage files \u2014 that's your agent's job",
8455
- "```",
8864
+ "### ON-DEMAND: Extended Actions",
8456
8865
  "",
8457
- "> \u26A0\uFE0F **Research-only sessions included.** Workflow ini wajib meskipun session hanya membaca/riset tanpa code changes.",
8458
- "",
8459
- "> \u{1F9E0} **Graph Philosophy:** Record findings INLINE as you work, not just at the end. Every time you read a",
8460
- "> file, grep a pattern, or trace a flow \u2014 call `research_save` immediately to persist what you learned.",
8461
- "> The knowledge graph accumulates across sessions. The more you use it, the richer it becomes.",
8462
- "",
8463
- "> \u{1F52C} **Scanner vs Inline Recording:**",
8464
- "> \u2022 Scanner = cold start only. Auto-detects structure in TS/JS/TSX/JSX files during research step 3.",
8465
- "> \u2022 Inline Recording = primary method. You (the agent) record findings as you read/grep/edit.",
8466
- "> \u2022 Non-TS/JS projects (Go, Ruby, Python, Rust, PHP) rely ENTIRELY on inline recording.",
8467
- "",
8468
- "> \u{1F6E1}\uFE0F **Node Idempotency & Relational Integrity (Anti-Duplication & No Orphans):**",
8469
- "> \u2022 **Zero Duplicate Risk:** `research_save` and `add_node` are fully idempotent. Re-saving an existing scope/file updates metadata and timestamp without duplicating nodes.",
8470
- "> \u2022 **Zero Orphan Guarantee:** Every node created with a `filePath` or `scope` is automatically linked via a `contains` or `depends_on` edge to its parent file node.",
8471
- "> \u2022 **Always Connected:** Avoid recording standalone orphan entities; link findings to a core business domain or file path.",
8472
- "",
8473
- "**AUTO-RECORDED (cold start scanner \u2014 TS/JS only):**",
8474
- "- Files, Functions, Classes \u2192 scanned during research step 3",
8475
- "- Imports, Calls, Extends \u2192 scanned edges",
8476
- "- Components, Routes \u2192 scanned when patterns match",
8477
- "",
8478
- "\u{1F534} **MUST RECORD \u2014 High Impact (use MCP):**",
8479
- "- `arch_flow` \u2014 AFTER tracing COMPLETE flow (not per hop). Max 5 core files.",
8480
- "- `gotcha` \u2014 IMMEDIATELY when bug/quirk found. No re-research needed.",
8481
- "- `decision` \u2014 IMMEDIATELY when choosing. Preserves rationale context.",
8482
- "- `research_save` \u2014 After exploring area. Creates searchable cache + graph file node.",
8483
- "",
8484
- "\u{1F7E2} **SKIP \u2014 Low Impact (agent can grep faster):**",
8485
- "- Function/class nodes \u2192 grep `funcName(` / `class ClassName`",
8486
- "- Component nodes \u2192 glob `**/*ComponentName*`",
8487
- "- Import edges \u2192 check imports directly",
8488
- "- Route nodes \u2192 check router file directly",
8489
- "- Visual graph \u2192 for humans, not AI agents",
8490
- "",
8491
- "**RECORD INLINE \u2014 Focus on high-impact actions:**",
8492
- "- Finished tracing a flow? \u2192 `arch_flow` AFTER tracing COMPLETE flow (max 5 core files)",
8493
- "- Found a bug/quirk? \u2192 `gotcha` IMMEDIATELY (no re-research needed)",
8494
- "- Chose between options? \u2192 `decision` IMMEDIATELY (preserves rationale)",
8495
- "- Explored a new area? \u2192 `research_save` (creates searchable cache)",
8496
- "",
8497
- "**SKIP recording these \u2014 grep/glob is faster:**",
8498
- "- Function/class nodes \u2192 just `grep funcName(` or `grep class ClassName`",
8499
- "- Component/route nodes \u2192 glob directly",
8500
- "- Import edges \u2192 read import statements",
8501
- "- Visual graph \u2192 for humans, not AI",
8502
- "",
8503
- "### STEP 5: Save Research (RECORD INLINE \u2014 After Every Read/Grep)",
8504
- "```",
8505
- 'kuma_memory({ action: "research_save", scope: "<file or area>", confidence: 0.8 })',
8506
- "```",
8507
- "**When:** After EVERY read/grep that discovers new files, functions, imports, or patterns. Don't wait \u2014 record inline!",
8508
- "**Why:** Persist findings into knowledge graph + research cache. Graph grows richer across sessions.",
8866
+ "**Record only when something important happens:**",
8867
+ '- Found a bug/quirk? \u2192 `kuma_memory({ action: "gotcha" })` IMMEDIATELY',
8868
+ '- Traced a complete flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 core files)',
8869
+ '- Chose between options? \u2192 `kuma_memory({ action: "decision" })` to preserve rationale',
8870
+ '- Explored new area? \u2192 `kuma_memory({ action: "research_save", scope: "<file>" })`',
8509
8871
  "",
8510
- "### STEP 6: Record Gotchas (RECORD INLINE \u2014 When Bug Found)",
8511
- "```",
8512
- 'kuma_memory({ action: "gotcha", scope: "<file>", content: "<description>", status: "medium" })',
8513
- "```",
8514
- "**When:** IMMEDIATELY when you discover bugs, quirks, unexpected behavior, or workarounds. Record INLINE, don't save for later.",
8515
- "**Why:** Prevents future agents from hitting the same issues. Every bug = a gotcha. Skip now = lost forever.",
8872
+ "**Safety checks (use when appropriate):**",
8873
+ '- Before risky edits: `kuma_safety({ action: "guard", guardGoal: "..." })`',
8874
+ '- After edits: `kuma_safety({ action: "verify", scope: "<area>" })`',
8875
+ '- Before research: `kuma_context({ action: "research", scope: "<area>" })`',
8516
8876
  "",
8517
- "**\u26A0\uFE0F Parameters:**",
8518
- "- `scope` \u2014 file path where the bug was found",
8519
- "- `content` \u2014 bug description",
8520
- "- `status` \u2014 severity: `low` | `medium` | `high` | `critical` (default: `medium`)",
8521
- "- `description` \u2014 optional workaround/fix description",
8877
+ "### Recording Rules",
8522
8878
  "",
8523
- "### STEP 7: Record Arch Flow (RECORD COMPLETE FLOW \u2014 Core Logic Only)",
8524
- "```",
8525
- 'kuma_memory({ action: "arch_flow", content: "domain: <name> | hops: <core_file1> \u2192 <core_file2> \u2192 <core_file3>" })',
8526
- "```",
8527
- "**When:** AFTER tracing a COMPLETE architecture flow (not per hop).",
8528
- "**Why:** Record flow architecture for future sessions without cluttering the graph.",
8879
+ "**RECORD (high value):**",
8880
+ "- `gotcha` \u2014 IMMEDIATELY when bug/quirk found (saves future agents from same issue)",
8881
+ "- `arch_flow` \u2014 AFTER tracing COMPLETE flow (max 5 core files, skip UI/controllers/schemas)",
8882
+ "- `decision` \u2014 IMMEDIATELY when choosing between options (preserves rationale)",
8883
+ "- `research_save` \u2014 After exploring a new area (creates search cache)",
8529
8884
  "",
8530
- "**Rules:**",
8531
- "1. MAX 5 files per flow \u2014 record core business logic files only.",
8532
- "2. SKIP UI components (*.tsx, *.jsx), Controllers (*Controller.js), Schemas (*Schema.js), and simple CRUD services.",
8533
- "3. INCLUDE files with complex business logic, decision points, state mutations, or error handling.",
8534
- "4. Format hops separated by ` \u2192 ` (e.g. `domain: LeadDistributionFlow | hops: LeadRotationService.js \u2192 ProspectAssignmentService.js \u2192 ActivityLogService.js`).",
8885
+ "**SKIP (low value):** function/class/component nodes \u2014 grep/glob is faster",
8535
8886
  "",
8536
- "**\u26A0\uFE0F CRITICAL FORMAT \u2014 Content MUST use this exact syntax:**",
8887
+ "**\u26A0\uFE0F gotcha format:**",
8537
8888
  "```",
8538
- "domain: <DomainName> | hops: <core_file1.js> \u2192 <core_file2.js> \u2192 <core_file3.js> | gotchas: <optional> | decisions: <optional>",
8889
+ 'kuma_memory({ action: "gotcha", scope: "<file_path>", content: "<description>", status: "medium" })',
8539
8890
  "```",
8540
- "- `domain:` \u2014 short domain name (e.g. `InviteUserFlow`, `LeadManagementFlow`)",
8541
- "- `hops:` \u2014 max 5 core file names separated by ` \u2192 `",
8542
- "- `gotchas:` \u2014 optional comma-separated gotchas",
8543
- "- `decisions:` \u2014 optional comma-separated decisions",
8891
+ "- `scope` \u2014 file path where the bug was found",
8892
+ "- `content` \u2014 bug description",
8893
+ "- `status` \u2014 severity: `low` | `medium` | `high` | `critical`",
8544
8894
  "",
8545
- "**Example:**",
8895
+ "**\u26A0\uFE0F arch_flow format:**",
8546
8896
  "```",
8547
- "domain: InviteUserFlow | hops: invitationModal.tsx \u2192 useInviteMemberForm.ts \u2192 workspaceService.ts \u2192 workspaceController.js",
8897
+ 'kuma_memory({ action: "arch_flow", content: "domain: <Name> | hops: <file1> \u2192 <file2> \u2192 <file3>" })',
8548
8898
  "```",
8899
+ "- MAX 5 files per flow \u2014 core business logic only",
8900
+ "- SKIP UI components, Controllers, Schemas, simple CRUD",
8901
+ "- INCLUDE files with complex logic, decision points, state mutations",
8549
8902
  "",
8550
- "### STEP 8: Record Decision (RECORD INLINE \u2014 When Choosing)",
8903
+ "**\u26A0\uFE0F decision format:**",
8551
8904
  "```",
8552
8905
  'kuma_memory({ action: "decision", decisionAction: "record", title: "...", context: "...", rationale: "...", outcome: "..." })',
8553
8906
  "```",
8554
- "**When:** IMMEDIATELY when you choose between 2+ options, uncover an architecture pattern, or identify a root cause.",
8555
- "**Why:** Saves ADR (Architecture Decision Record). Bukan cuma code changes \u2014 temuan, tradeoffs, root causes juga.",
8907
+ "- Required: `title` and `rationale`",
8556
8908
  "",
8557
- "**\u26A0\uFE0F Required:** `title` and `rationale`. Optional: `context`, `outcome`.",
8909
+ "---",
8558
8910
  "",
8559
- "### STEP 9: Verify (MANDATORY \u2014 After Research/Editing)",
8560
- "```",
8561
- 'kuma_safety({ action: "verify", scope: "<area or file>" })',
8562
- "```",
8563
- "**When:** After research/edits are done, before switching tasks. Even for research-only \u2014 confirms nothing is broken.",
8564
- '**Why:** Auto-run scoped tests + AST code validation. If no test framework, at minimum report "No tests to run".',
8911
+ "## \u26A0\uFE0F HONEST LIMITATIONS",
8565
8912
  "",
8566
- "### STEP 10: Check Changes (MANDATORY \u2014 End of Session)",
8567
- "```",
8568
- 'kuma_context({ action: "changes" })',
8569
- "```",
8570
- "**When:** End of session or before git commit. Even 0 changes = valid result to confirm.",
8571
- "**Why:** View session log of all modifications + tool calls made in this session.",
8913
+ "- **Node IDs are text-based** \u2014 same file recorded with slightly different paths can create duplicates. Use `delete_node` to clean up.",
8914
+ "- **Metadata is JSON** \u2014 format varies per node type. Studio displays all fields dynamically.",
8915
+ "- **Gotchas have 2 stores** \u2014 `known_gotchas` table + `nodes` graph. `syncGotchasGraph()` keeps them in sync.",
8916
+ "- **Edge weights are static** \u2014 `contains`=2.0, `flows_through`=1.0. Dynamic ranking not yet implemented.",
8572
8917
  "",
8573
8918
  "---",
8574
8919
  "",
8575
8920
  "## \u{1F4CB} Tool Reference",
8576
8921
  "",
8577
- "### \u{1F9E0} kuma_context \u2014 Context & Research",
8578
- "",
8579
- "| Action | Description | When to Call |",
8580
- "|--------|-------------|--------------|",
8581
- "| `init` | Load project brief, restore session | STEP 1 \u2014 Every session |",
8582
- "| `research` | 5-step research pipeline | STEP 3 \u2014 Before edits |",
8583
- "| `impact` | Analyze change effects on symbol | After research, before design |",
8584
- "| `navigate` | Trace code flow | When need to understand call flow |",
8585
- "| `changes` | View session change log | STEP 10 \u2014 End of session |",
8586
- "| `health` | Project health score 0-100 | Periodic check |",
8587
- "| `digest` | Ultra-compact project briefing | Session start (alt to init) |",
8588
- "| `drift` | Detect memory staleness | When suspecting stale context |",
8589
- "| `rollback` | Undo a change by change ID | After changes review |",
8590
- "| `researches` | List all cached research scopes | When need to find past research |",
8591
- "| `sync` | Unified batch state (init+health+memory) | Token-efficient session start |",
8592
- "| `visualize` | Mermaid knowledge graph diagram | Human debugging / overview |",
8593
- "",
8594
- "### \u{1F4DD} kuma_memory \u2014 Decision & Knowledge",
8595
- "",
8596
- "| Action | Description | When to Call |",
8597
- "|--------|-------------|--------------|",
8598
- "| `research_save` | Save research results | STEP 5 \u2014 After exploring area (creates search cache) |",
8599
- "| `gotcha` | Record bugs/quirks/workarounds | STEP 6 \u2014 \u{1F525} IMMEDIATELY when bug found (inline) |",
8600
- "| `arch_flow` | Document architecture flow | STEP 7 \u2014 After tracing COMPLETE flow, max 5 core files |",
8601
- "| `decision` | ADR-style decision record | STEP 8 \u2014 IMMEDIATELY when choosing (inline) |",
8602
- "| `mine` | Mine decisions from git log | When need historical context |",
8603
- "| `session` | Session summary | End of session |",
8604
- "| `heal` | Self-heal knowledge graph | When graph has errors |",
8605
- "| `search` | Search memories + graph | Find stored information |",
8606
- "| `todo` | Persistent todo CRUD | When managing tasks |",
8607
- "| `context` | Inject context notes | Additional notes when needed |",
8608
- "| `domain_rules` | Layer 1: business rules | Record business rules when discovered |",
8609
- "| `feature` | Record high-level feature with owns edges | Define features like 'Authentication', 'Billing' |",
8610
- "| `add_node` | Create structural nodes (low impact \u2014 grep faster) | Rarely; only for visualize debugging |",
8611
- "",
8612
- "| `delete_node` | Delete specific nodes, gotchas, todos, or decisions by ID | When you saved wrong data or need cleanup |",
8613
- "| `clear` | Wipe ALL graph nodes, edges, gotchas | Emergency reset \u2014 irreversible |",
8614
- "| `layers` | Show all 3 layers summary | Overview memory layers |",
8615
- "",
8616
- "### \u{1F6E1}\uFE0F kuma_safety \u2014 Safety & Policy",
8617
- "",
8618
- "| Action | Description | When to Call |",
8619
- "|--------|-------------|--------------|",
8620
- "| `guard` | Anti-pattern, drift, loop | STEP 2 \u2014 Before work |",
8621
- "| `verify` | Auto-run scoped tests + validation | STEP 9 \u2014 After research/edits |",
8622
- "| `check` | Pre-execution safety check | Before risky operations |",
8623
- "| `audit` | Query safety audit trail | Security investigation |",
8624
- "| `lock` | Multi-agent file lock | Multi-agent collaboration |",
8625
- "| `health` | Safety score 0-100 | Periodic check |",
8626
- "| `policy` | Policy-as-Code engine | Evaluate command/file paths |",
8627
- "| `ast` | AST-based code validation | Code correctness checks |",
8628
- "| `checkpoint` | Create atomic snapshot | Before major refactors |",
8629
- "| `rollback_label` | Restore from checkpoint | After checkpoint created |",
8630
- "| `checkpoint_list` | List all checkpoints | Review snapshots |",
8631
- "| `contract` | Pre/post-condition check | Contract verification |",
8632
- "| `override` | Logged safety bypass | When safe to bypass guard (with reason) |",
8633
- "| `security` | Security leak scanner | Periodic or pre-commit scan |",
8634
- "| `gc` | Garbage collection | Clean up old data |",
8635
- "| `doctor` | Health diagnostics | Troubleshoot issues |",
8636
- "| `portability` | Check path portability | Cross-platform validation |",
8637
- "| `gitignore` | Auto-configure .gitignore for .kuma/ | After project setup |",
8638
- "| `clean` | Purge scratch + drift warnings | After cleanup needed |",
8639
- "",
8640
- "### \u{1F4D0} Tool Format Quick Reference",
8641
- "",
8642
- "**add_node \u2014 Create manual structural nodes:**",
8643
- "```",
8644
- "kuma_memory({ action: 'add_node', title: '<type>', content: '<name>', scope: '<file_path>' })",
8645
- "```",
8646
- "- `title` \u2014 node type: `function` | `class` | `component` | `file` | `api_route` | `test`",
8647
- "- `content` \u2014 node name (e.g. function name, file path)",
8648
- "- `scope` \u2014 file where this node lives (creates `contains` edge automatically)",
8649
- "",
8650
- "**delete_node \u2014 Remove nodes/records:**",
8651
- "```",
8652
- "// Delete a graph node (by ID):",
8653
- "kuma_memory({ action: 'delete_node', target: 'function::sendMessage' })",
8654
- "",
8655
- "// Delete a record (by scope + numeric ID):",
8656
- "kuma_memory({ action: 'delete_node', scope: 'gotcha', target: '42' })",
8657
- "kuma_memory({ action: 'delete_node', scope: 'todo', target: '7' })",
8658
- "kuma_memory({ action: 'delete_node', scope: 'decision', target: '3' })",
8659
- "```",
8660
- "- Graph node IDs use `::` separator (e.g. `function::sendMessage`, `feature_domain::InviteUserFlow`)",
8661
- "- Record IDs are numeric (shown in list/search results)",
8662
- "- Deleting `feature_domain` cascade-deletes all `cross_service_link` and edges",
8922
+ "### kuma_context \u2014 Context & Research",
8923
+ "",
8924
+ "| Action | Description |",
8925
+ "|--------|-------------|",
8926
+ "| `init` | Load project brief, restore session |",
8927
+ "| `research` | 5-step research pipeline |",
8928
+ "| `impact` | Analyze change effects |",
8929
+ "| `navigate` | Trace code flow |",
8930
+ "| `changes` | View session change log |",
8931
+ "| `health` | Project health score 0-100 |",
8932
+ "| `digest` | Ultra-compact project briefing |",
8933
+ "| `drift` | Detect memory staleness |",
8934
+ "| `rollback` | Undo a change by change ID |",
8935
+ "",
8936
+ "### kuma_memory \u2014 Knowledge Recording",
8937
+ "",
8938
+ "| Action | Description |",
8939
+ "|--------|-------------|",
8940
+ "| `gotcha` | Record bug/quirk (IMMEDIATELY when found) |",
8941
+ "| `arch_flow` | Record architecture flow (max 5 core files) |",
8942
+ "| `decision` | Record ADR / decision rationale |",
8943
+ "| `research_save` | Cache research findings |",
8944
+ "| `search` | Search knowledge graph |",
8945
+ "| `session_mine` | Auto-extract insights from transcript |",
8946
+ "| `delete_node` | Remove node + graph + table entries |",
8947
+ "",
8948
+ "### kuma_safety \u2014 Safety & Verification",
8949
+ "",
8950
+ "| Action | Description |",
8951
+ "|--------|-------------|",
8952
+ "| `guard` | Anti-pattern detection before edits |",
8953
+ "| `verify` | Auto-run scoped tests after edits |",
8954
+ "| `gotcha_staleness` | Verify gotcha file references |",
8663
8955
  "",
8664
8956
  "---",
8665
8957
  "",
8666
- "## \u{1F4CA} Knowledge Graph \u2014 Node Types & Shapes",
8667
- "",
8668
- "Kuma's knowledge graph visualizes your project structure. Each node type has a unique shape:",
8669
- "",
8670
- "| Shape | Node Type | Description | Auto/Manual |",
8671
- "|-------|-----------|-------------|-------------|",
8672
- "| \u25A2 `box` | function | Functions (named/arrow) | AUTO by scanner |",
8673
- "| \u2B21 `hexagon` | class | Classes (TS/JS) | AUTO by scanner |",
8674
- "| \u25C7 `diamond` | component | React/Vue components | AUTO by scanner |",
8675
- "| \u25A3 `square` | file | Source files | AUTO by scanner |",
8676
- "| \u25B2 `triangle` | api_route | API routes | AUTO by scanner |",
8677
- "| \u2B52 `star` | test | Test files/describes | AUTO by scanner |",
8678
- "| \u25CB `ellipse` | module | Directory modules | AUTO by scanner |",
8679
- "| \u{1F5C4}\uFE0F `cylinder` | db_table | Database tables | AUTO by scanner |",
8680
- "| \u{1F537} `diamond` | interface | TypeScript interfaces | AUTO by scanner |",
8681
- "| \u{1F538} `small_diamond` | type | Type aliases | AUTO by scanner |",
8682
- "| \u{1F539} `small_square` | variable | Variables/constants | AUTO by scanner |",
8683
- "| \u{1F504} `circle` | workflow | Workflow definitions | AUTO by scanner |",
8684
- "| \u{1F4C4} `note` | context | Manual context notes | MANUAL \u2014 kuma_memory context |",
8685
- "| \u2B50 `star` | feature | High-level feature (Authentication, Billing) | MANUAL \u2014 kuma_memory feature |",
8686
- "| \u{1F3DB}\uFE0F `pillar` | feature_domain | High-level feature domain anchor | MANUAL \u2014 kuma_memory arch_flow (domain:) |",
8687
- "| \u{1F517} `link` | cross_service_link | Cross-service/inter-file flow step | MANUAL \u2014 kuma_memory arch_flow (hops:) |",
8688
- "| \u{1F3F7}\uFE0F `tag` | domain_rule | Business rules (Layer 1) | MANUAL \u2014 kuma_memory domain_rules |",
8689
- "| \u{1F500} `flow` | arch_flow | Architecture flows (Layer 2) | MANUAL \u2014 kuma_memory arch_flow |",
8690
- "| \u26A0\uFE0F `warning` | gotcha | Known gotchas (Layer 3) | MANUAL \u2014 kuma_memory gotcha |",
8691
- "| \u{1F4CC} `milestone` | decision | Architecture decisions | MANUAL \u2014 kuma_memory decision |",
8692
- "| \u2705 `checklist` | todo | Persistent todos | MANUAL \u2014 kuma_memory todo |",
8693
- "",
8694
- "**Edges (connections between nodes):**",
8695
- "- `imports` \u2014 file imports another (AUTO)",
8696
- "- `calls` \u2014 function calls another (AUTO)",
8697
- "- `defines` \u2014 file defines function/class (AUTO)",
8698
- "- `depends_on` \u2014 logical dependency (AUTO + MANUAL)",
8699
- "- `extends` \u2014 class extends class (AUTO)",
8700
- "- `contains` \u2014 file contains function (AUTO)",
8701
- "- `composes` \u2014 component contains sub-component (AUTO)",
8702
- "- `owns` \u2014 domain/file owns file (MANUAL \u2014 arch_flow, feature)",
8703
- "- `affects` \u2014 impact propagation chain (MANUAL \u2014 arch_flow, feature)",
8704
- "- `flows_through` \u2014 flow step to next step (MANUAL \u2014 arch_flow)",
8705
- "- `triggers` \u2014 flow triggers action (MANUAL)",
8706
- "- `syncs_with` \u2014 cross-service sync (MANUAL)",
8707
- "- `modified_by` \u2014 file modified by action (AUTO)",
8708
- "- `references` \u2014 node references context note (MANUAL)",
8958
+ "## \u{1F9E0} Session Memory",
8709
8959
  "",
8710
- "---",
8711
- "",
8712
- "## \u{1F4CC} Workflow Summary",
8713
- "",
8714
- "```",
8715
- "EVERY SESSION (Mandatory \u2014 including research-only):",
8716
- " 1. kuma_context \u2192 init \u2014 Load project context",
8717
- " 2. kuma_safety \u2192 guard \u2014 Safety check",
8718
- " 3. kuma_context \u2192 research \u2014 Research target area",
8719
- " --- EDIT/READ (use native tools) ---",
8720
- " 5. kuma_memory \u2192 research_save \u2014 After exploring area (creates search cache)",
8721
- " 6. kuma_memory \u2192 gotcha \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)",
8722
- " 7. kuma_memory \u2192 arch_flow \u2014 After tracing COMPLETE flow (max 5 core files)",
8723
- " 8. kuma_memory \u2192 feature \u2014 \u2B50 When discovering a module/feature boundary (Authentication, Billing)",
8724
- " 9. kuma_memory \u2192 decision \u2014 When choosing (preserves rationale)",
8725
- " 10. kuma_safety \u2192 verify \u2014 Run tests / confirm nothing broken",
8726
- " 11. kuma_context \u2192 changes \u2014 Review session activity",
8727
- "",
8728
- "\u{1F525} `arch_flow` + `gotcha` are EXPONENTIAL \u2014 each record saves 5-10 files next session.",
8729
- "\u2B50 `feature` captures module ownership \u2014 use when you identify a cohesive feature boundary.",
8730
- "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
8731
- "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
8732
- "",
8733
- "ON DISCOVERY (When needed):",
8734
- " kuma_memory \u2192 feature \u2014 Record high-level feature (Authentication, Billing)",
8735
- " kuma_memory \u2192 domain_rules \u2014 Business rules (Layer 1)",
8736
- " kuma_memory \u2192 context \u2014 Additional notes",
8737
- " kuma_memory \u2192 todo \u2014 Task management",
8738
- " kuma_memory \u2192 mine \u2014 Historical context",
8739
- " kuma_memory \u2192 add_node \u2014 Manual function/class/component node (for visualize)",
8740
- "",
8741
- "GRAPH-AS-RETRIEVAL (search now includes):",
8742
- " \u2022 Task-focused retrieval: top N relevant nodes for a task description",
8743
- " \u2022 Impact analysis: which nodes are affected by a change",
8744
- " \u2022 arch_flow now auto-creates affects edges for impact propagation",
8745
- "```",
8960
+ "Session memory tracks tool calls, recordings, and tool health across the session.",
8961
+ "It detects loops (10+ identical calls), suppresses cleanup noise, and provides context for guard checks.",
8746
8962
  "",
8747
- "## \u{1F9E0} How the Scanner Works",
8748
- "",
8749
- "Kuma's code scanner (`kumaCodeScanner.ts`) uses **lightweight regex-based pattern matching**",
8750
- "for **cold start** \u2014 giving new agents basic project structure on first session.",
8751
- "It is NOT a full AST parser (no Babel/TypeScript compiler dependency).",
8752
- "",
8753
- "**Scope:** TS/JS/TSX/JSX only. Non-TypeScript projects (Go, Ruby, Python, Rust, PHP, etc.)",
8754
- "rely entirely on **AI inline recording** \u2014 the agent records what it discovers as it works.",
8755
- "",
8756
- "**What auto-scanner detects (line-by-line regex, TS/JS only):**",
8757
- "- `function` declarations (named + async + generator)",
8758
- "- Arrow function assignments (`const foo = (...) => ...`)",
8759
- "- Typed arrow functions (`const foo: Type = (...) => ...`)",
8760
- "- Class declarations (includes extends/implements)",
8761
- "- React/Vue components (functions returning JSX)",
8762
- "- API route handlers (Express + Hono patterns)",
8763
- "- Import statements (local file imports only)",
8764
- "- Function calls cross-file (`calls` edges)",
8765
- "- Test files (`.test.`, `.spec.`, `__tests__/`)",
8766
- "",
8767
- "**Known limitations:**",
8768
- "- Regex-based: can miss multi-line constructs or non-standard patterns",
8769
- "- Complex generics or deeply nested syntax may not match",
8770
- "- TypeScript advanced types may be partially parsed",
8771
- "- Scanner runs during `kuma_context research` step 3 (max 200 files)",
8772
- "- If scanner returns 0 structural nodes, the regex patterns didn't match your code style",
8773
- "",
8774
- "**Key principle: Scanner = cold start, Inline Recording = primary.**",
8775
- "The scanner populates initial graph structure. Every subsequent session, the agent's",
8776
- "inline recording (research_save, gotcha, arch_flow, decision) enriches the graph further.",
8777
- "The more you use Kuma, the richer the graph becomes \u2014 persistent across sessions.",
8963
+ "\u{1F4CA} **Metrics Tracked:**",
8964
+ "- Tool call count & unique tools used",
8965
+ "- Recording counts: arch_flows, gotchas, decisions, research_saves",
8966
+ "- Error detection from bash/test output",
8967
+ "- Loop detection: identical tool+params called 10+ times",
8778
8968
  "",
8779
8969
  "---",
8780
8970
  "",
8781
- "## \u{1F4BE} How to Retrieve Past Research \u2014 research_save \u2192 search",
8971
+ "## \u{1F4CF} Guard Anti-Patterns",
8782
8972
  "",
8783
- "Kuma stores knowledge in two places. The primary retrieval method for AI agents",
8784
- "is **`search`**, not `visualize`. Visualize is for human debugging, not agent consumption.",
8973
+ "The guard checks for common anti-patterns before editing:",
8785
8974
  "",
8786
- "**Flow: research_save \u2192 search (for agents)**",
8787
- '- Save: `kuma_memory({ action: "research_save", scope: "<file or area>", confidence: 0.8 })`',
8788
- '- Retrieve: `kuma_memory({ action: "search", query: "<term>" })`',
8789
- '- Auto-load: `kuma_context({ action: "research", scope: "<area>" })`',
8790
- '- List cache: `kuma_context({ action: "researches" })`',
8791
- "",
8792
- "**Key design:** `research_save` now creates BOTH research cache entries AND",
8793
- "knowledge graph nodes (file + research scope). This means `search` finds",
8794
- "past research even if the graph scanner didn't detect your code style.",
8795
- "",
8796
- "**Quick retrieval tips:**",
8797
- '- `kuma_memory({ action: "search", query: "<term>" })` \u2014 **primary method**. Finds past research + graph nodes + codebase',
8798
- '- `kuma_context({ action: "research", scope: "<area>" })` \u2014 deep 5-step research, auto-loads past cache',
8799
- '- `kuma_context({ action: "init" })` \u2014 shows graph stats + recent research + health score',
8800
- '- `kuma_context({ action: "drift" })` \u2014 check if memories are stale vs actual code',
8801
- '- `kuma_context({ action: "changes" })` \u2014 view session activity log',
8802
- '- `kuma_context({ action: "visualize" })` \u2014 (human debugging) Mermaid diagram of the graph',
8975
+ "1. **Missing Research** \u2014 editing unfamiliar code without research",
8976
+ "2. **Missing Recordings** \u2014 many tool calls with 0 recordings (knowledge loss)",
8977
+ "3. **AST Anti-Patterns** \u2014 editing generated files, test snapshots, dist directories",
8978
+ "4. **Scope Mismatch** \u2014 editing files outside declared scope",
8979
+ "5. **Loop Detection** \u2014 same tool+params called 10+ times (param variation < 5)",
8803
8980
  "",
8804
8981
  "---",
8805
8982
  "",
8806
- "## \u{1F52C} How Verification Works",
8983
+ "## \u{1F5D1}\uFE0F Graph Management",
8807
8984
  "",
8808
- '`kuma_safety({ action: "verify", scope: "<area>" })` \u2014 STEP 9',
8985
+ '**Delete:** `kuma_memory({ action: "delete_node", target: "<node_id_or_type>" })`',
8986
+ "- Deletes from graph nodes, edges, AND known_gotchas table (unified delete)",
8809
8987
  "",
8810
- "**Auto-detection logic:**",
8811
- "1. Checks `package.json` \u2192 `scripts.test` \u2192 uses npm/pnpm/yarn test",
8812
- "2. If no test script, checks for Makefile with `test:` target",
8813
- "3. Falls back to Python (pytest), Rust (cargo test), Go (go test)",
8814
- "4. Last resort: `npx tsc --noEmit` (TypeScript syntax check) or `node -c`",
8815
- "5. If nothing found: returns informative message instead of failing",
8988
+ '**Clear entire graph:** `kuma_memory({ action: "clear" })`',
8816
8989
  "",
8817
- "**Important:** Verification is NEVER auto-triggered. You must call it explicitly.",
8818
- "The 5-layer safety guard (concurrency lock, rate limiting, staleness cache,",
8819
- "runaway detection, hard timeout) prevents resource exhaustion.",
8990
+ '**Self-heal:** `kuma_safety({ action: "gotcha_staleness" })`',
8991
+ "- Verifies gotcha file/symbol references still exist",
8992
+ "- Removes obsolete gotchas (deleted files)",
8820
8993
  "",
8821
8994
  "---",
8822
8995
  "",
8823
- "## \u{1F6AB} What Kuma Does NOT Do (Use Agent Native Tools)",
8824
- "",
8825
- "| Function | Correct Tool |",
8826
- "|----------|-------------|",
8827
- "| Edit files | Agent native edit tools |",
8828
- "| Search code | Agent native grep/search |",
8829
- "| Run commands | Agent native terminal |",
8830
- "| Create files | Agent native file creation |",
8831
- "| Git operations | Agent native git tools |",
8832
- "| Lint/test | Agent native run commands |",
8833
- "",
8834
- "---",
8835
- "_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
8996
+ "_Generated by Kuma MCP_"
8836
8997
  ].join("\n");
8837
8998
  }
8838
8999
  function handleOpencodeSecondary(root, results) {
@@ -8847,28 +9008,19 @@ function handleOpencodeSecondary(root, results) {
8847
9008
  "---",
8848
9009
  "",
8849
9010
  "Kuma MCP tools are installed (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
8850
- '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context and session memory.',
8851
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
9011
+ '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
8852
9012
  "",
8853
- "\u26A0\uFE0F **MANDATORY WORKFLOW** \u2014 Follow in order every session (including research-only):",
9013
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
9014
+ ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
9015
+ " 2. *(edit/read using native tools)*",
9016
+ ' 3. `kuma_kuma_context({ action: "changes" })` \u2014 Review session',
8854
9017
  "",
8855
- ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8856
- ' 2. `kuma_kuma_safety({ action: "guard" })` \u2014 Safety check before work',
8857
- ' 3. `kuma_kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
8858
- " 4. *(edit/read using native tools)*",
8859
- ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
8860
- ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)',
8861
- ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 After tracing COMPLETE flow, max 5 core files',
8862
- ' 8. `kuma_kuma_memory({ action: "decision" })` \u2014 When choosing (preserves rationale)',
8863
- ' 9. `kuma_kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
8864
- ' 10. `kuma_kuma_context({ action: "changes" })` \u2014 Review session activity',
9018
+ "\u{1F4DD} **RECORD when needed:**",
9019
+ ' \u2022 Bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` | Flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` | Decision? \u2192 `kuma_kuma_memory({ action: "decision" })`',
8865
9020
  "",
8866
- "\u{1F525} `arch_flow` + `gotcha` = EXPONENTIAL value. Each record saves 5-10 files next session.",
8867
- "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
8868
- "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
9021
+ "\u{1F527} **ON-DEMAND:** guard (risky ops) | research (unfamiliar code) | verify (after edits)",
8869
9022
  "",
8870
- "\u26A0\uFE0F **OpenCode platform note:** Tool names are prefixed with `kuma_kuma_*`",
8871
- " (server name `kuma` + already-prefixed tool name `kuma_context`).",
9023
+ "\u26A0\uFE0F **OpenCode note:** Tool names use `kuma_kuma_*` prefix.",
8872
9024
  "",
8873
9025
  "\u{1F4D6} Full rules: `.kuma/init.md`"
8874
9026
  ].join("\n");
@@ -8976,29 +9128,24 @@ function getCombinedAgentsMd(selectedTypes) {
8976
9128
  "",
8977
9129
  "---",
8978
9130
  "",
8979
- '> \u26A0\uFE0F **WARNING:** Agent often skip steps due to "research-only" bias. Do NOT skip.',
8980
- "> Follow the full 10-step workflow even for read-only investigations.",
9131
+ "## SESSION WORKFLOW",
8981
9132
  "",
8982
- "## \u26A0\uFE0F MANDATORY WORKFLOW",
9133
+ "**DEFAULT: 3-Step Lean Workflow** (for most tasks):",
8983
9134
  "",
8984
- "Follow these steps in order every session (including research-only):",
9135
+ ' 1. `[kuma_]context({ action: "init" })` \u2014 Load context (START HERE)',
9136
+ " 2. *(edit/read using native tools)*",
9137
+ ' 3. `[kuma_]context({ action: "changes" })` \u2014 Review session',
8985
9138
  "",
8986
- ' 1. `[kuma_]context({ action: "init" })` \u2014 Load project context (START HERE)',
8987
- ' 2. `[kuma_]safety({ action: "guard" })` \u2014 Safety check before editing',
8988
- ' 3. `[kuma_]context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
8989
- " 4. *(edit/read using native tools)*",
8990
- ' 5. `[kuma_]memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
8991
- ' 6. `[kuma_]memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)',
8992
- ' 7. `[kuma_]memory({ action: "arch_flow" })` \u2014 After tracing COMPLETE flow (max 5 core files)',
8993
- ' 8. `[kuma_]memory({ action: "decision" })` \u2014 When choosing (preserves rationale)',
8994
- ' 9. `[kuma_]safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
8995
- ' 10. `[kuma_]context({ action: "changes" })` \u2014 Review session activity',
9139
+ "**Record only when something important happens:**",
9140
+ '- Found a bug/quirk? \u2192 `[kuma_]memory({ action: "gotcha" })` IMMEDIATELY',
9141
+ '- Traced a complete flow? \u2192 `[kuma_]memory({ action: "arch_flow" })` (max 5 core files)',
9142
+ '- Chose between options? \u2192 `[kuma_]memory({ action: "decision" })`',
8996
9143
  "",
8997
- "\u{1F525} `arch_flow` + `gotcha` = EXPONENTIAL value. Each record saves 5-10 files next session.",
8998
- "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
8999
- "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
9144
+ "**Safety checks (use when appropriate):**",
9145
+ '- Before risky edits: `[kuma_]safety({ action: "guard", guardGoal: "..." })`',
9146
+ '- After edits: `[kuma_]safety({ action: "verify", scope: "<area>" })`',
9147
+ '- Before research: `[kuma_]context({ action: "research", scope: "<area>" })`',
9000
9148
  "",
9001
- "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
9002
9149
  "\u{1F5D1}\uFE0F **Delete/Clear:** `delete_node` (delete node/gotcha/todo/decision) | `clear` (wipe entire graph)",
9003
9150
  "",
9004
9151
  "Replace `[kuma_]` with the correct prefix for your platform (see below).",
@@ -9618,31 +9765,25 @@ var init_init = __esm({
9618
9765
  };
9619
9766
  BOOTSTRAP_LINES = [
9620
9767
  "Kuma MCP tools are installed (kuma_context, kuma_memory, kuma_safety).",
9621
- '**Before coding, call `kuma_context({ action: "init" })`** to load project context and session memory.',
9622
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
9768
+ '**Before coding, call `kuma_context({ action: "init" })`** to load project context.',
9623
9769
  "",
9624
- "\u26A0\uFE0F **MANDATORY WORKFLOW** \u2014 Follow in order every session (including research-only):",
9770
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
9771
+ ' 1. `kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
9772
+ " 2. *(edit/read using native tools)*",
9773
+ ' 3. `kuma_context({ action: "changes" })` \u2014 Review session',
9625
9774
  "",
9626
- ' 1. `kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
9627
- ' 2. `kuma_safety({ action: "guard" })` \u2014 Safety check before work',
9628
- ' 3. `kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
9629
- " 4. *(edit/read using native tools)*",
9630
- ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
9631
- ' 6. `kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential value)',
9632
- ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 After tracing COMPLETE flow (max 5 files)',
9633
- ' 8. `kuma_memory({ action: "decision" })` \u2014 When choosing between options (preserves rationale)',
9634
- ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
9635
- ' 10. `kuma_context({ action: "changes" })` \u2014 Review session activity',
9775
+ "\u{1F4DD} **RECORD when needed (don't force it):**",
9776
+ ' \u2022 Found a bug? \u2192 `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
9777
+ ' \u2022 Traced a flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 files)',
9778
+ ' \u2022 Chose between options? \u2192 `kuma_memory({ action: "decision" })`',
9636
9779
  "",
9637
- "\u{1F525} **Power Curve:** `arch_flow` + `gotcha` are EXPONENTIAL \u2014 arch_flow records complete flow (max 5 core files), gotcha records bugs inline.",
9638
- " The more you record, the more overpowered the agent becomes. These are the highest-value actions.",
9639
- "\u{1F7E2} **SKIP** recording function/class/component nodes \u2014 grep/glob is faster.",
9640
- "\u{1F9E0} Knowledge graph is persistent \u2014 nodes/edges accumulate across sessions, getting richer over time.",
9780
+ "\u{1F527} **ON-DEMAND (use when appropriate):**",
9781
+ ' \u2022 `kuma_safety({ action: "guard" })` \u2014 before risky ops (deletions, refactors)',
9782
+ ' \u2022 `kuma_context({ action: "research" })` \u2014 before editing unfamiliar code',
9783
+ ' \u2022 `kuma_safety({ action: "verify" })` \u2014 after edits to confirm nothing broken',
9641
9784
  "",
9642
- "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
9643
- "\u{1F5D1}\uFE0F **Delete/Clear:** `delete_node` (delete node/gotcha/todo/decision) | `clear` (wipe entire graph)",
9644
- "\u{1F4D6} Full rules: `.kuma/init.md`",
9645
- "\u{1F9E0} Tools: `kuma_context` | `kuma_memory` | `kuma_safety`"
9785
+ "\u{1F5D1}\uFE0F **Delete/Clear:** `delete_node` | `clear`",
9786
+ "\u{1F4D6} Full rules: `.kuma/init.md`"
9646
9787
  ].join("\n");
9647
9788
  KUMA_CORE_INSTRUCTIONS = BOOTSTRAP_LINES;
9648
9789
  TEMPLATES = {
@@ -10150,34 +10291,34 @@ var init_skillGenerator = __esm({
10150
10291
  BOOTSTRAP = [
10151
10292
  "Kuma MCP tools are available (kuma_context, kuma_memory, kuma_safety).",
10152
10293
  '**Before coding, call `kuma_context({ action: "init" })`** to load project context.',
10153
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
10154
10294
  "",
10155
- "\u26A1 **ADAPTIVE WORKFLOW (Lean Mode by Default):**",
10156
- " \u2022 **Lean Mode (Default, < 3 files):** `init` \u2192 edit/read \u2192 record (`decision`/`gotcha`/`feature` if needed)",
10157
- " \u2022 **Standard Mode (3-10 files/unfamiliar):** `init` \u2192 `guard` \u2192 `research` \u2192 edit \u2192 `record` \u2192 `verify`",
10158
- " \u2022 **Full Mode (> 10 files/refactor):** `init` \u2192 `guard` \u2192 `research` \u2192 `impact` \u2192 edit \u2192 `record` \u2192 `verify` \u2192 `changes`",
10295
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
10296
+ " 1. `init` \u2014 load context",
10297
+ " 2. *(edit/read using native tools)*",
10298
+ " 3. `changes` \u2014 review session",
10159
10299
  "",
10160
- "\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow), `feature` (high-level module ownership)",
10161
- "\u{1F7E2} **SKIP RECORDING:** function, class, import, route (use native grep/glob instead)",
10300
+ "\u{1F4DD} **RECORD when needed (don't force it):**",
10301
+ ' \u2022 Found a bug? \u2192 `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
10302
+ ' \u2022 Traced a flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 files)',
10303
+ ' \u2022 Chose between options? \u2192 `kuma_memory({ action: "decision" })`',
10162
10304
  "",
10163
- "\u{1F4D6} Full rules & modes: `.kuma/init.md` | `.kuma/MODE.md` | `.kuma/SKIP_RULES.md`",
10164
- "\u{1F9E0} Tools: `kuma_context` | `kuma_memory` | `kuma_safety`"
10305
+ "\u{1F4D6} Full rules: `.kuma/init.md`"
10165
10306
  ].join("\n");
10166
10307
  BOOTSTRAP_OPENCODE = [
10167
10308
  "Kuma MCP tools are available (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
10168
10309
  '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
10169
- "Project knowledge persists in `.kuma/memories/*.md` across sessions.",
10170
10310
  "",
10171
- "\u26A1 **ADAPTIVE WORKFLOW (Lean Mode by Default):**",
10172
- ' \u2022 **Lean Mode (Default, < 3 files):** `kuma_kuma_context({ action: "init" })` \u2192 edit/read \u2192 record (`decision`/`gotcha`)',
10173
- " \u2022 **Standard Mode (3-10 files/unfamiliar):** `init` \u2192 `guard` \u2192 `research` \u2192 edit \u2192 `record` \u2192 `verify`",
10174
- " \u2022 **Full Mode (> 10 files/refactor):** `init` \u2192 `guard` \u2192 `research` \u2192 `impact` \u2192 edit \u2192 `record` \u2192 `verify` \u2192 `changes`",
10311
+ "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
10312
+ " 1. `init` \u2014 load context",
10313
+ " 2. *(edit/read using native tools)*",
10314
+ " 3. `changes` \u2014 review session",
10175
10315
  "",
10176
- "\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow), `feature` (high-level module ownership)",
10177
- "\u{1F7E2} **SKIP RECORDING:** function, class, import, route (use native grep/glob instead)",
10316
+ "\u{1F4DD} **RECORD when needed (don't force it):**",
10317
+ ' \u2022 Found a bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
10318
+ ' \u2022 Traced a flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
10319
+ ' \u2022 Chose between options? \u2192 `kuma_kuma_memory({ action: "decision" })`',
10178
10320
  "",
10179
- "\u{1F4D6} Full rules & modes: `.kuma/init.md` | `.kuma/MODE.md` | `.kuma/SKIP_RULES.md`",
10180
- "\u{1F9E0} Tools: `kuma_kuma_context` | `kuma_kuma_memory` | `kuma_kuma_safety`"
10321
+ "\u{1F4D6} Full rules: `.kuma/init.md`"
10181
10322
  ].join("\n");
10182
10323
  }
10183
10324
  });
@@ -10199,7 +10340,7 @@ init_kumaMemory();
10199
10340
  init_pathValidator();
10200
10341
  import fs11 from "fs";
10201
10342
  import path11 from "path";
10202
- import crypto3 from "crypto";
10343
+ import crypto4 from "crypto";
10203
10344
  var CONTEXT_ALIASES = {
10204
10345
  // Research synonyms
10205
10346
  "research": "research",
@@ -10677,7 +10818,7 @@ function computeProjectHash(scope) {
10677
10818
  try {
10678
10819
  const root = getProjectRoot();
10679
10820
  const files = fs11.readdirSync(root).slice(0, 20);
10680
- const hash = crypto3.createHash("md5");
10821
+ const hash = crypto4.createHash("md5");
10681
10822
  hash.update(scope);
10682
10823
  for (const f of files) {
10683
10824
  try {
@@ -10712,8 +10853,10 @@ var ALLOWED_NODE_TYPES = /* @__PURE__ */ new Set([
10712
10853
  // High-level feature domains
10713
10854
  "file",
10714
10855
  // File references (needed for graph connectivity)
10715
- "research"
10856
+ "research",
10716
10857
  // Research cache nodes (needed for search)
10858
+ "flow_explanation"
10859
+ // Prose explanation of arch_flow (semantic layer)
10717
10860
  ]);
10718
10861
  function isNodeTypeAllowed(type) {
10719
10862
  return ALLOWED_NODE_TYPES.has(type);
@@ -10747,6 +10890,7 @@ function getNoiseFilterPolicy() {
10747
10890
 
10748
10891
  // src/tools/kumaMemoryTool.ts
10749
10892
  init_pathValidator();
10893
+ init_pathValidator();
10750
10894
  import fs15 from "fs";
10751
10895
  import path15 from "path";
10752
10896
  var MEMORY_ALIASES = {
@@ -10798,6 +10942,13 @@ var MEMORY_ALIASES = {
10798
10942
  "notes": "context",
10799
10943
  "note": "context",
10800
10944
  "inject": "context",
10945
+ // Harvest synonyms
10946
+ "harvest": "harvest",
10947
+ "session_mining": "harvest",
10948
+ // Session mine synonyms
10949
+ "session_mine": "session_mine",
10950
+ "mine-session": "session_mine",
10951
+ "extract-session": "session_mine",
10801
10952
  "context-note": "context",
10802
10953
  // Benchmark synonyms
10803
10954
  "benchmark": "benchmark",
@@ -10856,7 +11007,6 @@ var MEMORY_ALIASES = {
10856
11007
  "reset": "clear",
10857
11008
  "purge-all": "clear",
10858
11009
  // Harvest synonyms (Pilar 5)
10859
- "harvest": "harvest",
10860
11010
  "git-harvest": "harvest",
10861
11011
  "auto-harvest": "harvest",
10862
11012
  "git": "harvest",
@@ -10918,6 +11068,10 @@ async function handleMemory(params) {
10918
11068
  }
10919
11069
  case "harvest":
10920
11070
  return handleHarvest(params);
11071
+ case "mine":
11072
+ return handleMine(params);
11073
+ case "session_mine":
11074
+ return handleSessionMine(params);
10921
11075
  case "noise_policy":
10922
11076
  return getNoiseFilterPolicy();
10923
11077
  default:
@@ -10957,7 +11111,8 @@ Use kuma_memory({ action: 'decision', title: '...', context: '...', rationale: '
10957
11111
  }
10958
11112
  }
10959
11113
  async function handleResearchSave(params) {
10960
- const scope = params.scope || "project";
11114
+ const rawScope = params.scope || "project";
11115
+ const scope = normalizeScope(rawScope) || rawScope;
10961
11116
  const record = params.record || JSON.stringify({
10962
11117
  scope,
10963
11118
  confidence: params.confidence || 0.8,
@@ -10968,16 +11123,24 @@ async function handleResearchSave(params) {
10968
11123
  try {
10969
11124
  const { getDb: getDb2, flushDb: flushDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
10970
11125
  const db = await getDb2();
10971
- const fileId = `file::${scope}`;
10972
11126
  const researchId = `research::${scope}`;
10973
11127
  const now = Math.floor(Date.now() / 1e3);
10974
- db.run(`
10975
- INSERT INTO nodes (id, type, name, file_path, metadata, updated_at)
10976
- VALUES (?, 'file', ?, ?, ?, ?)
10977
- ON CONFLICT(id) DO UPDATE SET
10978
- metadata = json_patch(COALESCE(nodes.metadata, '{}'), excluded.metadata),
10979
- updated_at = excluded.updated_at
10980
- `, [fileId, scope, scope, JSON.stringify({ findings: [params.content || ""], confidence: params.confidence || 0.8 }), now]);
11128
+ const looksLikeFile = scope.includes("/") || /\.\w{1,5}$/.test(scope);
11129
+ if (looksLikeFile) {
11130
+ const fileId = `file::${scope}`;
11131
+ db.run(`
11132
+ INSERT INTO nodes (id, type, name, file_path, metadata, updated_at)
11133
+ VALUES (?, 'file', ?, ?, ?, ?)
11134
+ ON CONFLICT(id) DO UPDATE SET
11135
+ metadata = json_patch(COALESCE(nodes.metadata, '{}'), excluded.metadata),
11136
+ updated_at = excluded.updated_at
11137
+ `, [fileId, scope, scope, JSON.stringify({ findings: [params.content || ""], confidence: params.confidence || 0.8 }), now]);
11138
+ db.run(`
11139
+ INSERT INTO edges (source_id, target_id, type, weight, metadata, created_at)
11140
+ VALUES (?, ?, 'contains', 1.0, '{}', ?)
11141
+ ON CONFLICT DO NOTHING
11142
+ `, [fileId, researchId, now]);
11143
+ }
10981
11144
  db.run(`
10982
11145
  INSERT INTO nodes (id, type, name, file_path, metadata, updated_at)
10983
11146
  VALUES (?, 'research', ?, null, ?, ?)
@@ -10985,12 +11148,12 @@ async function handleResearchSave(params) {
10985
11148
  metadata = json_patch(COALESCE(nodes.metadata, '{}'), excluded.metadata),
10986
11149
  updated_at = excluded.updated_at
10987
11150
  `, [researchId, `research:${scope}`, JSON.stringify({ confidence: params.confidence || 0.8, last_findings: params.content || "" }), now]);
10988
- db.run(`
10989
- INSERT INTO edges (source_id, target_id, type, weight, metadata, created_at)
10990
- VALUES (?, ?, 'contains', 1.0, '{}', ?)
10991
- ON CONFLICT DO NOTHING
10992
- `, [fileId, researchId, now]);
10993
11151
  flushDb2(db);
11152
+ try {
11153
+ const { rebuildFtsIndex: rebuildFtsIndex2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
11154
+ rebuildFtsIndex2();
11155
+ } catch {
11156
+ }
10994
11157
  } catch {
10995
11158
  }
10996
11159
  try {
@@ -11232,6 +11395,30 @@ async function handleLayerAction(layer, params) {
11232
11395
  const { recordDomainFlow: recordDomainFlow2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
11233
11396
  const flow = await recordDomainFlow2({ domain, hops, gotchas, decisions, filePaths });
11234
11397
  await writeLayer2("arch_flow", content);
11398
+ try {
11399
+ const { upsertNode: upsertNode2, addEdge: addEdge2, nodeId: gNodeId } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
11400
+ const proseId = `flow_explanation::${domain}::prose`;
11401
+ const prose = [
11402
+ `This is the ${domain} flow.`,
11403
+ `It starts at ${hops[0]?.from || "entry point"}.`,
11404
+ ...hops.map((h, i) => `Step ${i + 1}: ${h.from} \u2192 ${h.to} (${h.description || h.relation})`),
11405
+ gotchas.length > 0 ? `Watch out for: ${gotchas.join(", ")}` : "",
11406
+ decisions.length > 0 ? `Decisions made: ${decisions.join(", ")}` : ""
11407
+ ].filter(Boolean).join(" ");
11408
+ await upsertNode2({
11409
+ id: proseId,
11410
+ type: "flow_explanation",
11411
+ name: `prose:${domain}`,
11412
+ metadata: { prose, hopsCount: hops.length, domain }
11413
+ });
11414
+ await addEdge2({
11415
+ sourceId: proseId,
11416
+ targetId: gNodeId("feature_domain", domain),
11417
+ type: "explains",
11418
+ metadata: { autoGenerated: true }
11419
+ });
11420
+ } catch {
11421
+ }
11235
11422
  sessionMemory.recordMemoryAction("arch_flow");
11236
11423
  return `\u2705 Domain flow "${domain}" recorded \u2014 ${flow.nodeCount} nodes, ${flow.edgeCount} edges created.
11237
11424
  \u{1F3DB}\uFE0F **Domain Anchor:** ${domain}
@@ -11270,9 +11457,10 @@ async function handleGotchaAction(params) {
11270
11457
 
11271
11458
  \u2705 Correct: kuma_memory({ action: "gotcha", scope: "path/to/file.ts", content: "bug description", status: "high" })`;
11272
11459
  }
11460
+ const normalizedScope = normalizeScope(params.scope) || params.scope;
11273
11461
  const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
11274
11462
  const result = await addGotcha2({
11275
- filePath: params.scope,
11463
+ filePath: normalizedScope,
11276
11464
  description: params.content,
11277
11465
  severity: params.status || "medium",
11278
11466
  workaround: params.description
@@ -11297,9 +11485,24 @@ async function handleDeleteNode(params) {
11297
11485
  switch (params.scope) {
11298
11486
  case "gotcha":
11299
11487
  case "gotchas": {
11488
+ const gotchaRow = db.prepare("SELECT file_path, description FROM known_gotchas WHERE id = ?");
11489
+ gotchaRow.bind([id]);
11490
+ let gotchaFilePath = "";
11491
+ let gotchaDesc = "";
11492
+ if (gotchaRow.step()) {
11493
+ const row = gotchaRow.getAsObject();
11494
+ gotchaFilePath = row.file_path;
11495
+ gotchaDesc = row.description;
11496
+ }
11497
+ gotchaRow.free();
11300
11498
  db.run("DELETE FROM known_gotchas WHERE id = ?", [id]);
11499
+ if (gotchaFilePath && gotchaDesc) {
11500
+ const gotchaNodeId = `gotcha::${gotchaFilePath}::${gotchaDesc.substring(0, 30)}`;
11501
+ db.run("DELETE FROM edges WHERE source_id = ? OR target_id = ?", [gotchaNodeId, gotchaNodeId]);
11502
+ db.run("DELETE FROM nodes WHERE id = ?", [gotchaNodeId]);
11503
+ }
11301
11504
  saveDb2(db);
11302
- return `\u{1F5D1}\uFE0F **Gotcha #${id} deleted.**`;
11505
+ return `\u{1F5D1}\uFE0F **Gotcha #${id} deleted** (table + graph).`;
11303
11506
  }
11304
11507
  case "todo":
11305
11508
  case "todos": {
@@ -11337,6 +11540,15 @@ async function handleDeleteNode(params) {
11337
11540
  `gotcha::${domainName}::%`
11338
11541
  ]);
11339
11542
  db.run("DELETE FROM edges WHERE source_id LIKE ? OR target_id LIKE ?", [`%${domainName}%`, `%${domainName}%`]);
11543
+ } else if (targetId.startsWith("gotcha::")) {
11544
+ db.run("DELETE FROM edges WHERE source_id = ? OR target_id = ?", [targetId, targetId]);
11545
+ db.run("DELETE FROM nodes WHERE id = ?", [targetId]);
11546
+ const parts = targetId.split("::");
11547
+ if (parts.length >= 3) {
11548
+ const gotchaFilePath = parts[1];
11549
+ const gotchaDesc = parts.slice(2).join("::");
11550
+ db.run("DELETE FROM known_gotchas WHERE file_path = ? AND description LIKE ?", [gotchaFilePath, `${gotchaDesc}%`]);
11551
+ }
11340
11552
  } else {
11341
11553
  db.run("DELETE FROM edges WHERE source_id = ? OR target_id = ?", [targetId, targetId]);
11342
11554
  db.run("DELETE FROM nodes WHERE id = ?", [targetId]);
@@ -11361,9 +11573,10 @@ async function handleDeleteNode(params) {
11361
11573
  async function handleAddNode(params) {
11362
11574
  const type = params.title;
11363
11575
  const name = params.content;
11364
- const filePath = params.scope;
11576
+ const rawFilePath = params.scope;
11577
+ const filePath = rawFilePath ? normalizeScope(rawFilePath) || rawFilePath : void 0;
11365
11578
  if (!type || !name) {
11366
- return "\u26A0\uFE0F `title` (node type) and `content` (node name) required.\n\n\u2705 Allowed types: arch_flow, gotcha, decision, cross_service_link, feature_domain, file, research\n\u274C Rejected: function, class, component, variable, method\n\nExample: kuma_memory({ action: 'arch_flow', content: 'domain: AuthFlow | hops: auth.ts \u2192 middleware.ts' })";
11579
+ return "\u26A0\uFE0F `title` (node type) and `content` (node name) required.\n\n\u2705 Allowed types: arch_flow, gotcha, decision, cross_service_link, feature_domain, file, research, flow_explanation\n\u274C Rejected: function, class, component, variable, method\n\nExample: kuma_memory({ action: 'arch_flow', content: 'domain: AuthFlow | hops: auth.ts \u2192 middleware.ts' })";
11367
11580
  }
11368
11581
  if (!isNodeTypeAllowed(type)) {
11369
11582
  return [
@@ -11493,6 +11706,54 @@ async function handleHarvest(params) {
11493
11706
  const dryRun = params.status === "dry-run" || params.status === "preview";
11494
11707
  return await harvestGitHistory2({ commitCount: count, dryRun });
11495
11708
  }
11709
+ async function handleSessionMine(_params) {
11710
+ const { mineSessionInsights: mineSessionInsights2, formatMinedInsights: formatMinedInsights2 } = await Promise.resolve().then(() => (init_kumaSessionMiner(), kumaSessionMiner_exports));
11711
+ const insights = mineSessionInsights2();
11712
+ if (insights.length === 0) {
11713
+ return formatMinedInsights2(insights);
11714
+ }
11715
+ if (_params.scope === "approve" || _params.limit === -1) {
11716
+ let approved = 0;
11717
+ for (const ins of insights) {
11718
+ try {
11719
+ if (ins.type === "gotcha") {
11720
+ const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
11721
+ await addGotcha2({ filePath: ins.scope, description: ins.content, severity: ins.confidence > 0.7 ? "high" : "medium" });
11722
+ } else if (ins.type === "decision") {
11723
+ const { recordDecision: recordDecision2 } = await Promise.resolve().then(() => (init_kumaMemory(), kumaMemory_exports));
11724
+ await recordDecision2({
11725
+ title: ins.content.substring(0, 80),
11726
+ context: ins.source,
11727
+ options: [],
11728
+ rationale: ins.content,
11729
+ outcome: "session_mined",
11730
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11731
+ });
11732
+ } else if (ins.type === "arch_flow") {
11733
+ const { recordDomainFlow: recordDomainFlow2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
11734
+ const hops = ins.content.split("\u2192").map((h) => h.trim()).filter(Boolean);
11735
+ if (hops.length >= 2) {
11736
+ await recordDomainFlow2({
11737
+ domain: ins.scope.split("/").pop()?.replace(/\.\w+$/, "") || "Unknown",
11738
+ hops: hops.map((h, i) => ({
11739
+ from: i === 0 ? "entry" : hops[i - 1],
11740
+ to: h,
11741
+ relation: "flows",
11742
+ description: h
11743
+ }))
11744
+ });
11745
+ }
11746
+ }
11747
+ approved++;
11748
+ } catch {
11749
+ }
11750
+ }
11751
+ sessionMemory.recordToolCall("kuma_memory_session_mine_approve", { count: approved });
11752
+ return `\u2705 **Session Mining** \u2014 ${approved}/${insights.length} insights auto-recorded to Knowledge Graph.`;
11753
+ }
11754
+ sessionMemory.recordToolCall("kuma_memory_session_mine_preview", { count: insights.length });
11755
+ return formatMinedInsights2(insights);
11756
+ }
11496
11757
 
11497
11758
  // src/tools/kumaSafetyTool.ts
11498
11759
  init_sessionMemory();
@@ -12538,6 +12799,8 @@ async function handleSafety(params) {
12538
12799
  return handleCheckpointList(params);
12539
12800
  case "contract":
12540
12801
  return handleContract(params);
12802
+ case "gotcha_staleness":
12803
+ return handleGotchaStaleness(params);
12541
12804
  default:
12542
12805
  return `Unknown action "${action}".`;
12543
12806
  }
@@ -12798,6 +13061,11 @@ async function handleVerify(params) {
12798
13061
  });
12799
13062
  return verifyResult + recordingWarning;
12800
13063
  }
13064
+ async function handleGotchaStaleness(_params) {
13065
+ const { verifyGotchaStaleness: verifyGotchaStaleness2, formatGotchaStalenessReport: formatGotchaStalenessReport2 } = await Promise.resolve().then(() => (init_kumaSelfHeal(), kumaSelfHeal_exports));
13066
+ const stale = await verifyGotchaStaleness2();
13067
+ return formatGotchaStalenessReport2(stale);
13068
+ }
12801
13069
 
12802
13070
  // src/engine/kumaAutoInit.ts
12803
13071
  init_sessionMemory();