@plumpslabs/kuma 2.3.20 → 2.3.23
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/README.md +18 -0
- package/dist/index.js +10316 -380
- package/package.json +2 -2
- package/packages/ide/studio/dist/index.js +90 -9
- package/packages/ide/studio/public/index.html +528 -233
- package/dist/agentDetector-YOWQVJFR.js +0 -186
- package/dist/chunk-3BRBJZ7P.js +0 -1055
- package/dist/chunk-3OHYYXYN.js +0 -71
- package/dist/chunk-ABKE45T4.js +0 -1264
- package/dist/chunk-E2KFPEBT.js +0 -183
- package/dist/chunk-FKRSI5U5.js +0 -282
- package/dist/chunk-GFLSAXAH.js +0 -155
- package/dist/chunk-L7F67KUP.js +0 -172
- package/dist/chunk-LVKOGXLC.js +0 -658
- package/dist/chunk-PRUTTZBS.js +0 -1113
- package/dist/contextDigest-QB5XHPXE.js +0 -177
- package/dist/domainRules-QLPAQASB.js +0 -20
- package/dist/init-PL4XL662.js +0 -15
- package/dist/kumaAstValidator-CNM7FHYA.js +0 -150
- package/dist/kumaCheckpoint-J2LDQMEO.js +0 -207
- package/dist/kumaCodeScanner-J6B2EHGI.js +0 -563
- package/dist/kumaContractEngine-KX27T4N7.js +0 -305
- package/dist/kumaDb-4XZ5S2LH.js +0 -65
- package/dist/kumaDriftDetector-TOORILSZ.js +0 -237
- package/dist/kumaGotchas-XRGFFBTA.js +0 -151
- package/dist/kumaGraph-UMXZNGYF.js +0 -44
- package/dist/kumaMemory-FBJMV77G.js +0 -16
- package/dist/kumaMiner-XJETL7TL.js +0 -176
- package/dist/kumaPolicyEngine-2QDJDLM7.js +0 -311
- package/dist/kumaProgressiveContext-MWEDRXOH.js +0 -231
- package/dist/kumaSearch-PV4QTKE7.js +0 -321
- package/dist/kumaTrajectory-7NOAVNG2.js +0 -460
- package/dist/kumaVerifier-6YEGC77M.js +0 -265
- package/dist/kumaVisualize-264OEBGJ.js +0 -264
- package/dist/pathValidator-V4DC6U6Z.js +0 -22
- package/dist/safetyAudit-O45SPNTS.js +0 -12
- package/dist/safetyScore-TMMRD2MV.js +0 -333
- package/dist/sessionMemory-YPKVIOMV.js +0 -6
- package/dist/skillGenerator-PWEJKZNX.js +0 -304
package/dist/chunk-E2KFPEBT.js
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
// src/utils/pathValidator.ts
|
|
2
|
-
import path from "path";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
function validateFilePath(filePath, projectRoot) {
|
|
5
|
-
try {
|
|
6
|
-
const root = projectRoot ?? getProjectRoot();
|
|
7
|
-
const resolvedRoot = path.resolve(root);
|
|
8
|
-
const normalizedRoot = path.normalize(resolvedRoot).toLowerCase();
|
|
9
|
-
let resolvedPath;
|
|
10
|
-
if (!path.isAbsolute(filePath)) {
|
|
11
|
-
const cwdPath = path.resolve(process.cwd(), filePath);
|
|
12
|
-
const normalizedCwdPath = path.normalize(cwdPath).toLowerCase();
|
|
13
|
-
if (fs.existsSync(cwdPath) && normalizedCwdPath.startsWith(normalizedRoot)) {
|
|
14
|
-
resolvedPath = cwdPath;
|
|
15
|
-
} else {
|
|
16
|
-
resolvedPath = path.resolve(resolvedRoot, filePath);
|
|
17
|
-
}
|
|
18
|
-
} else {
|
|
19
|
-
resolvedPath = path.resolve(resolvedRoot, filePath);
|
|
20
|
-
}
|
|
21
|
-
const normalizedPath = path.normalize(resolvedPath).toLowerCase();
|
|
22
|
-
if (!normalizedPath.startsWith(normalizedRoot)) {
|
|
23
|
-
return {
|
|
24
|
-
valid: false,
|
|
25
|
-
error: new Error(
|
|
26
|
-
`PATH_TRAVERSAL: Access denied. Path "${filePath}" resolves outside project root "${resolvedRoot}".`
|
|
27
|
-
)
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if (normalizedPath.includes("..")) {
|
|
31
|
-
return {
|
|
32
|
-
valid: false,
|
|
33
|
-
error: new Error(
|
|
34
|
-
`PATH_TRAVERSAL: Path traversal detected in "${filePath}". Relative paths with ".." are not allowed.`
|
|
35
|
-
)
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
const blockedPatterns = [
|
|
39
|
-
"/etc/",
|
|
40
|
-
"/sys/",
|
|
41
|
-
"/proc/",
|
|
42
|
-
"/dev/",
|
|
43
|
-
"/boot/",
|
|
44
|
-
"/usr/",
|
|
45
|
-
"C:\\Windows",
|
|
46
|
-
"C:\\Program Files",
|
|
47
|
-
"C:\\Program Files (x86)",
|
|
48
|
-
"C:\\System32",
|
|
49
|
-
"~/.ssh",
|
|
50
|
-
"/root/"
|
|
51
|
-
];
|
|
52
|
-
for (const pattern of blockedPatterns) {
|
|
53
|
-
if (normalizedPath.includes(pattern.toLowerCase())) {
|
|
54
|
-
return {
|
|
55
|
-
valid: false,
|
|
56
|
-
error: new Error(
|
|
57
|
-
`PATH_TRAVERSAL: Access to system directory "${pattern}" is blocked.`
|
|
58
|
-
)
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
if (fs.existsSync(resolvedPath)) {
|
|
64
|
-
const realPath = fs.realpathSync(resolvedPath);
|
|
65
|
-
const normalizedRealPath = path.normalize(realPath).toLowerCase();
|
|
66
|
-
if (!normalizedRealPath.startsWith(normalizedRoot)) {
|
|
67
|
-
return {
|
|
68
|
-
valid: false,
|
|
69
|
-
error: new Error(
|
|
70
|
-
`PATH_TRAVERSAL: Symlink escape detected. Path "${filePath}" resolves to "${realPath}" which is outside project root "${resolvedRoot}".`
|
|
71
|
-
)
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} catch {
|
|
76
|
-
}
|
|
77
|
-
if (normalizedPath.includes("node_modules")) {
|
|
78
|
-
return {
|
|
79
|
-
valid: true,
|
|
80
|
-
resolvedPath
|
|
81
|
-
// Warning will be handled by caller
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return { valid: true, resolvedPath };
|
|
85
|
-
} catch (err) {
|
|
86
|
-
return {
|
|
87
|
-
valid: false,
|
|
88
|
-
error: err instanceof Error ? err : new Error(`Path validation error: ${String(err)}`)
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function validateFileExtension(filePath) {
|
|
93
|
-
const allowedExtensions = [
|
|
94
|
-
".ts",
|
|
95
|
-
".tsx",
|
|
96
|
-
".js",
|
|
97
|
-
".jsx",
|
|
98
|
-
".mjs",
|
|
99
|
-
".cjs",
|
|
100
|
-
".json",
|
|
101
|
-
".md",
|
|
102
|
-
".css",
|
|
103
|
-
".html",
|
|
104
|
-
".htm",
|
|
105
|
-
".env",
|
|
106
|
-
".env.example",
|
|
107
|
-
".env.local",
|
|
108
|
-
".yml",
|
|
109
|
-
".yaml",
|
|
110
|
-
".toml",
|
|
111
|
-
".svg",
|
|
112
|
-
".png",
|
|
113
|
-
".jpg",
|
|
114
|
-
".gif",
|
|
115
|
-
".sh",
|
|
116
|
-
".bat",
|
|
117
|
-
".ps1",
|
|
118
|
-
".sql",
|
|
119
|
-
".graphql",
|
|
120
|
-
".gql",
|
|
121
|
-
".vue",
|
|
122
|
-
".svelte",
|
|
123
|
-
".astro",
|
|
124
|
-
".prisma",
|
|
125
|
-
".proto",
|
|
126
|
-
".txt",
|
|
127
|
-
".log"
|
|
128
|
-
];
|
|
129
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
130
|
-
return allowedExtensions.includes(ext);
|
|
131
|
-
}
|
|
132
|
-
function getProjectRoot() {
|
|
133
|
-
return process.env.AGENT_PROJECT_ROOT ?? process.cwd();
|
|
134
|
-
}
|
|
135
|
-
function isReadable(filePath) {
|
|
136
|
-
try {
|
|
137
|
-
fs.accessSync(filePath, fs.constants.R_OK);
|
|
138
|
-
return true;
|
|
139
|
-
} catch {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function isWritable(dirPath) {
|
|
144
|
-
try {
|
|
145
|
-
fs.accessSync(dirPath, fs.constants.W_OK);
|
|
146
|
-
return true;
|
|
147
|
-
} catch {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function getBackupPath(filePath, timestamp) {
|
|
152
|
-
const ts = timestamp ?? Date.now();
|
|
153
|
-
const root = getProjectRoot();
|
|
154
|
-
const relativePath = path.relative(root, filePath);
|
|
155
|
-
return path.join(root, ".kuma", "backups", String(ts), relativePath);
|
|
156
|
-
}
|
|
157
|
-
function ensureBackupDir() {
|
|
158
|
-
const root = getProjectRoot();
|
|
159
|
-
const backupDir = path.join(root, ".kuma", "backups");
|
|
160
|
-
if (!fs.existsSync(backupDir)) {
|
|
161
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
162
|
-
}
|
|
163
|
-
return backupDir;
|
|
164
|
-
}
|
|
165
|
-
function getKumaDir() {
|
|
166
|
-
const root = getProjectRoot();
|
|
167
|
-
return path.join(root, ".kuma");
|
|
168
|
-
}
|
|
169
|
-
function getKumaBackupsDir() {
|
|
170
|
-
return path.join(getKumaDir(), "backups");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export {
|
|
174
|
-
validateFilePath,
|
|
175
|
-
validateFileExtension,
|
|
176
|
-
getProjectRoot,
|
|
177
|
-
isReadable,
|
|
178
|
-
isWritable,
|
|
179
|
-
getBackupPath,
|
|
180
|
-
ensureBackupDir,
|
|
181
|
-
getKumaDir,
|
|
182
|
-
getKumaBackupsDir
|
|
183
|
-
};
|
package/dist/chunk-FKRSI5U5.js
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
sessionMemory
|
|
3
|
-
} from "./chunk-LVKOGXLC.js";
|
|
4
|
-
import {
|
|
5
|
-
getProjectRoot
|
|
6
|
-
} from "./chunk-E2KFPEBT.js";
|
|
7
|
-
|
|
8
|
-
// src/engine/domainRules.ts
|
|
9
|
-
import fs from "fs";
|
|
10
|
-
import path from "path";
|
|
11
|
-
var LAYER_FILES = {
|
|
12
|
-
domain_rules: { filename: "DOMAIN_RULES.md", label: "Domain & Business Rules" },
|
|
13
|
-
arch_flow: { filename: "ARCHITECTURE_FLOW.md", label: "Architecture & Sequence Flow" },
|
|
14
|
-
gotcha: { filename: "KNOWN_GOTCHAS.md", label: "Known Code Gotchas" }
|
|
15
|
-
};
|
|
16
|
-
var DOMAIN_RULES_TEMPLATE = `# Domain & Business Rules (Layer 1)
|
|
17
|
-
|
|
18
|
-
Auto-generated by Kuma 3-Layer Memory Engine.
|
|
19
|
-
Update this file with core business logic, workflows, and boundary rules.
|
|
20
|
-
|
|
21
|
-
## Who
|
|
22
|
-
<!-- Who are the users/actors? -->
|
|
23
|
-
|
|
24
|
-
## What
|
|
25
|
-
<!-- What are the core features and business capabilities? -->
|
|
26
|
-
|
|
27
|
-
## When
|
|
28
|
-
<!-- What are the time-based rules, schedules, deadlines? -->
|
|
29
|
-
|
|
30
|
-
## Where
|
|
31
|
-
<!-- Where does the business operate (environments, regions)? -->
|
|
32
|
-
|
|
33
|
-
## Why
|
|
34
|
-
<!-- Why were key architectural decisions made? -->
|
|
35
|
-
|
|
36
|
-
## How
|
|
37
|
-
<!-- How do core workflows execute? -->
|
|
38
|
-
|
|
39
|
-
## Business Rules
|
|
40
|
-
<!-- Critical business logic constraints -->
|
|
41
|
-
- Rule 1:
|
|
42
|
-
- Rule 2:
|
|
43
|
-
|
|
44
|
-
## Workflows
|
|
45
|
-
<!-- Ordered sequence of operations -->
|
|
46
|
-
1.
|
|
47
|
-
2.
|
|
48
|
-
|
|
49
|
-
## Boundary Conditions
|
|
50
|
-
<!-- Edge cases, error states, validation rules -->
|
|
51
|
-
-
|
|
52
|
-
`;
|
|
53
|
-
var ARCH_FLOW_TEMPLATE = `# Architecture & Sequence Flow Map (Layer 2)
|
|
54
|
-
|
|
55
|
-
Auto-generated by Kuma 3-Layer Memory Engine.
|
|
56
|
-
Document execution call chains and architectural dependencies.
|
|
57
|
-
|
|
58
|
-
## System Overview
|
|
59
|
-
<!-- High-level description of the system architecture -->
|
|
60
|
-
|
|
61
|
-
## Entry Points
|
|
62
|
-
<!-- Main entry points for the application -->
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
## Execution Chains
|
|
66
|
-
<!-- Route -> Middleware -> Controller -> Service -> DB -->
|
|
67
|
-
\`\`\`
|
|
68
|
-
Request Flow:
|
|
69
|
-
1. [Entry] -> [Middleware] -> [Handler] -> [Service] -> [DB]
|
|
70
|
-
\`\`\`
|
|
71
|
-
|
|
72
|
-
## Module Dependency Map
|
|
73
|
-
<!-- How modules depend on each other -->
|
|
74
|
-
- Module A depends on: Module B, Module C
|
|
75
|
-
- Module B depends on: Module D
|
|
76
|
-
|
|
77
|
-
## Data Flow
|
|
78
|
-
<!-- How data flows through the system -->
|
|
79
|
-
1. Input:
|
|
80
|
-
2. Process:
|
|
81
|
-
3. Storage:
|
|
82
|
-
4. Output:
|
|
83
|
-
|
|
84
|
-
## External Integrations
|
|
85
|
-
<!-- External services, APIs, databases -->
|
|
86
|
-
-
|
|
87
|
-
`;
|
|
88
|
-
var GOTCHAS_TEMPLATE = `# Known Code Gotchas (Layer 3)
|
|
89
|
-
|
|
90
|
-
Auto-generated by Kuma 3-Layer Memory Engine.
|
|
91
|
-
Catalog of fragile legacy contracts, edge cases, and unexpected framework quirks.
|
|
92
|
-
|
|
93
|
-
## How to Add a Gotcha
|
|
94
|
-
|
|
95
|
-
\`\`\`
|
|
96
|
-
kuma_memory({
|
|
97
|
-
action: 'gotcha',
|
|
98
|
-
scope: 'auth_middleware',
|
|
99
|
-
content: 'The auth middleware expects req.user after JWT decode. Missing middleware causes silent 500.',
|
|
100
|
-
filePath: 'src/middleware/auth.ts'
|
|
101
|
-
})
|
|
102
|
-
\`\`\`
|
|
103
|
-
|
|
104
|
-
## Active Gotchas
|
|
105
|
-
|
|
106
|
-
<!-- Format:
|
|
107
|
-
### [File Path] \u2014 [Short Description]
|
|
108
|
-
- **Issue**: [What happens]
|
|
109
|
-
- **Fix**: [How to handle it correctly]
|
|
110
|
-
- **Severity**: [low | medium | high | critical]
|
|
111
|
-
- **Added**: [Date]
|
|
112
|
-
-->
|
|
113
|
-
|
|
114
|
-
`;
|
|
115
|
-
function layerFilePath(layer) {
|
|
116
|
-
return path.join(getProjectRoot(), ".kuma", LAYER_FILES[layer].filename);
|
|
117
|
-
}
|
|
118
|
-
function ensureLayerFile(layer) {
|
|
119
|
-
const fp = layerFilePath(layer);
|
|
120
|
-
if (!fs.existsSync(fp)) {
|
|
121
|
-
const template = layer === "domain_rules" ? DOMAIN_RULES_TEMPLATE : layer === "arch_flow" ? ARCH_FLOW_TEMPLATE : GOTCHAS_TEMPLATE;
|
|
122
|
-
const dir = path.dirname(fp);
|
|
123
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
124
|
-
fs.writeFileSync(fp, template, "utf-8");
|
|
125
|
-
console.error(`[DomainRules] Auto-created ${LAYER_FILES[layer].filename}`);
|
|
126
|
-
}
|
|
127
|
-
return fp;
|
|
128
|
-
}
|
|
129
|
-
function readLayer(layer) {
|
|
130
|
-
try {
|
|
131
|
-
const fp = ensureLayerFile(layer);
|
|
132
|
-
return fs.readFileSync(fp, "utf-8");
|
|
133
|
-
} catch (err) {
|
|
134
|
-
return `Error reading ${layer}: ${err}`;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function writeLayer(layer, content) {
|
|
138
|
-
try {
|
|
139
|
-
const fp = ensureLayerFile(layer);
|
|
140
|
-
fs.writeFileSync(fp, content, "utf-8");
|
|
141
|
-
const label = LAYER_FILES[layer].label;
|
|
142
|
-
sessionMemory.recordToolCall("kuma_memory", { action: `write_${layer}` });
|
|
143
|
-
return `\u2705 **${label}** updated successfully.`;
|
|
144
|
-
} catch (err) {
|
|
145
|
-
return `\u274C Failed to update ${layer}: ${err}`;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function appendToLayer(layer, entry) {
|
|
149
|
-
try {
|
|
150
|
-
const fp = ensureLayerFile(layer);
|
|
151
|
-
const existing = fs.readFileSync(fp, "utf-8");
|
|
152
|
-
const newContent = existing.trimEnd() + "\n\n" + entry.trim() + "\n";
|
|
153
|
-
fs.writeFileSync(fp, newContent, "utf-8");
|
|
154
|
-
return `\u2705 Entry added to **${LAYER_FILES[layer].label}**.`;
|
|
155
|
-
} catch (err) {
|
|
156
|
-
return `\u274C Failed to append: ${err}`;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function getLayersSummary() {
|
|
160
|
-
const lines = ["\u{1F4DA} **3-Layer Memory Engine**", "\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"];
|
|
161
|
-
for (const layer of ["domain_rules", "arch_flow", "gotcha"]) {
|
|
162
|
-
const fp = layerFilePath(layer);
|
|
163
|
-
const exists = fs.existsSync(fp);
|
|
164
|
-
const label = LAYER_FILES[layer].label;
|
|
165
|
-
if (exists) {
|
|
166
|
-
try {
|
|
167
|
-
const content = fs.readFileSync(fp, "utf-8");
|
|
168
|
-
const lineCount = content.split("\n").length;
|
|
169
|
-
const sizeKB = Math.round(Buffer.byteLength(content) / 1024);
|
|
170
|
-
lines.push(` ${layer === "domain_rules" ? "\u{1F4CB}" : layer === "arch_flow" ? "\u{1F3D7}\uFE0F" : "\u26A0\uFE0F"} **${label}** \u2014 ${lineCount} lines, ${sizeKB}KB`);
|
|
171
|
-
} catch {
|
|
172
|
-
lines.push(` \u2753 **${label}** \u2014 could not read`);
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
lines.push(` \u{1F4C4} **${label}** \u2014 not yet created`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return lines.join("\n");
|
|
179
|
-
}
|
|
180
|
-
function getActiveGotchas() {
|
|
181
|
-
const results = [];
|
|
182
|
-
try {
|
|
183
|
-
const fp = ensureLayerFile("gotcha");
|
|
184
|
-
const content = fs.readFileSync(fp, "utf-8");
|
|
185
|
-
const lines = content.split("\n");
|
|
186
|
-
let currentSection = "";
|
|
187
|
-
let currentFilePath = "";
|
|
188
|
-
let currentDesc = "";
|
|
189
|
-
let currentSeverity = "";
|
|
190
|
-
for (const line of lines) {
|
|
191
|
-
const fileMatch = line.match(/^###\s+(.+?)\s*[—–-]+\s*(.+)/);
|
|
192
|
-
if (fileMatch) {
|
|
193
|
-
if (currentFilePath) {
|
|
194
|
-
results.push({ filePath: currentFilePath, description: currentDesc, severity: currentSeverity, content: currentSection });
|
|
195
|
-
}
|
|
196
|
-
currentFilePath = fileMatch[1].trim();
|
|
197
|
-
currentDesc = fileMatch[2].trim();
|
|
198
|
-
currentSeverity = "medium";
|
|
199
|
-
currentSection = line + "\n";
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
const sevMatch = line.match(/- \*\*Severity\*\*:\s*(\w+)/);
|
|
203
|
-
if (sevMatch) currentSeverity = sevMatch[1].toLowerCase();
|
|
204
|
-
currentSection += line + "\n";
|
|
205
|
-
}
|
|
206
|
-
if (currentFilePath) {
|
|
207
|
-
results.push({ filePath: currentFilePath, description: currentDesc, severity: currentSeverity, content: currentSection });
|
|
208
|
-
}
|
|
209
|
-
} catch {
|
|
210
|
-
}
|
|
211
|
-
return results;
|
|
212
|
-
}
|
|
213
|
-
function checkFileGotchas(filePath) {
|
|
214
|
-
const gotchas = getActiveGotchas();
|
|
215
|
-
const warnings = [];
|
|
216
|
-
for (const g of gotchas) {
|
|
217
|
-
if (filePath.includes(g.filePath) || g.filePath.includes(filePath)) {
|
|
218
|
-
const icon = g.severity === "critical" ? "\u{1F534}" : g.severity === "high" ? "\u{1F7E0}" : g.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
219
|
-
warnings.push(`${icon} **Gotcha**: ${g.description} (${g.severity})`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return warnings;
|
|
223
|
-
}
|
|
224
|
-
function generateDigest() {
|
|
225
|
-
const lines = [
|
|
226
|
-
"\u{1F4CB} **Kuma Project Digest**",
|
|
227
|
-
"\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",
|
|
228
|
-
""
|
|
229
|
-
];
|
|
230
|
-
try {
|
|
231
|
-
const fp1 = ensureLayerFile("domain_rules");
|
|
232
|
-
const content = fs.readFileSync(fp1, "utf-8");
|
|
233
|
-
const rules = [];
|
|
234
|
-
const workflows = [];
|
|
235
|
-
for (const line of content.split("\n")) {
|
|
236
|
-
const r = line.match(/^- Rule \d+:?\s*(.+)/);
|
|
237
|
-
if (r) rules.push(r[1].substring(0, 60));
|
|
238
|
-
const w = line.match(/^\d+\.\s*(.+)/);
|
|
239
|
-
if (w) workflows.push(w[1].substring(0, 60));
|
|
240
|
-
}
|
|
241
|
-
if (rules.length > 0) lines.push(`\u{1F4CB} **Domain Rules** (${rules.length}): ${rules.slice(0, 3).join("; ")}`);
|
|
242
|
-
if (workflows.length > 0) lines.push(`\u{1F504} **Workflows**: ${workflows.slice(0, 3).join(" \u2192 ")}`);
|
|
243
|
-
} catch {
|
|
244
|
-
lines.push("\u{1F4CB} **Domain Rules**: not set");
|
|
245
|
-
}
|
|
246
|
-
try {
|
|
247
|
-
const fp2 = ensureLayerFile("arch_flow");
|
|
248
|
-
const content = fs.readFileSync(fp2, "utf-8");
|
|
249
|
-
const chains = [];
|
|
250
|
-
for (const line of content.split("\n")) {
|
|
251
|
-
if (line.includes("->") && line.includes("[")) chains.push(line.trim());
|
|
252
|
-
}
|
|
253
|
-
if (chains.length > 0) lines.push(`\u{1F3D7}\uFE0F **Architecture**: ${chains[0].substring(0, 80)}`);
|
|
254
|
-
else lines.push("\u{1F3D7}\uFE0F **Architecture**: see .kuma/ARCHITECTURE_FLOW.md");
|
|
255
|
-
} catch {
|
|
256
|
-
lines.push("\u{1F3D7}\uFE0F **Architecture**: not documented");
|
|
257
|
-
}
|
|
258
|
-
const gotchas = getActiveGotchas();
|
|
259
|
-
if (gotchas.length > 0) {
|
|
260
|
-
const highCount = gotchas.filter((g) => g.severity === "high" || g.severity === "critical").length;
|
|
261
|
-
lines.push(`\u26A0\uFE0F **Gotchas**: ${gotchas.length} active (${highCount} high/critical)`);
|
|
262
|
-
for (const g of gotchas.slice(0, 3)) {
|
|
263
|
-
lines.push(` \u2022 ${g.filePath}: ${g.description.substring(0, 60)}`);
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
lines.push("\u26A0\uFE0F **Gotchas**: none recorded");
|
|
267
|
-
}
|
|
268
|
-
const summary = sessionMemory.getSummary();
|
|
269
|
-
lines.push(`\u{1F3AF} **Goal**: ${summary.currentGoal?.substring(0, 60) || "not set"}`);
|
|
270
|
-
lines.push(`\u{1F4DD} **Modified**: ${summary.modifiedFiles?.length || 0} files | \u{1F6E0}\uFE0F ${summary.toolCallCount} calls`);
|
|
271
|
-
return lines.join("\n");
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export {
|
|
275
|
-
readLayer,
|
|
276
|
-
writeLayer,
|
|
277
|
-
appendToLayer,
|
|
278
|
-
getLayersSummary,
|
|
279
|
-
getActiveGotchas,
|
|
280
|
-
checkFileGotchas,
|
|
281
|
-
generateDigest
|
|
282
|
-
};
|
package/dist/chunk-GFLSAXAH.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getDb,
|
|
3
|
-
saveDb
|
|
4
|
-
} from "./chunk-3BRBJZ7P.js";
|
|
5
|
-
|
|
6
|
-
// src/engine/safetyAudit.ts
|
|
7
|
-
var SCHEMA_SQL = `
|
|
8
|
-
CREATE TABLE IF NOT EXISTS safety_audit (
|
|
9
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10
|
-
timestamp INTEGER NOT NULL,
|
|
11
|
-
tool_name TEXT NOT NULL,
|
|
12
|
-
action TEXT NOT NULL,
|
|
13
|
-
file_path TEXT,
|
|
14
|
-
risk_level TEXT NOT NULL DEFAULT 'low',
|
|
15
|
-
policy_violations INTEGER DEFAULT 0,
|
|
16
|
-
allowed INTEGER NOT NULL DEFAULT 1,
|
|
17
|
-
duration_ms INTEGER DEFAULT 0,
|
|
18
|
-
metadata TEXT DEFAULT '{}'
|
|
19
|
-
);
|
|
20
|
-
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON safety_audit(timestamp);
|
|
21
|
-
CREATE INDEX IF NOT EXISTS idx_audit_tool ON safety_audit(tool_name);
|
|
22
|
-
CREATE INDEX IF NOT EXISTS idx_audit_risk ON safety_audit(risk_level);
|
|
23
|
-
CREATE INDEX IF NOT EXISTS idx_audit_allowed ON safety_audit(allowed);
|
|
24
|
-
`;
|
|
25
|
-
var schemaEnsured = false;
|
|
26
|
-
async function ensureSchema() {
|
|
27
|
-
if (schemaEnsured) return;
|
|
28
|
-
try {
|
|
29
|
-
const db = await getDb();
|
|
30
|
-
db.exec(SCHEMA_SQL);
|
|
31
|
-
saveDb();
|
|
32
|
-
schemaEnsured = true;
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.error(`[SafetyAudit] Failed to ensure schema: ${err}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function recordAudit(entry) {
|
|
38
|
-
try {
|
|
39
|
-
await ensureSchema();
|
|
40
|
-
const db = await getDb();
|
|
41
|
-
db.run(`
|
|
42
|
-
INSERT INTO safety_audit (timestamp, tool_name, action, file_path, risk_level, policy_violations, allowed, duration_ms, metadata)
|
|
43
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
44
|
-
`, [
|
|
45
|
-
entry.timestamp,
|
|
46
|
-
entry.toolName,
|
|
47
|
-
entry.action,
|
|
48
|
-
entry.filePath || null,
|
|
49
|
-
entry.riskLevel,
|
|
50
|
-
entry.policyViolations,
|
|
51
|
-
entry.allowed ? 1 : 0,
|
|
52
|
-
entry.durationMs,
|
|
53
|
-
JSON.stringify(entry.metadata || {})
|
|
54
|
-
]);
|
|
55
|
-
saveDb();
|
|
56
|
-
} catch (err) {
|
|
57
|
-
console.error(`[SafetyAudit] Failed to record audit: ${err}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async function queryAudit(params) {
|
|
61
|
-
try {
|
|
62
|
-
await ensureSchema();
|
|
63
|
-
const db = await getDb();
|
|
64
|
-
const { toolName, riskLevel, allowed, limit = 20, since } = params;
|
|
65
|
-
let sql = "SELECT * FROM safety_audit WHERE 1=1";
|
|
66
|
-
const bindParams = [];
|
|
67
|
-
if (toolName) {
|
|
68
|
-
sql += " AND tool_name = ?";
|
|
69
|
-
bindParams.push(toolName);
|
|
70
|
-
}
|
|
71
|
-
if (riskLevel) {
|
|
72
|
-
sql += " AND risk_level = ?";
|
|
73
|
-
bindParams.push(riskLevel);
|
|
74
|
-
}
|
|
75
|
-
if (allowed !== void 0) {
|
|
76
|
-
sql += " AND allowed = ?";
|
|
77
|
-
bindParams.push(allowed ? 1 : 0);
|
|
78
|
-
}
|
|
79
|
-
if (since) {
|
|
80
|
-
sql += " AND timestamp >= ?";
|
|
81
|
-
bindParams.push(since);
|
|
82
|
-
}
|
|
83
|
-
sql += " ORDER BY timestamp DESC LIMIT ?";
|
|
84
|
-
bindParams.push(limit);
|
|
85
|
-
const stmt = db.prepare(sql);
|
|
86
|
-
stmt.bind(bindParams);
|
|
87
|
-
const results = [];
|
|
88
|
-
while (stmt.step()) {
|
|
89
|
-
results.push(stmt.getAsObject());
|
|
90
|
-
}
|
|
91
|
-
stmt.free();
|
|
92
|
-
if (results.length === 0) {
|
|
93
|
-
return "\u{1F4CB} **Safety Audit** \u2014 No records found for the given filters.";
|
|
94
|
-
}
|
|
95
|
-
const lines = [
|
|
96
|
-
`\u{1F4CB} **Safety Audit** \u2014 ${results.length} record(s)`,
|
|
97
|
-
`\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`,
|
|
98
|
-
""
|
|
99
|
-
];
|
|
100
|
-
for (const r of results) {
|
|
101
|
-
const riskIcon = r.risk_level === "critical" ? "\u{1F534}" : r.risk_level === "high" ? "\u{1F7E0}" : r.risk_level === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
102
|
-
const allowIcon = r.allowed ? "\u2705" : "\u26D4";
|
|
103
|
-
const time = new Date(r.timestamp * 1e3).toLocaleTimeString();
|
|
104
|
-
lines.push(`${allowIcon} ${riskIcon} **${r.tool_name}** \u2014 ${r.action}`);
|
|
105
|
-
if (r.file_path) lines.push(` \u{1F4CD} ${r.file_path}`);
|
|
106
|
-
lines.push(` \u{1F550} ${time} | ${r.duration_ms}ms | ${r.policy_violations} policy violation(s)`);
|
|
107
|
-
lines.push("");
|
|
108
|
-
}
|
|
109
|
-
return lines.join("\n");
|
|
110
|
-
} catch (err) {
|
|
111
|
-
return `Error querying audit: ${err}`;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async function auditStats() {
|
|
115
|
-
try {
|
|
116
|
-
await ensureSchema();
|
|
117
|
-
const db = await getDb();
|
|
118
|
-
const total = db.exec("SELECT COUNT(*) as c FROM safety_audit")[0]?.values[0][0] ?? 0;
|
|
119
|
-
const blocked = db.exec("SELECT COUNT(*) as c FROM safety_audit WHERE allowed = 0")[0]?.values[0][0] ?? 0;
|
|
120
|
-
const critical = db.exec("SELECT COUNT(*) as c FROM safety_audit WHERE risk_level IN ('high','critical')")[0]?.values[0][0] ?? 0;
|
|
121
|
-
let topBlocked = [];
|
|
122
|
-
try {
|
|
123
|
-
const stmt = db.prepare("SELECT tool_name, COUNT(*) as cnt FROM safety_audit WHERE allowed = 0 GROUP BY tool_name ORDER BY cnt DESC LIMIT 5");
|
|
124
|
-
while (stmt.step()) {
|
|
125
|
-
topBlocked.push(stmt.getAsObject());
|
|
126
|
-
}
|
|
127
|
-
stmt.free();
|
|
128
|
-
} catch {
|
|
129
|
-
}
|
|
130
|
-
const lines = [
|
|
131
|
-
`\u{1F4CA} **Safety Audit Stats**`,
|
|
132
|
-
`\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`,
|
|
133
|
-
"",
|
|
134
|
-
`\u{1F4DD} Total operations: ${total}`,
|
|
135
|
-
`\u26D4 Blocked: ${blocked} (${total > 0 ? (blocked / total * 100).toFixed(1) : 0}%)`,
|
|
136
|
-
`\u{1F534} High/Critical risk: ${critical}`,
|
|
137
|
-
""
|
|
138
|
-
];
|
|
139
|
-
if (topBlocked.length > 0) {
|
|
140
|
-
lines.push("**Most blocked tools:**");
|
|
141
|
-
for (const t of topBlocked) {
|
|
142
|
-
lines.push(` \u2022 ${t.tool_name}: ${t.cnt}x blocked`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return lines.join("\n");
|
|
146
|
-
} catch (err) {
|
|
147
|
-
return `Error getting audit stats: ${err}`;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export {
|
|
152
|
-
recordAudit,
|
|
153
|
-
queryAudit,
|
|
154
|
-
auditStats
|
|
155
|
-
};
|