@plumpslabs/kuma 2.3.8 → 2.3.10
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/{agentDetector-G4RBMZ6X.js → agentDetector-3OTNKKBS.js} +4 -4
- package/dist/{chunk-NAM7SCBT.js → chunk-3TX6Q37T.js} +26 -1
- package/dist/{chunk-L5WU2HTN.js → chunk-J2CBDLXB.js} +46 -5
- package/dist/{chunk-RTGLQDMI.js → chunk-OJALFQ4H.js} +2 -2
- package/dist/{chunk-53J56QCQ.js → chunk-Q2444HWO.js} +1 -1
- package/dist/{chunk-SLRRDKQ2.js → chunk-R6B3VUSB.js} +2 -2
- package/dist/{chunk-PTEPJK6M.js → chunk-RI3DKY62.js} +1 -1
- package/dist/{chunk-FL2TLLMX.js → chunk-SPHI2BOO.js} +1 -1
- package/dist/{chunk-4OB3XMHT.js → chunk-ZKDYTYMS.js} +108 -2
- package/dist/{contextDigest-4PCK3DSM.js → contextDigest-5JXLH56Z.js} +3 -3
- package/dist/{domainRules-LDZPOIGZ.js → domainRules-ED6EOFVY.js} +2 -2
- package/dist/index.js +259 -71
- package/dist/{init-AJAESUZZ.js → init-LLP6JDFR.js} +1 -1
- package/dist/kumaCheckpoint-XIQWRMGV.js +207 -0
- package/dist/kumaCodeScanner-LFJGPJNH.js +566 -0
- package/dist/kumaContractEngine-KX27T4N7.js +305 -0
- package/dist/{kumaDb-6D53ERFP.js → kumaDb-IC7UX7PU.js} +3 -1
- package/dist/{kumaDriftDetector-OESI5GTC.js → kumaDriftDetector-QBY6OJOO.js} +1 -1
- package/dist/{kumaGotchas-DU5H6N7X.js → kumaGotchas-5HODT5RI.js} +3 -3
- package/dist/{kumaGraph-AWP743TJ.js → kumaGraph-GUQM75WL.js} +8 -2
- package/dist/{kumaMemory-7TCUDVZX.js → kumaMemory-FMOWIODH.js} +2 -2
- package/dist/{kumaMiner-FFXSRHAO.js → kumaMiner-3RXEOCBP.js} +3 -3
- package/dist/{kumaPolicyEngine-S2PXN3C7.js → kumaPolicyEngine-ORBWL5PT.js} +2 -2
- package/dist/kumaProgressiveContext-CVSNPY3W.js +231 -0
- package/dist/{kumaSearch-QGB4QVXS.js → kumaSearch-B5WVYHHD.js} +1 -1
- package/dist/kumaTrajectory-T6DXGOG4.js +460 -0
- package/dist/{kumaVerifier-ARSGPZAM.js → kumaVerifier-KAYJHR5K.js} +2 -2
- package/dist/{kumaVisualize-TECABHUY.js → kumaVisualize-AFL7STLL.js} +1 -1
- package/dist/{safetyAudit-55IVCG5B.js → safetyAudit-32WSA4Z5.js} +2 -2
- package/dist/{safetyScore-GR5N55CU.js → safetyScore-WU27EOG4.js} +5 -5
- package/dist/{sessionMemory-CA34TCPF.js → sessionMemory-4UWA3E5J.js} +1 -1
- package/dist/{skillGenerator-F6YO4H5D.js → skillGenerator-ILZFWO5O.js} +6 -8
- package/package.json +18 -13
- package/packages/ide/studio/dist/index.js +140 -0
- package/packages/ide/studio/public/index.html +500 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getProjectRoot
|
|
3
|
+
} from "./chunk-E2KFPEBT.js";
|
|
4
|
+
|
|
5
|
+
// src/engine/kumaContractEngine.ts
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
var DEFAULT_CONTRACTS = {
|
|
9
|
+
version: 1,
|
|
10
|
+
contracts: [
|
|
11
|
+
{
|
|
12
|
+
id: "no-delete-tests-without-replacement",
|
|
13
|
+
description: "Test files should not be deleted without replacement",
|
|
14
|
+
appliesTo: ["*"],
|
|
15
|
+
preConditions: [],
|
|
16
|
+
postConditions: [
|
|
17
|
+
{
|
|
18
|
+
type: "not_contains",
|
|
19
|
+
target: "*/.test.*",
|
|
20
|
+
value: "test.skip",
|
|
21
|
+
description: "Tests should not be skipped without reason"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
severity: "warning"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "no-silent-empty-catch",
|
|
28
|
+
description: "Catch blocks must not be empty",
|
|
29
|
+
appliesTo: ["*"],
|
|
30
|
+
preConditions: [],
|
|
31
|
+
postConditions: [
|
|
32
|
+
{
|
|
33
|
+
type: "not_contains",
|
|
34
|
+
target: "*.ts",
|
|
35
|
+
value: "catch {}",
|
|
36
|
+
description: "Empty catch blocks suppress errors silently"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "not_contains",
|
|
40
|
+
target: "*.ts",
|
|
41
|
+
value: "catch (e) {}",
|
|
42
|
+
description: "Empty catch (e) {} suppresses errors silently"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
severity: "error"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "api-response-shape",
|
|
49
|
+
description: "API response shape must be preserved",
|
|
50
|
+
appliesTo: ["src/api/*", "src/routes/*"],
|
|
51
|
+
preConditions: [],
|
|
52
|
+
postConditions: [],
|
|
53
|
+
severity: "warning"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
function contractsDir() {
|
|
58
|
+
return path.join(getProjectRoot(), ".kuma", "contracts");
|
|
59
|
+
}
|
|
60
|
+
function ensureContractsDir() {
|
|
61
|
+
const dir = contractsDir();
|
|
62
|
+
if (!fs.existsSync(dir)) {
|
|
63
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
64
|
+
const defaultPath = path.join(dir, "default.json");
|
|
65
|
+
if (!fs.existsSync(defaultPath)) {
|
|
66
|
+
fs.writeFileSync(
|
|
67
|
+
defaultPath,
|
|
68
|
+
JSON.stringify(DEFAULT_CONTRACTS, null, 2),
|
|
69
|
+
"utf-8"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function loadContracts() {
|
|
75
|
+
ensureContractsDir();
|
|
76
|
+
const dir = contractsDir();
|
|
77
|
+
const contracts = [];
|
|
78
|
+
try {
|
|
79
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
try {
|
|
82
|
+
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
83
|
+
const config = JSON.parse(content);
|
|
84
|
+
contracts.push(config);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(`[ContractEngine] Failed to load ${file}: ${err}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
if (contracts.length === 0) {
|
|
92
|
+
contracts.push(DEFAULT_CONTRACTS);
|
|
93
|
+
}
|
|
94
|
+
return contracts;
|
|
95
|
+
}
|
|
96
|
+
function evaluatePreConditions(toolName, filePath) {
|
|
97
|
+
const contracts = loadContracts();
|
|
98
|
+
const verdicts = [];
|
|
99
|
+
for (const config of contracts) {
|
|
100
|
+
for (const contract of config.contracts) {
|
|
101
|
+
if (!appliesToContract(contract, toolName, filePath)) continue;
|
|
102
|
+
const violations = [];
|
|
103
|
+
for (const condition of contract.preConditions) {
|
|
104
|
+
const result = evaluateCondition(condition, filePath);
|
|
105
|
+
if (!result.passed) {
|
|
106
|
+
violations.push({
|
|
107
|
+
condition,
|
|
108
|
+
message: result.message,
|
|
109
|
+
severity: contract.severity
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
verdicts.push({
|
|
114
|
+
contractId: contract.id,
|
|
115
|
+
description: contract.description,
|
|
116
|
+
passed: violations.length === 0,
|
|
117
|
+
violations
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return verdicts;
|
|
122
|
+
}
|
|
123
|
+
function evaluatePostConditions(toolName, filePath) {
|
|
124
|
+
const contracts = loadContracts();
|
|
125
|
+
const verdicts = [];
|
|
126
|
+
for (const config of contracts) {
|
|
127
|
+
for (const contract of config.contracts) {
|
|
128
|
+
if (!appliesToContract(contract, toolName, filePath)) continue;
|
|
129
|
+
const violations = [];
|
|
130
|
+
for (const condition of contract.postConditions) {
|
|
131
|
+
const result = evaluateCondition(condition, filePath);
|
|
132
|
+
if (!result.passed) {
|
|
133
|
+
violations.push({
|
|
134
|
+
condition,
|
|
135
|
+
message: result.message,
|
|
136
|
+
severity: contract.severity
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
verdicts.push({
|
|
141
|
+
contractId: contract.id,
|
|
142
|
+
description: contract.description,
|
|
143
|
+
passed: violations.length === 0,
|
|
144
|
+
violations
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return verdicts;
|
|
149
|
+
}
|
|
150
|
+
function evaluateCondition(condition, contextPath) {
|
|
151
|
+
const root = getProjectRoot();
|
|
152
|
+
switch (condition.type) {
|
|
153
|
+
case "file_exists": {
|
|
154
|
+
const targetPath = resolveTarget(condition.target, contextPath, root);
|
|
155
|
+
const exists = targetPath.some((p) => fs.existsSync(p));
|
|
156
|
+
return {
|
|
157
|
+
passed: exists,
|
|
158
|
+
message: exists ? `\u2705 File exists: ${condition.target}` : `\u274C Required file not found: ${condition.target} (${condition.description})`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case "file_not_exists": {
|
|
162
|
+
const targetPath = resolveTarget(condition.target, contextPath, root);
|
|
163
|
+
const exists = targetPath.some((p) => fs.existsSync(p));
|
|
164
|
+
return {
|
|
165
|
+
passed: !exists,
|
|
166
|
+
message: !exists ? `\u2705 File does not exist: ${condition.target}` : `\u274C File should not exist: ${condition.target} (${condition.description})`
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
case "contains": {
|
|
170
|
+
if (!contextPath) return { passed: true, message: "\u26A0\uFE0F No file to check" };
|
|
171
|
+
const fullPath = path.resolve(root, contextPath);
|
|
172
|
+
if (!fs.existsSync(fullPath)) return { passed: true, message: "\u26A0\uFE0F File not found" };
|
|
173
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
174
|
+
const hasContent = condition.value ? content.includes(condition.value) : false;
|
|
175
|
+
return {
|
|
176
|
+
passed: hasContent,
|
|
177
|
+
message: hasContent ? `\u2705 Contains expected content` : `\u274C Missing expected content: "${condition.value}" (${condition.description})`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
case "not_contains": {
|
|
181
|
+
if (!contextPath) return { passed: true, message: "\u26A0\uFE0F No file to check" };
|
|
182
|
+
const root2 = getProjectRoot();
|
|
183
|
+
const targets = resolveTarget(condition.target, contextPath, root2);
|
|
184
|
+
for (const t of targets) {
|
|
185
|
+
if (fs.existsSync(t)) {
|
|
186
|
+
const content = fs.readFileSync(t, "utf-8");
|
|
187
|
+
if (condition.value && content.includes(condition.value)) {
|
|
188
|
+
return {
|
|
189
|
+
passed: false,
|
|
190
|
+
message: `\u274C Found forbidden pattern "${condition.value}" in ${path.relative(root2, t)} (${condition.description})`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return { passed: true, message: "\u2705 No forbidden patterns found" };
|
|
196
|
+
}
|
|
197
|
+
case "has_test_file": {
|
|
198
|
+
if (!contextPath) return { passed: true, message: "\u26A0\uFE0F No file to check" };
|
|
199
|
+
const basename = path.basename(contextPath, path.extname(contextPath));
|
|
200
|
+
const dir = path.dirname(contextPath);
|
|
201
|
+
const possibleTests = [
|
|
202
|
+
path.join(dir, `${basename}.test.ts`),
|
|
203
|
+
path.join(dir, `${basename}.spec.ts`),
|
|
204
|
+
path.join(dir, `__tests__/${basename}.test.ts`),
|
|
205
|
+
path.join(dir, `__tests__/${basename}.spec.ts`)
|
|
206
|
+
];
|
|
207
|
+
const hasTest = possibleTests.some((t) => fs.existsSync(t));
|
|
208
|
+
return {
|
|
209
|
+
passed: hasTest,
|
|
210
|
+
message: hasTest ? `\u2705 Test file exists` : `\u274C No test file found for ${contextPath} (${condition.description})`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
default:
|
|
214
|
+
return { passed: true, message: `\u26A0\uFE0F Unknown condition type: ${condition.type}` };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function appliesToContract(contract, toolName, filePath) {
|
|
218
|
+
if (contract.appliesTo.includes("*")) return true;
|
|
219
|
+
if (contract.appliesTo.includes(toolName)) return true;
|
|
220
|
+
if (filePath) {
|
|
221
|
+
return contract.appliesTo.some((pattern) => {
|
|
222
|
+
return filePath.includes(pattern.replace("*", ""));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
function resolveTarget(pattern, contextPath, root) {
|
|
228
|
+
const r = root || getProjectRoot();
|
|
229
|
+
if (pattern.startsWith("*")) {
|
|
230
|
+
if (contextPath) {
|
|
231
|
+
const dir = path.dirname(path.resolve(r, contextPath));
|
|
232
|
+
const suffix = pattern.replace("*", "");
|
|
233
|
+
try {
|
|
234
|
+
const files = fs.readdirSync(dir);
|
|
235
|
+
return files.filter((f) => f.endsWith(suffix) || f.includes(suffix)).map((f) => path.join(dir, f));
|
|
236
|
+
} catch {
|
|
237
|
+
return [path.join(dir, pattern.replace("*", ""))];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
return [path.resolve(r, pattern)];
|
|
243
|
+
}
|
|
244
|
+
async function runContractChecks(toolName, filePath, phase = "pre") {
|
|
245
|
+
const verdicts = phase === "pre" ? evaluatePreConditions(toolName, filePath) : evaluatePostConditions(toolName, filePath);
|
|
246
|
+
if (verdicts.length === 0) {
|
|
247
|
+
return "\u2705 **No applicable contracts** for this action.";
|
|
248
|
+
}
|
|
249
|
+
const lines = [
|
|
250
|
+
`\u{1F4DC} **Contract Check** (${phase}-conditions)`,
|
|
251
|
+
`\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`,
|
|
252
|
+
``
|
|
253
|
+
];
|
|
254
|
+
let totalViolations = 0;
|
|
255
|
+
let errors = 0;
|
|
256
|
+
for (const v of verdicts) {
|
|
257
|
+
if (v.passed) {
|
|
258
|
+
lines.push(` \u2705 **${v.description}** \u2014 passed`);
|
|
259
|
+
} else {
|
|
260
|
+
totalViolations += v.violations.length;
|
|
261
|
+
const errorCount = v.violations.filter((x) => x.severity === "error").length;
|
|
262
|
+
errors += errorCount;
|
|
263
|
+
lines.push(` ${errorCount > 0 ? "\u{1F534}" : "\u{1F7E1}"} **${v.description}** \u2014 ${v.violations.length} violation(s)`);
|
|
264
|
+
for (const violation of v.violations) {
|
|
265
|
+
lines.push(` ${violation.severity === "error" ? "\u{1F534}" : "\u{1F7E1}"} ${violation.message.substring(0, 120)}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
lines.push("");
|
|
270
|
+
if (errors > 0) {
|
|
271
|
+
lines.push(`\u{1F534} **${errors} error(s)** \u2014 blocking. Resolve before proceeding.`);
|
|
272
|
+
} else if (totalViolations > 0) {
|
|
273
|
+
lines.push(`\u{1F7E1} **${totalViolations} warning(s)** \u2014 review recommended.`);
|
|
274
|
+
} else {
|
|
275
|
+
lines.push("\u2705 All contract checks passed.");
|
|
276
|
+
}
|
|
277
|
+
return lines.join("\n");
|
|
278
|
+
}
|
|
279
|
+
function listContracts() {
|
|
280
|
+
const configs = loadContracts();
|
|
281
|
+
const lines = [
|
|
282
|
+
"\u{1F4DC} **Active Contracts**",
|
|
283
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
284
|
+
""
|
|
285
|
+
];
|
|
286
|
+
for (const config of configs) {
|
|
287
|
+
for (const contract of config.contracts) {
|
|
288
|
+
const appliesTo = contract.appliesTo.join(", ");
|
|
289
|
+
lines.push(` \u{1F4CC} **${contract.id}**`);
|
|
290
|
+
lines.push(` ${contract.description}`);
|
|
291
|
+
lines.push(` Applies to: ${appliesTo}`);
|
|
292
|
+
lines.push(` Pre-conditions: ${contract.preConditions.length}`);
|
|
293
|
+
lines.push(` Post-conditions: ${contract.postConditions.length}`);
|
|
294
|
+
lines.push("");
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return lines.join("\n");
|
|
298
|
+
}
|
|
299
|
+
export {
|
|
300
|
+
evaluatePostConditions,
|
|
301
|
+
evaluatePreConditions,
|
|
302
|
+
listContracts,
|
|
303
|
+
loadContracts,
|
|
304
|
+
runContractChecks
|
|
305
|
+
};
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
listTodos,
|
|
18
18
|
recordChange,
|
|
19
19
|
recordDecisionLog,
|
|
20
|
+
resetDbInstance,
|
|
20
21
|
rollbackChange,
|
|
21
22
|
runDoctor,
|
|
22
23
|
runGarbageCollection,
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
saveVerification,
|
|
29
30
|
updateDecisionStatus,
|
|
30
31
|
updateTodoStatus
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-3TX6Q37T.js";
|
|
32
33
|
import "./chunk-E2KFPEBT.js";
|
|
33
34
|
export {
|
|
34
35
|
addContextNote,
|
|
@@ -49,6 +50,7 @@ export {
|
|
|
49
50
|
listTodos,
|
|
50
51
|
recordChange,
|
|
51
52
|
recordDecisionLog,
|
|
53
|
+
resetDbInstance,
|
|
52
54
|
rollbackChange,
|
|
53
55
|
runDoctor,
|
|
54
56
|
runGarbageCollection,
|
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
appendToLayer,
|
|
3
3
|
checkFileGotchas,
|
|
4
4
|
getActiveGotchas
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Q2444HWO.js";
|
|
6
6
|
import {
|
|
7
7
|
sessionMemory
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-R6B3VUSB.js";
|
|
9
9
|
import {
|
|
10
10
|
getDb,
|
|
11
11
|
saveDb
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3TX6Q37T.js";
|
|
13
13
|
import "./chunk-E2KFPEBT.js";
|
|
14
14
|
|
|
15
15
|
// src/engine/kumaGotchas.ts
|
|
@@ -5,17 +5,20 @@ import {
|
|
|
5
5
|
formatFlow,
|
|
6
6
|
formatImpact,
|
|
7
7
|
getGraphStats,
|
|
8
|
+
listFederatedReferences,
|
|
9
|
+
nodeId,
|
|
8
10
|
queryGraph,
|
|
9
11
|
recordApiRoute,
|
|
10
12
|
recordFileDefinition,
|
|
11
13
|
recordFunctionCall,
|
|
12
14
|
recordImport,
|
|
13
15
|
recordTestRelation,
|
|
16
|
+
resolveFederatedNode,
|
|
14
17
|
searchGraph,
|
|
15
18
|
traceFlow,
|
|
16
19
|
upsertNode
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-ZKDYTYMS.js";
|
|
21
|
+
import "./chunk-3TX6Q37T.js";
|
|
19
22
|
import "./chunk-E2KFPEBT.js";
|
|
20
23
|
export {
|
|
21
24
|
addEdge,
|
|
@@ -24,12 +27,15 @@ export {
|
|
|
24
27
|
formatFlow,
|
|
25
28
|
formatImpact,
|
|
26
29
|
getGraphStats,
|
|
30
|
+
listFederatedReferences,
|
|
31
|
+
nodeId,
|
|
27
32
|
queryGraph,
|
|
28
33
|
recordApiRoute,
|
|
29
34
|
recordFileDefinition,
|
|
30
35
|
recordFunctionCall,
|
|
31
36
|
recordImport,
|
|
32
37
|
recordTestRelation,
|
|
38
|
+
resolveFederatedNode,
|
|
33
39
|
searchGraph,
|
|
34
40
|
traceFlow,
|
|
35
41
|
upsertNode
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
recordDecision,
|
|
5
5
|
scoreMemoryRelevance,
|
|
6
6
|
shouldRecordDecision
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-OJALFQ4H.js";
|
|
8
|
+
import "./chunk-R6B3VUSB.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
export {
|
|
11
11
|
formatDecisionTemplate,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
recordDecision
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-OJALFQ4H.js";
|
|
4
|
+
import "./chunk-R6B3VUSB.js";
|
|
5
5
|
import {
|
|
6
6
|
recordDecisionLog
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3TX6Q37T.js";
|
|
8
8
|
import "./chunk-E2KFPEBT.js";
|
|
9
9
|
|
|
10
10
|
// src/engine/kumaMiner.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sessionMemory
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-R6B3VUSB.js";
|
|
4
4
|
import {
|
|
5
5
|
getProjectRoot
|
|
6
6
|
} from "./chunk-E2KFPEBT.js";
|
|
@@ -215,7 +215,7 @@ function evaluateDatabaseAction(action) {
|
|
|
215
215
|
};
|
|
216
216
|
}
|
|
217
217
|
async function processOverride(request) {
|
|
218
|
-
const { recordAudit } = await import("./safetyAudit-
|
|
218
|
+
const { recordAudit } = await import("./safetyAudit-32WSA4Z5.js");
|
|
219
219
|
await recordAudit({
|
|
220
220
|
timestamp: Math.floor(Date.now() / 1e3),
|
|
221
221
|
toolName: request.toolName,
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getActiveGotchas,
|
|
3
|
+
readLayer
|
|
4
|
+
} from "./chunk-Q2444HWO.js";
|
|
5
|
+
import "./chunk-R6B3VUSB.js";
|
|
6
|
+
import {
|
|
7
|
+
getDb
|
|
8
|
+
} from "./chunk-3TX6Q37T.js";
|
|
9
|
+
import {
|
|
10
|
+
getProjectRoot
|
|
11
|
+
} from "./chunk-E2KFPEBT.js";
|
|
12
|
+
|
|
13
|
+
// src/engine/kumaProgressiveContext.ts
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
async function getProjectMeta() {
|
|
17
|
+
const root = getProjectRoot();
|
|
18
|
+
const name = root.split("/").pop() || "unknown";
|
|
19
|
+
const stack = [];
|
|
20
|
+
if (fs.existsSync(path.join(root, "package.json"))) {
|
|
21
|
+
try {
|
|
22
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
|
|
23
|
+
if (pkg.dependencies?.next) stack.push("Next.js");
|
|
24
|
+
else if (pkg.dependencies?.react) stack.push("React");
|
|
25
|
+
if (pkg.devDependencies?.typescript) stack.push("TypeScript");
|
|
26
|
+
if (fs.existsSync(path.join(root, "pnpm-lock.yaml"))) stack.push("pnpm");
|
|
27
|
+
else if (fs.existsSync(path.join(root, "yarn.lock"))) stack.push("yarn");
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (fs.existsSync(path.join(root, "go.mod"))) stack.push("Go");
|
|
32
|
+
if (fs.existsSync(path.join(root, "Cargo.toml"))) stack.push("Rust");
|
|
33
|
+
if (fs.existsSync(path.join(root, "pyproject.toml"))) stack.push("Python");
|
|
34
|
+
let nodeCount = 0;
|
|
35
|
+
let edgeCount = 0;
|
|
36
|
+
try {
|
|
37
|
+
const db = await getDb();
|
|
38
|
+
const nodeRes = db.exec("SELECT COUNT(*) as c FROM nodes");
|
|
39
|
+
nodeCount = nodeRes[0]?.values[0]?.[0] ?? 0;
|
|
40
|
+
const edgeRes = db.exec("SELECT COUNT(*) as c FROM edges");
|
|
41
|
+
edgeCount = edgeRes[0]?.values[0]?.[0] ?? 0;
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
const kumaDir = path.join(root, ".kuma");
|
|
45
|
+
const layerStatus = {
|
|
46
|
+
domainRules: fs.existsSync(path.join(kumaDir, "DOMAIN_RULES.md")),
|
|
47
|
+
archFlow: fs.existsSync(path.join(kumaDir, "ARCHITECTURE_FLOW.md")),
|
|
48
|
+
gotchas: getActiveGotchas().length
|
|
49
|
+
};
|
|
50
|
+
let hasDecisions = false;
|
|
51
|
+
let hasTodos = false;
|
|
52
|
+
try {
|
|
53
|
+
const db = await getDb();
|
|
54
|
+
const decRes = db.exec("SELECT COUNT(*) as c FROM decision_log");
|
|
55
|
+
hasDecisions = (decRes[0]?.values[0]?.[0] ?? 0) > 0;
|
|
56
|
+
const todoRes = db.exec("SELECT COUNT(*) as c FROM todos WHERE status != 'done'");
|
|
57
|
+
hasTodos = (todoRes[0]?.values[0]?.[0] ?? 0) > 0;
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
return { name, stack, entryPoints: [], nodeCount, edgeCount, layerStatus, hasDecisions, hasTodos };
|
|
61
|
+
}
|
|
62
|
+
function formatProjectMeta(meta) {
|
|
63
|
+
const parts = [
|
|
64
|
+
`\u{1F4C1} ${meta.name}`,
|
|
65
|
+
meta.stack.length > 0 ? `\u26A1 ${meta.stack.join("/")}` : "",
|
|
66
|
+
`\u{1F4CA} ${meta.nodeCount}n/${meta.edgeCount}e`,
|
|
67
|
+
meta.layerStatus.domainRules ? "\u{1F4CB}R" : "",
|
|
68
|
+
meta.layerStatus.archFlow ? "\u{1F3D7}\uFE0FA" : "",
|
|
69
|
+
meta.layerStatus.gotchas > 0 ? `\u26A0\uFE0F${meta.layerStatus.gotchas}g` : "",
|
|
70
|
+
meta.hasDecisions ? "\u{1F4DD}D" : "",
|
|
71
|
+
meta.hasTodos ? "\u{1F4CC}T" : ""
|
|
72
|
+
].filter(Boolean);
|
|
73
|
+
return parts.join(" | ");
|
|
74
|
+
}
|
|
75
|
+
async function loadSection(section, scope) {
|
|
76
|
+
switch (section) {
|
|
77
|
+
case "domain_rules": {
|
|
78
|
+
try {
|
|
79
|
+
const content = readLayer("domain_rules");
|
|
80
|
+
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("<!--"));
|
|
81
|
+
return `\u{1F4CB} **Domain Rules**
|
|
82
|
+
${lines.slice(0, 20).join("\n")}`;
|
|
83
|
+
} catch {
|
|
84
|
+
return "\u{1F4CB} **Domain Rules**: not configured";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
case "architecture": {
|
|
88
|
+
try {
|
|
89
|
+
const content = readLayer("arch_flow");
|
|
90
|
+
const relevant = scope ? content.split("\n").filter((l) => l.toLowerCase().includes(scope.toLowerCase())).slice(0, 15) : content.split("\n").filter((l) => l.trim() && !l.startsWith("<!--")).slice(0, 20);
|
|
91
|
+
return `\u{1F3D7}\uFE0F **Architecture${scope ? `: ${scope}` : ""}**
|
|
92
|
+
${relevant.join("\n")}`;
|
|
93
|
+
} catch {
|
|
94
|
+
return "\u{1F3D7}\uFE0F **Architecture**: not documented";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
case "gotchas": {
|
|
98
|
+
const gotchas = getActiveGotchas();
|
|
99
|
+
if (gotchas.length === 0) return "\u26A0\uFE0F **Gotchas**: none recorded";
|
|
100
|
+
const filtered = scope ? gotchas.filter((g) => g.filePath.toLowerCase().includes(scope.toLowerCase())) : gotchas;
|
|
101
|
+
const lines = filtered.slice(0, 5).map((g) => {
|
|
102
|
+
const icon = g.severity === "critical" ? "\u{1F534}" : g.severity === "high" ? "\u{1F7E0}" : "\u{1F7E1}";
|
|
103
|
+
return `${icon} ${g.filePath}: ${g.description.substring(0, 80)}`;
|
|
104
|
+
});
|
|
105
|
+
return `\u26A0\uFE0F **Gotchas** (${filtered.length} active)
|
|
106
|
+
${lines.join("\n")}`;
|
|
107
|
+
}
|
|
108
|
+
case "decisions": {
|
|
109
|
+
try {
|
|
110
|
+
const db = await getDb();
|
|
111
|
+
const stmt = db.prepare(
|
|
112
|
+
`SELECT title, status, created_at FROM decision_log ORDER BY created_at DESC LIMIT 10`
|
|
113
|
+
);
|
|
114
|
+
const results = [];
|
|
115
|
+
while (stmt.step()) results.push(stmt.getAsObject());
|
|
116
|
+
stmt.free();
|
|
117
|
+
if (results.length === 0) return "\u{1F4DD} **Decisions**: none recorded";
|
|
118
|
+
const lines = results.map(
|
|
119
|
+
(d) => ` ${d.status === "active" ? "\u2705" : "\u{1F4DD}"} ${d.title} (${d.status})`
|
|
120
|
+
);
|
|
121
|
+
return `\u{1F4DD} **Decisions** (${results.length})
|
|
122
|
+
${lines.join("\n")}`;
|
|
123
|
+
} catch {
|
|
124
|
+
return "\u{1F4DD} **Decisions**: unavailable";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
case "graph": {
|
|
128
|
+
try {
|
|
129
|
+
const db = await getDb();
|
|
130
|
+
const nodeRes = db.exec("SELECT type, COUNT(*) as cnt FROM nodes GROUP BY type ORDER BY cnt DESC LIMIT 8");
|
|
131
|
+
if (!nodeRes[0]?.values) return "\u{1F4CA} **Graph**: empty";
|
|
132
|
+
const lines = nodeRes[0].values.map((r) => ` ${r[0]}: ${r[1]}`);
|
|
133
|
+
return `\u{1F4CA} **Knowledge Graph**
|
|
134
|
+
${lines.join("\n")}`;
|
|
135
|
+
} catch {
|
|
136
|
+
return "\u{1F4CA} **Graph**: unavailable";
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
case "changes": {
|
|
140
|
+
try {
|
|
141
|
+
const db = await getDb();
|
|
142
|
+
const stmt = db.prepare("SELECT file_path, change_type FROM change_log ORDER BY id DESC LIMIT 8");
|
|
143
|
+
const results = [];
|
|
144
|
+
while (stmt.step()) results.push(stmt.getAsObject());
|
|
145
|
+
stmt.free();
|
|
146
|
+
if (results.length === 0) return "\u{1F4DD} **Changes**: none in this session";
|
|
147
|
+
const lines = results.map(
|
|
148
|
+
(r) => ` ${r.change_type === "modified" ? "\u{1F4DD}" : r.change_type === "created" ? "\u2728" : "\u274C"} ${r.file_path}`
|
|
149
|
+
);
|
|
150
|
+
return `\u{1F4DD} **Recent Changes** (${results.length})
|
|
151
|
+
${lines.join("\n")}`;
|
|
152
|
+
} catch {
|
|
153
|
+
return "\u{1F4DD} **Changes**: unavailable";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
case "health": {
|
|
157
|
+
try {
|
|
158
|
+
const { computeSafetyScore } = await import("./safetyScore-WU27EOG4.js");
|
|
159
|
+
const score = await computeSafetyScore();
|
|
160
|
+
return `\u{1F3E5} **Health**: ${score.score}/100 (${score.risk})`;
|
|
161
|
+
} catch {
|
|
162
|
+
return "\u{1F3E5} **Health**: unavailable";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
default:
|
|
166
|
+
return `Unknown section: ${section}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function getSkillBoundary(skillArea) {
|
|
170
|
+
const area = skillArea.toLowerCase();
|
|
171
|
+
if (area.includes("auth") || area.includes("login") || area.includes("security")) {
|
|
172
|
+
return {
|
|
173
|
+
name: "auth",
|
|
174
|
+
relatedSections: ["domain_rules", "architecture", "gotchas", "decisions"],
|
|
175
|
+
relatedPaths: ["src/auth", "src/middleware", "src/guards"],
|
|
176
|
+
description: "Authentication & authorization context"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (area.includes("test") || area.includes("spec")) {
|
|
180
|
+
return {
|
|
181
|
+
name: "testing",
|
|
182
|
+
relatedSections: ["gotchas", "health"],
|
|
183
|
+
relatedPaths: ["__tests__", "*.test.*", "*.spec.*"],
|
|
184
|
+
description: "Testing & verification context"
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (area.includes("db") || area.includes("database") || area.includes("schema")) {
|
|
188
|
+
return {
|
|
189
|
+
name: "database",
|
|
190
|
+
relatedSections: ["architecture", "gotchas", "decisions"],
|
|
191
|
+
relatedPaths: ["prisma", "src/db", "schema", "migrations"],
|
|
192
|
+
description: "Database & schema context"
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (area.includes("api") || area.includes("route") || area.includes("endpoint")) {
|
|
196
|
+
return {
|
|
197
|
+
name: "api",
|
|
198
|
+
relatedSections: ["architecture", "gotchas", "decisions", "changes"],
|
|
199
|
+
relatedPaths: ["src/api", "src/routes", "src/controllers"],
|
|
200
|
+
description: "API & routing context"
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
name: "general",
|
|
205
|
+
relatedSections: ["domain_rules", "gotchas"],
|
|
206
|
+
relatedPaths: [],
|
|
207
|
+
description: "General project context"
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async function getProgressiveContext(skillArea) {
|
|
211
|
+
const meta = await getProjectMeta();
|
|
212
|
+
const boundary = getSkillBoundary(skillArea || "general");
|
|
213
|
+
const sections = await Promise.all(
|
|
214
|
+
boundary.relatedSections.map(async (section) => ({
|
|
215
|
+
name: section,
|
|
216
|
+
content: await loadSection(section, skillArea)
|
|
217
|
+
}))
|
|
218
|
+
);
|
|
219
|
+
return {
|
|
220
|
+
meta: formatProjectMeta(meta),
|
|
221
|
+
sections,
|
|
222
|
+
boundary
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
formatProjectMeta,
|
|
227
|
+
getProgressiveContext,
|
|
228
|
+
getProjectMeta,
|
|
229
|
+
getSkillBoundary,
|
|
230
|
+
loadSection
|
|
231
|
+
};
|