@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 +839 -571
- package/package.json +1 -1
- package/packages/ide/studio/dist/index.js +19 -8
- package/packages/ide/studio/public/index.html +45 -5
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(-
|
|
1932
|
-
if (recentCalls.length <
|
|
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 >=
|
|
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
|
|
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 =
|
|
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
|
-
|
|
2258
|
-
|
|
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
|
|
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)
|
|
2275
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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.
|
|
6380
|
-
`
|
|
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
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
const
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
8101
|
-
if (
|
|
8102
|
-
lines
|
|
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
|
-
"\
|
|
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
|
-
|
|
8252
|
-
'
|
|
8253
|
-
'
|
|
8254
|
-
|
|
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{
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
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
|
|
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
|
-
"\
|
|
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
|
-
|
|
8368
|
-
'
|
|
8369
|
-
'
|
|
8370
|
-
|
|
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{
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
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
|
|
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
|
|
8842
|
+
"# Kuma Init \u2014 Workflow & Rules",
|
|
8414
8843
|
"",
|
|
8415
|
-
"_(Auto-generated by `kuma init` \u2014 edit this file
|
|
8844
|
+
"_(Auto-generated by `kuma init` \u2014 edit this file to customize)_",
|
|
8416
8845
|
"",
|
|
8417
8846
|
"---",
|
|
8418
8847
|
"",
|
|
8419
|
-
">
|
|
8848
|
+
"> **Platform Tool Names:**",
|
|
8420
8849
|
'> \u2022 **OpenCode / Antigravity:** Use `kuma_kuma_*` prefix (e.g. `kuma_kuma_context({ action: "init" })`)',
|
|
8421
|
-
'> \u2022 **Other platforms
|
|
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
|
-
"##
|
|
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
|
-
"###
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
"###
|
|
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
|
-
"
|
|
8458
|
-
"",
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
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
|
-
"
|
|
8511
|
-
"
|
|
8512
|
-
'
|
|
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
|
-
"
|
|
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
|
-
"
|
|
8524
|
-
"
|
|
8525
|
-
|
|
8526
|
-
"
|
|
8527
|
-
"
|
|
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
|
-
"**
|
|
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
|
|
8887
|
+
"**\u26A0\uFE0F gotcha format:**",
|
|
8537
8888
|
"```",
|
|
8538
|
-
|
|
8889
|
+
'kuma_memory({ action: "gotcha", scope: "<file_path>", content: "<description>", status: "medium" })',
|
|
8539
8890
|
"```",
|
|
8540
|
-
"- `
|
|
8541
|
-
"- `
|
|
8542
|
-
"- `
|
|
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
|
-
"
|
|
8895
|
+
"**\u26A0\uFE0F arch_flow format:**",
|
|
8546
8896
|
"```",
|
|
8547
|
-
"domain:
|
|
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
|
-
"
|
|
8903
|
+
"**\u26A0\uFE0F decision format:**",
|
|
8551
8904
|
"```",
|
|
8552
8905
|
'kuma_memory({ action: "decision", decisionAction: "record", title: "...", context: "...", rationale: "...", outcome: "..." })',
|
|
8553
8906
|
"```",
|
|
8554
|
-
"
|
|
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
|
-
"
|
|
8909
|
+
"---",
|
|
8558
8910
|
"",
|
|
8559
|
-
"
|
|
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
|
-
"
|
|
8567
|
-
"
|
|
8568
|
-
|
|
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
|
-
"###
|
|
8578
|
-
"",
|
|
8579
|
-
"| Action | Description |
|
|
8580
|
-
"
|
|
8581
|
-
"| `init` | Load project brief, restore session |
|
|
8582
|
-
"| `research` | 5-step research pipeline |
|
|
8583
|
-
"| `impact` | Analyze change effects
|
|
8584
|
-
"| `navigate` | Trace code flow |
|
|
8585
|
-
"| `changes` | View session change log |
|
|
8586
|
-
"| `health` | Project health score 0-100 |
|
|
8587
|
-
"| `digest` | Ultra-compact project briefing |
|
|
8588
|
-
"| `drift` | Detect memory staleness |
|
|
8589
|
-
"| `rollback` | Undo a change by change ID |
|
|
8590
|
-
"
|
|
8591
|
-
"
|
|
8592
|
-
"
|
|
8593
|
-
"",
|
|
8594
|
-
"
|
|
8595
|
-
"",
|
|
8596
|
-
"|
|
|
8597
|
-
"
|
|
8598
|
-
"| `research_save` |
|
|
8599
|
-
"| `
|
|
8600
|
-
"| `
|
|
8601
|
-
"| `
|
|
8602
|
-
"
|
|
8603
|
-
"
|
|
8604
|
-
"
|
|
8605
|
-
"|
|
|
8606
|
-
"
|
|
8607
|
-
"| `
|
|
8608
|
-
"| `
|
|
8609
|
-
"| `
|
|
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{
|
|
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
|
-
"
|
|
8748
|
-
"",
|
|
8749
|
-
"
|
|
8750
|
-
"
|
|
8751
|
-
"
|
|
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{
|
|
8971
|
+
"## \u{1F4CF} Guard Anti-Patterns",
|
|
8782
8972
|
"",
|
|
8783
|
-
"
|
|
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
|
-
"**
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
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{
|
|
8983
|
+
"## \u{1F5D1}\uFE0F Graph Management",
|
|
8807
8984
|
"",
|
|
8808
|
-
'`
|
|
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
|
-
|
|
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
|
-
|
|
8818
|
-
"
|
|
8819
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
-
"\
|
|
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
|
-
|
|
8856
|
-
'
|
|
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{
|
|
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
|
|
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
|
-
|
|
8980
|
-
"> Follow the full 10-step workflow even for read-only investigations.",
|
|
9131
|
+
"## SESSION WORKFLOW",
|
|
8981
9132
|
"",
|
|
8982
|
-
"
|
|
9133
|
+
"**DEFAULT: 3-Step Lean Workflow** (for most tasks):",
|
|
8983
9134
|
"",
|
|
8984
|
-
|
|
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
|
-
|
|
8987
|
-
'
|
|
8988
|
-
'
|
|
8989
|
-
|
|
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
|
-
"
|
|
8998
|
-
|
|
8999
|
-
|
|
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
|
|
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
|
-
"\
|
|
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
|
-
|
|
9627
|
-
'
|
|
9628
|
-
'
|
|
9629
|
-
|
|
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{
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
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
|
-
"\
|
|
9643
|
-
"\u{
|
|
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 **
|
|
10156
|
-
"
|
|
10157
|
-
"
|
|
10158
|
-
"
|
|
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{
|
|
10161
|
-
|
|
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
|
|
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 **
|
|
10172
|
-
|
|
10173
|
-
"
|
|
10174
|
-
"
|
|
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{
|
|
10177
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
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:
|
|
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
|
|
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();
|