tribunal-kit 4.4.0 → 4.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/agents/api-architect.md +66 -66
- package/.agent/agents/db-latency-auditor.md +216 -216
- package/.agent/agents/precedence-reviewer.md +250 -250
- package/.agent/agents/resilience-reviewer.md +88 -88
- package/.agent/agents/schema-reviewer.md +67 -67
- package/.agent/agents/throughput-optimizer.md +299 -299
- package/.agent/agents/ui-ux-auditor.md +292 -292
- package/.agent/agents/vitals-reviewer.md +223 -223
- package/.agent/history/architecture-graph.yaml +32 -1
- package/.agent/history/graph-cache.json +66 -19
- package/.agent/history/snapshots/bin__tribunal-kit.js.json +19 -0
- package/.agent/history/snapshots/eslint.config.js.json +9 -0
- package/.agent/history/snapshots/migrate_refs.js.json +3 -3
- package/.agent/history/snapshots/scripts__changelog.js.json +2 -1
- package/.agent/history/snapshots/scripts__sync-version.js.json +2 -1
- package/.agent/history/snapshots/scripts__validate-payload.js.json +1 -0
- package/.agent/history/snapshots/test__integration__bridges.test.js.json +2 -1
- package/.agent/history/snapshots/test__integration__init.test.js.json +1 -0
- package/.agent/history/snapshots/test__integration__routing.test.js.json +1 -0
- package/.agent/history/snapshots/test__integration__swarm_dispatcher.test.js.json +2 -1
- package/.agent/history/snapshots/test__integration__wave2.test.js.json +2 -1
- package/.agent/history/snapshots/test__unit__args.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__case_law_manager.test.js.json +1 -0
- package/.agent/history/snapshots/test__unit__context_broker.test.js.json +11 -0
- package/.agent/history/snapshots/test__unit__copyDir.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__graph_tools.test.js.json +1 -0
- package/.agent/history/snapshots/test__unit__inner_loop_validator.test.js.json +11 -0
- package/.agent/history/snapshots/test__unit__selfInstall.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__semver.test.js.json +11 -1
- package/.agent/history/snapshots/test__unit__swarm_dispatcher.test.js.json +1 -0
- package/.agent/scripts/_colors.js +154 -2
- package/.agent/scripts/_utils.js +205 -3
- package/.agent/scripts/append_flow.js +72 -72
- package/.agent/scripts/auto_preview.js +197 -197
- package/.agent/scripts/bundle_analyzer.js +90 -119
- package/.agent/scripts/case_law_manager.js +18 -13
- package/.agent/scripts/checklist.js +100 -88
- package/.agent/scripts/colors.js +7 -13
- package/.agent/scripts/compress_skills.js +141 -141
- package/.agent/scripts/consolidate_skills.js +149 -149
- package/.agent/scripts/context_broker.js +605 -609
- package/.agent/scripts/deep_compress.js +150 -150
- package/.agent/scripts/dependency_analyzer.js +68 -106
- package/.agent/scripts/graph_builder.js +341 -311
- package/.agent/scripts/graph_visualizer.js +390 -384
- package/.agent/scripts/graph_zoom.js +6 -4
- package/.agent/scripts/inner_loop_validator.js +445 -465
- package/.agent/scripts/lint_runner.js +27 -28
- package/.agent/scripts/minify_context.js +100 -100
- package/.agent/scripts/mutation_runner.js +280 -280
- package/.agent/scripts/patch_skills_meta.js +156 -156
- package/.agent/scripts/patch_skills_output.js +244 -244
- package/.agent/scripts/schema_validator.js +280 -297
- package/.agent/scripts/security_scan.js +37 -64
- package/.agent/scripts/session_manager.js +270 -276
- package/.agent/scripts/skill_evolution.js +637 -644
- package/.agent/scripts/skill_integrator.js +307 -313
- package/.agent/scripts/strengthen_skills.js +193 -193
- package/.agent/scripts/strip_tribunal.js +47 -47
- package/.agent/scripts/swarm_dispatcher.js +360 -360
- package/.agent/scripts/test_runner.js +32 -39
- package/.agent/scripts/utils.js +10 -25
- package/.agent/scripts/verify_all.js +84 -92
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
- package/.agent/skills/doc.md +1 -1
- package/.agent/skills/knowledge-graph/SKILL.md +52 -52
- package/.agent/skills/ui-ux-pro-max/SKILL.md +562 -562
- package/.agent/workflows/generate.md +183 -183
- package/.agent/workflows/tribunal-speed.md +183 -183
- package/README.md +1 -1
- package/bin/tribunal-kit.js +76 -87
- package/package.json +6 -3
- package/scripts/changelog.js +167 -167
- package/scripts/sync-version.js +81 -81
- package/.agent/history/architecture-explorer.html +0 -352
- package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
|
@@ -18,9 +18,14 @@
|
|
|
18
18
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const path = require('path');
|
|
21
|
-
const { spawnSync } = require('child_process');
|
|
22
21
|
|
|
23
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
RED, GREEN, YELLOW, BLUE, BOLD, DIM, CYAN, RESET,
|
|
24
|
+
banner, sectionHeader, timer, formatMs,
|
|
25
|
+
ok, fail, warn, skip,
|
|
26
|
+
} = require('./_colors');
|
|
27
|
+
|
|
28
|
+
const { loadJson, runCommand } = require('./_utils');
|
|
24
29
|
|
|
25
30
|
const HEAVY_PACKAGES = {
|
|
26
31
|
"moment": "Use date-fns or dayjs instead (~2KB vs ~230KB)",
|
|
@@ -35,26 +40,6 @@ const HEAVY_PACKAGES = {
|
|
|
35
40
|
"antd": "Use modular imports with babel-plugin-import",
|
|
36
41
|
};
|
|
37
42
|
|
|
38
|
-
function header(title) {
|
|
39
|
-
console.log(`\n${BOLD}${BLUE}━━━ ${title} ━━━${RESET}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function ok(msg) {
|
|
43
|
-
console.log(` ${GREEN}✅ ${msg}${RESET}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function failPrint(msg) {
|
|
47
|
-
console.log(` ${RED}❌ ${msg}${RESET}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function warn(msg) {
|
|
51
|
-
console.log(` ${YELLOW}⚠️ ${msg}${RESET}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function skip(msg) {
|
|
55
|
-
console.log(` ${YELLOW}⏭️ ${msg}${RESET}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
43
|
function formatSize(sizeBytes) {
|
|
59
44
|
if (sizeBytes < 1024) return `${sizeBytes}B`;
|
|
60
45
|
if (sizeBytes < 1024 * 1024) return `${(sizeBytes / 1024).toFixed(1)}KB`;
|
|
@@ -62,22 +47,19 @@ function formatSize(sizeBytes) {
|
|
|
62
47
|
}
|
|
63
48
|
|
|
64
49
|
function detectBundler(projectRoot) {
|
|
65
|
-
const
|
|
66
|
-
if (!
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return "webpack";
|
|
79
|
-
}
|
|
80
|
-
} catch {}
|
|
50
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
51
|
+
if (!pkg) return null;
|
|
52
|
+
|
|
53
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
54
|
+
|
|
55
|
+
if (deps.vite) return "vite";
|
|
56
|
+
if (deps.next) return "next";
|
|
57
|
+
if (deps.webpack) return "webpack";
|
|
58
|
+
|
|
59
|
+
if (fs.existsSync(path.join(projectRoot, "webpack.config.js")) ||
|
|
60
|
+
fs.existsSync(path.join(projectRoot, "webpack.config.ts"))) {
|
|
61
|
+
return "webpack";
|
|
62
|
+
}
|
|
81
63
|
|
|
82
64
|
return null;
|
|
83
65
|
}
|
|
@@ -91,16 +73,21 @@ function findDistDir(projectRoot) {
|
|
|
91
73
|
return null;
|
|
92
74
|
}
|
|
93
75
|
|
|
94
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Analyze dist directory. Uses inline walker (not shared _utils.walkDir)
|
|
78
|
+
* because it needs to collect file sizes and totals in one pass.
|
|
79
|
+
*/
|
|
80
|
+
function analyzeDist(distDir) {
|
|
95
81
|
const files = [];
|
|
96
82
|
let total = 0;
|
|
97
83
|
|
|
98
|
-
function
|
|
99
|
-
|
|
84
|
+
function _walk(dir) {
|
|
85
|
+
let items;
|
|
86
|
+
try { items = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
100
87
|
for (const item of items) {
|
|
101
88
|
const fpath = path.join(dir, item.name);
|
|
102
89
|
if (item.isDirectory()) {
|
|
103
|
-
|
|
90
|
+
_walk(fpath);
|
|
104
91
|
} else {
|
|
105
92
|
const size = fs.statSync(fpath).size;
|
|
106
93
|
total += size;
|
|
@@ -109,82 +96,58 @@ function analyzeDist(distDir, thresholdKb) {
|
|
|
109
96
|
}
|
|
110
97
|
}
|
|
111
98
|
|
|
112
|
-
|
|
113
|
-
walkDir(distDir);
|
|
114
|
-
} catch {}
|
|
115
|
-
|
|
99
|
+
_walk(distDir);
|
|
116
100
|
files.sort((a, b) => b[1] - a[1]);
|
|
117
101
|
return { total, files };
|
|
118
102
|
}
|
|
119
103
|
|
|
120
104
|
function checkHeavyDependencies(projectRoot) {
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (deps.includes(pkgName)) {
|
|
131
|
-
found.push([pkgName, suggestion]);
|
|
132
|
-
}
|
|
105
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
106
|
+
if (!pkg) return [];
|
|
107
|
+
|
|
108
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
109
|
+
const found = [];
|
|
110
|
+
|
|
111
|
+
for (const [pkgName, suggestion] of Object.entries(HEAVY_PACKAGES)) {
|
|
112
|
+
if (deps.includes(pkgName)) {
|
|
113
|
+
found.push([pkgName, suggestion]);
|
|
133
114
|
}
|
|
134
|
-
return found;
|
|
135
|
-
} catch {
|
|
136
|
-
return [];
|
|
137
115
|
}
|
|
116
|
+
return found;
|
|
138
117
|
}
|
|
139
118
|
|
|
140
119
|
function runBuild(projectRoot) {
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (!pkg.scripts || !pkg.scripts.build) {
|
|
146
|
-
skip("No 'build' script found in package.json");
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
} catch {}
|
|
120
|
+
const pkg = loadJson(path.join(projectRoot, "package.json"));
|
|
121
|
+
if (pkg && (!pkg.scripts || !pkg.scripts.build)) {
|
|
122
|
+
skip("No 'build' script found in package.json");
|
|
123
|
+
return true;
|
|
150
124
|
}
|
|
151
125
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
timeout: 300000,
|
|
158
|
-
shell: process.platform === 'win32'
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
if (result.status === 0) {
|
|
162
|
-
ok("Build completed successfully");
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
126
|
+
const elapsed = timer();
|
|
127
|
+
const result = runCommand('npm', ['run', 'build'], {
|
|
128
|
+
cwd: projectRoot,
|
|
129
|
+
timeout: 300000,
|
|
130
|
+
});
|
|
165
131
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
132
|
+
const ms = elapsed();
|
|
133
|
+
if (result.ok) {
|
|
134
|
+
ok(`Build completed successfully ${DIM}(${formatMs(ms)})${RESET}`);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fail(`Build failed ${DIM}(${formatMs(ms)})${RESET}`);
|
|
139
|
+
const output = (result.stdout + "\n" + result.stderr).trim();
|
|
140
|
+
if (output) {
|
|
141
|
+
for (const line of output.split("\n").slice(0, 10)) {
|
|
142
|
+
console.log(` ${line}`);
|
|
177
143
|
}
|
|
178
|
-
return false;
|
|
179
|
-
} catch (e) {
|
|
180
|
-
failPrint(`Execution error: ${e.message}`);
|
|
181
|
-
return false;
|
|
182
144
|
}
|
|
145
|
+
return false;
|
|
183
146
|
}
|
|
184
147
|
|
|
185
148
|
function main() {
|
|
186
149
|
const args = process.argv.slice(2);
|
|
187
|
-
|
|
150
|
+
|
|
188
151
|
let targetPath = null;
|
|
189
152
|
let buildFlag = false;
|
|
190
153
|
let threshold = 250;
|
|
@@ -193,6 +156,9 @@ function main() {
|
|
|
193
156
|
if (args[i] === '--build') buildFlag = true;
|
|
194
157
|
else if (args[i] === '--threshold' && i + 1 < args.length) {
|
|
195
158
|
threshold = parseInt(args[++i], 10);
|
|
159
|
+
} else if (args[i] === '-h' || args[i] === '--help') {
|
|
160
|
+
console.log("Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]");
|
|
161
|
+
process.exit(0);
|
|
196
162
|
} else if (args[i].startsWith('-')) {
|
|
197
163
|
console.log("Usage: node bundle_analyzer.js <path> [--build] [--threshold <kb>]");
|
|
198
164
|
process.exit(1);
|
|
@@ -208,18 +174,19 @@ function main() {
|
|
|
208
174
|
|
|
209
175
|
const projectRoot = path.resolve(targetPath);
|
|
210
176
|
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
211
|
-
|
|
177
|
+
fail(`Directory not found: ${projectRoot}`);
|
|
212
178
|
process.exit(1);
|
|
213
179
|
}
|
|
214
180
|
|
|
215
|
-
console.log(`${BOLD}Tribunal — bundle_analyzer.js${RESET}`);
|
|
216
|
-
console.log(`Project: ${projectRoot}`);
|
|
217
|
-
|
|
218
181
|
const bundler = detectBundler(projectRoot);
|
|
219
|
-
|
|
182
|
+
console.log(banner('bundle_analyzer.js', {
|
|
183
|
+
Project: projectRoot,
|
|
184
|
+
Bundler: bundler || 'auto-detect',
|
|
185
|
+
Threshold: `${threshold}KB`,
|
|
186
|
+
}));
|
|
220
187
|
|
|
221
188
|
if (buildFlag) {
|
|
222
|
-
|
|
189
|
+
console.log(sectionHeader('Building Project'));
|
|
223
190
|
if (!runBuild(projectRoot)) {
|
|
224
191
|
process.exit(1);
|
|
225
192
|
}
|
|
@@ -228,18 +195,21 @@ function main() {
|
|
|
228
195
|
const distDir = findDistDir(projectRoot);
|
|
229
196
|
const heavy = checkHeavyDependencies(projectRoot);
|
|
230
197
|
|
|
198
|
+
// PERFORMANCE FIX: Cache analyzeDist result — was called TWICE before
|
|
199
|
+
let distResult = null;
|
|
200
|
+
|
|
231
201
|
if (!distDir) {
|
|
232
202
|
skip("No build output directory found (dist/, build/, .next/, out/)");
|
|
233
203
|
skip("Run with --build to create a build first, or build manually");
|
|
234
204
|
} else {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
console.log(`\n Total bundle size: ${BOLD}${formatSize(total)}${RESET}`);
|
|
205
|
+
console.log(sectionHeader(`Bundle Size Analysis (${path.relative(projectRoot, distDir)}/)`));
|
|
206
|
+
distResult = analyzeDist(distDir);
|
|
207
|
+
console.log(`\n Total bundle size: ${BOLD}${formatSize(distResult.total)}${RESET}`);
|
|
238
208
|
|
|
239
209
|
const thresholdBytes = threshold * 1024;
|
|
240
210
|
console.log(`\n ${BOLD}Top files by size:${RESET}`);
|
|
241
211
|
let count = 0;
|
|
242
|
-
for (const [filepath, size] of files) {
|
|
212
|
+
for (const [filepath, size] of distResult.files) {
|
|
243
213
|
if (count++ >= 10) break;
|
|
244
214
|
const sizeStr = formatSize(size).padStart(10, ' ');
|
|
245
215
|
if (size > thresholdBytes) {
|
|
@@ -249,13 +219,13 @@ function main() {
|
|
|
249
219
|
}
|
|
250
220
|
}
|
|
251
221
|
|
|
252
|
-
const largeJs = files.filter(([f, s]) => (f.endsWith('.js') || f.endsWith('.mjs')) && s > thresholdBytes);
|
|
222
|
+
const largeJs = distResult.files.filter(([f, s]) => (f.endsWith('.js') || f.endsWith('.mjs')) && s > thresholdBytes);
|
|
253
223
|
if (largeJs.length > 0) {
|
|
254
224
|
console.log(`\n ${YELLOW}${largeJs.length} JS file(s) exceed ${threshold}KB threshold${RESET}`);
|
|
255
225
|
}
|
|
256
226
|
}
|
|
257
227
|
|
|
258
|
-
|
|
228
|
+
console.log(sectionHeader('Dependency Weight Check'));
|
|
259
229
|
if (heavy.length > 0) {
|
|
260
230
|
for (const [pkgName, suggestion] of heavy) {
|
|
261
231
|
warn(`'${pkgName}' is a heavy dependency`);
|
|
@@ -265,13 +235,13 @@ function main() {
|
|
|
265
235
|
ok("No known-heavy packages detected");
|
|
266
236
|
}
|
|
267
237
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const sizeStr = formatSize(total);
|
|
272
|
-
if (total > 5 * 1024 * 1024) {
|
|
273
|
-
|
|
274
|
-
} else if (total > 2 * 1024 * 1024) {
|
|
238
|
+
// ━━━ Summary ━━━ (reuses cached distResult instead of re-scanning)
|
|
239
|
+
console.log(`\n${BOLD}${CYAN}━━━ Bundle Analysis Summary ━━━${RESET}`);
|
|
240
|
+
if (distResult) {
|
|
241
|
+
const sizeStr = formatSize(distResult.total);
|
|
242
|
+
if (distResult.total > 5 * 1024 * 1024) {
|
|
243
|
+
fail(`Total bundle: ${sizeStr} — consider code splitting`);
|
|
244
|
+
} else if (distResult.total > 2 * 1024 * 1024) {
|
|
275
245
|
warn(`Total bundle: ${sizeStr} — review for optimization opportunities`);
|
|
276
246
|
} else {
|
|
277
247
|
ok(`Total bundle: ${sizeStr}`);
|
|
@@ -280,9 +250,10 @@ function main() {
|
|
|
280
250
|
|
|
281
251
|
if (heavy.length > 0) {
|
|
282
252
|
warn(`${heavy.length} heavy dependency suggestion(s) — see above`);
|
|
283
|
-
} else if (
|
|
253
|
+
} else if (distResult && heavy.length === 0) {
|
|
284
254
|
ok("No optimization suggestions");
|
|
285
255
|
}
|
|
256
|
+
console.log();
|
|
286
257
|
}
|
|
287
258
|
|
|
288
259
|
if (require.main === module) {
|
|
@@ -26,14 +26,7 @@ const crypto = require('crypto');
|
|
|
26
26
|
const readline = require('readline');
|
|
27
27
|
|
|
28
28
|
// ── Colours ──────────────────────────────────────────────────────────────────
|
|
29
|
-
const GREEN
|
|
30
|
-
const YELLOW = '\x1b[93m';
|
|
31
|
-
const CYAN = '\x1b[96m';
|
|
32
|
-
const RED = '\x1b[91m';
|
|
33
|
-
const BLUE = '\x1b[94m';
|
|
34
|
-
const BOLD = '\x1b[1m';
|
|
35
|
-
const DIM = '\x1b[2m';
|
|
36
|
-
const RESET = '\x1b[0m';
|
|
29
|
+
const { GREEN, YELLOW, CYAN, RED, BOLD, DIM, RESET } = require('./_colors');
|
|
37
30
|
|
|
38
31
|
// ── Find .agent directory ─────────────────────────────────────────────────────
|
|
39
32
|
function findAgentDir() {
|
|
@@ -48,10 +41,17 @@ function findAgentDir() {
|
|
|
48
41
|
process.exit(1);
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
// ── Lazy path resolution (avoids side effects at require-time) ───────────────
|
|
45
|
+
let _paths = null;
|
|
46
|
+
function getPaths() {
|
|
47
|
+
if (_paths) return _paths;
|
|
48
|
+
const agentDir = findAgentDir();
|
|
49
|
+
const historyDir = path.join(agentDir, 'history', 'case-law');
|
|
50
|
+
const casesDir = path.join(historyDir, 'cases');
|
|
51
|
+
const indexFile = path.join(historyDir, 'index.json');
|
|
52
|
+
_paths = { AGENT_DIR: agentDir, HISTORY_DIR: historyDir, CASES_DIR: casesDir, INDEX_FILE: indexFile };
|
|
53
|
+
return _paths;
|
|
54
|
+
}
|
|
55
55
|
|
|
56
56
|
const VALID_DOMAINS = new Set(['backend', 'frontend', 'database', 'security', 'performance', 'mobile', 'testing', 'devops', 'general']);
|
|
57
57
|
const VALID_VERDICTS = new Set(['REJECTED', 'APPROVED_WITH_CONDITIONS', 'PRECEDENT_SET', 'OVERRULED']);
|
|
@@ -108,12 +108,14 @@ function contentHash(text) {
|
|
|
108
108
|
|
|
109
109
|
// ── Index helpers ─────────────────────────────────────────────────────────────
|
|
110
110
|
function ensureDirs() {
|
|
111
|
+
const { HISTORY_DIR, CASES_DIR } = getPaths();
|
|
111
112
|
fs.mkdirSync(HISTORY_DIR, { recursive: true });
|
|
112
113
|
fs.mkdirSync(CASES_DIR, { recursive: true });
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
function loadIndex() {
|
|
116
117
|
ensureDirs();
|
|
118
|
+
const { INDEX_FILE } = getPaths();
|
|
117
119
|
if (fs.existsSync(INDEX_FILE)) {
|
|
118
120
|
try { return JSON.parse(fs.readFileSync(INDEX_FILE, 'utf8')); } catch { /* fallthrough */ }
|
|
119
121
|
}
|
|
@@ -122,18 +124,21 @@ function loadIndex() {
|
|
|
122
124
|
|
|
123
125
|
function saveIndex(index) {
|
|
124
126
|
ensureDirs();
|
|
127
|
+
const { INDEX_FILE } = getPaths();
|
|
125
128
|
const tmp = INDEX_FILE + '.tmp';
|
|
126
129
|
fs.writeFileSync(tmp, JSON.stringify(index, null, 2), 'utf8');
|
|
127
130
|
fs.renameSync(tmp, INDEX_FILE);
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
function loadCase(caseId) {
|
|
134
|
+
const { CASES_DIR } = getPaths();
|
|
131
135
|
const p = path.join(CASES_DIR, `case-${String(caseId).padStart(4, '0')}.json`);
|
|
132
136
|
if (!fs.existsSync(p)) return null;
|
|
133
137
|
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
function saveCase(caseRecord) {
|
|
141
|
+
const { CASES_DIR } = getPaths();
|
|
137
142
|
const p = path.join(CASES_DIR, `case-${String(caseRecord.id).padStart(4, '0')}.json`);
|
|
138
143
|
fs.writeFileSync(p, JSON.stringify(caseRecord, null, 2), 'utf8');
|
|
139
144
|
}
|
|
@@ -497,7 +502,7 @@ function cmdExport(args) {
|
|
|
497
502
|
const content = lines.join('\n');
|
|
498
503
|
if (toStdout) { console.log(content); return; }
|
|
499
504
|
|
|
500
|
-
const outPath = path.join(HISTORY_DIR, 'case-law-export.md');
|
|
505
|
+
const outPath = path.join(getPaths().HISTORY_DIR, 'case-law-export.md');
|
|
501
506
|
fs.writeFileSync(outPath, content, 'utf8');
|
|
502
507
|
console.log(`${GREEN}✔ Exported ${cases.length} cases to ${outPath}${RESET}`);
|
|
503
508
|
}
|