tribunal-kit 5.8.2 → 5.8.4
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/ARCHITECTURE.md +12 -12
- package/.agent/agents/complexity-reviewer.md +53 -0
- package/.agent/agents/precedence-reviewer.md +12 -12
- package/.agent/agents/swarm-worker-registry.md +3 -3
- package/.agent/history/integrity_manifest.json +958 -0
- package/.agent/history/memory/.memory.idx +305 -1
- package/.agent/history/memory/MEMORY.md +21 -1
- package/.agent/rules/GEMINI.md +31 -28
- package/.agent/scripts/case_law_manager.js +3 -15
- package/.agent/scripts/context_broker.js +3 -1
- package/.agent/scripts/guardrail_engine.js +643 -0
- package/.agent/scripts/integrity_manifest.js +486 -0
- package/.agent/scripts/signal_detector.js +1 -1
- package/.agent/scripts/skill_evolution.js +3 -15
- package/.agent/scripts/skill_integrator.js +2 -12
- package/.agent/scripts/swarm_dispatcher.js +2 -12
- package/.agent/skills/12-principles-of-animation/SKILL.md +69 -0
- package/.agent/skills/60fps-animation/SKILL.md +78 -0
- package/.agent/skills/accessible-animation/SKILL.md +86 -0
- package/.agent/skills/adapt/SKILL.md +75 -0
- package/.agent/skills/animation-on-scroll/SKILL.md +110 -0
- package/.agent/skills/animation-systems/SKILL.md +69 -0
- package/.agent/skills/antfu-conventions/SKILL.md +74 -0
- package/.agent/skills/app-builder/SKILL.md +1 -1
- package/.agent/skills/apple-design/SKILL.md +108 -0
- package/.agent/skills/audit-and-fix/SKILL.md +83 -0
- package/.agent/skills/baseline-ui/SKILL.md +74 -0
- package/.agent/skills/better-colors/SKILL.md +124 -0
- package/.agent/skills/better-typography/SKILL.md +111 -0
- package/.agent/skills/better-ui/SKILL.md +102 -0
- package/.agent/skills/bolder/SKILL.md +68 -0
- package/.agent/skills/build-primitive/SKILL.md +70 -0
- package/.agent/skills/clarify/SKILL.md +75 -0
- package/.agent/skills/cobejs/SKILL.md +96 -0
- package/.agent/skills/codebase-design/SKILL.md +84 -0
- package/.agent/skills/colorize/SKILL.md +77 -0
- package/.agent/skills/compact-landing/SKILL.md +70 -0
- package/.agent/skills/company-logos/SKILL.md +73 -0
- package/.agent/skills/create-design-md/SKILL.md +104 -0
- package/.agent/skills/critique/SKILL.md +95 -0
- package/.agent/skills/delight/SKILL.md +65 -0
- package/.agent/skills/design-lab/SKILL.md +59 -0
- package/.agent/skills/diagnosing-bugs/SKILL.md +190 -0
- package/.agent/skills/distill/SKILL.md +65 -0
- package/.agent/skills/domain-modeling/SKILL.md +78 -0
- package/.agent/skills/fixing-accessibility/SKILL.md +98 -0
- package/.agent/skills/fixing-metadata/SKILL.md +97 -0
- package/.agent/skills/fixing-motion-performance/SKILL.md +96 -0
- package/.agent/skills/gpt-taste/SKILL.md +68 -0
- package/.agent/skills/harden/SKILL.md +74 -0
- package/.agent/skills/impeccable/SKILL.md +72 -0
- package/.agent/skills/improve-codebase-architecture/SKILL.md +67 -0
- package/.agent/skills/improve-ui/SKILL.md +83 -0
- package/.agent/skills/landing-page/SKILL.md +88 -0
- package/.agent/skills/lint-and-validate/SKILL.md +1 -2
- package/.agent/skills/local-first-architecture/SKILL.md +66 -0
- package/.agent/skills/lottie-animation/SKILL.md +82 -0
- package/.agent/skills/marquee-loop/SKILL.md +96 -0
- package/.agent/skills/masked-reveal/SKILL.md +88 -0
- package/.agent/skills/micro-interaction/SKILL.md +100 -0
- package/.agent/skills/morphing-icons/SKILL.md +85 -0
- package/.agent/skills/page-transition-animation/SKILL.md +108 -0
- package/.agent/skills/parallel-agents/SKILL.md +5 -5
- package/.agent/skills/polish/SKILL.md +63 -0
- package/.agent/skills/pricing-page/SKILL.md +68 -0
- package/.agent/skills/progressive-blur/SKILL.md +100 -0
- package/.agent/skills/project-idioms/SKILL.md +7 -7
- package/.agent/skills/quieter/SKILL.md +68 -0
- package/.agent/skills/react-doctor/SKILL.md +70 -0
- package/.agent/skills/redesign-skill/SKILL.md +65 -0
- package/.agent/skills/shape/SKILL.md +73 -0
- package/.agent/skills/soft-skill/SKILL.md +72 -0
- package/.agent/skills/sounds-on-the-web/SKILL.md +100 -0
- package/.agent/skills/svg-animation/SKILL.md +91 -0
- package/.agent/skills/swiss-design/SKILL.md +64 -0
- package/.agent/skills/taste-skill/SKILL.md +71 -0
- package/.agent/skills/tdd-workflow/SKILL.md +36 -165
- package/.agent/skills/test-result-analyzer/SKILL.md +1 -1
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +82 -0
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +66 -0
- package/.agent/skills/transitions-dev/SKILL.md +101 -0
- package/.agent/skills/typeset/SKILL.md +76 -0
- package/.agent/skills/ui-skills-root/SKILL.md +85 -0
- package/.agent/skills/web-quality-audit/SKILL.md +67 -0
- package/.agent/workflows/fix.md +2 -2
- package/.agent/workflows/preview.md +5 -5
- package/.agent/workflows/status.md +1 -1
- package/.agent/workflows/tribunal-full.md +3 -3
- package/.agent/workflows/tribunal-speed.md +2 -2
- package/README.md +348 -238
- package/bin/tribunal-kit.js +62 -1482
- package/dist/cli.js +13 -0
- package/dist/commands/align.js +1 -1
- package/dist/commands/compile.js +1 -1
- package/dist/commands/guardrail.js +275 -0
- package/dist/commands/init.js +10 -9
- package/dist/commands/optimize.js +318 -0
- package/dist/utils/version.js +1 -1
- package/package.json +10 -8
- package/scripts/fix-vbc.js +98 -0
- package/scripts/fix-vbc.ps1 +119 -0
package/bin/tribunal-kit.js
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
* case — Manage Case Law precedents
|
|
11
11
|
* hook — Install pre-push git hook
|
|
12
12
|
* uninstall — Remove .agent/ from project
|
|
13
|
+
* align — Clean AI outputs & enforce guardrails
|
|
14
|
+
* compile — Compile rules for terminal agents
|
|
15
|
+
* memory — 4-Type Taxonomy Persistent Memory Engine
|
|
16
|
+
* guardrail — Validate .agent/ integrity
|
|
13
17
|
*
|
|
14
18
|
* Usage:
|
|
15
19
|
* npx tribunal-kit init
|
|
@@ -22,1513 +26,89 @@
|
|
|
22
26
|
* tribunal-kit uninstall
|
|
23
27
|
*/
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
"use strict";
|
|
30
|
+
|
|
26
31
|
const path = require("path");
|
|
27
|
-
const https = require("https");
|
|
28
|
-
const { execSync, spawn } = require("child_process");
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* No shell interpolation — immune to injection.
|
|
33
|
-
*/
|
|
34
|
-
function runScriptAsync(scriptPath, args = [], options = {}) {
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
37
|
-
stdio: "inherit",
|
|
38
|
-
...options,
|
|
39
|
-
});
|
|
40
|
-
child.on("close", (code) => {
|
|
41
|
-
if (code !== 0) reject(new Error(`Script failed with exit code ${code}`));
|
|
42
|
-
else resolve();
|
|
43
|
-
});
|
|
44
|
-
child.on("error", reject);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
33
|
+
// Delegate core execution to the modular dist/ entry point
|
|
34
|
+
const { main } = require("../dist/cli.js");
|
|
47
35
|
|
|
48
|
-
|
|
49
|
-
const
|
|
36
|
+
// Utilities re-exported for backwards compatibility with tests
|
|
37
|
+
const { compareSemver } = require("../dist/utils/version");
|
|
38
|
+
const { copyDir, countDir, isSelfInstall: _isSelfInstall } = require("../dist/utils/fs");
|
|
39
|
+
const { CORE_AGENTS, CORE_SKILLS, generateIDEBridges } = require("../dist/commands/init");
|
|
40
|
+
const { cmdMarathon: _cmdMarathon } = require("../dist/commands/marathon");
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
reset: "\x1b[0m",
|
|
54
|
-
bold: "\x1b[1m",
|
|
55
|
-
dim: "\x1b[2m",
|
|
56
|
-
red: "\x1b[91m",
|
|
57
|
-
green: "\x1b[92m",
|
|
58
|
-
yellow: "\x1b[93m",
|
|
59
|
-
blue: "\x1b[94m",
|
|
60
|
-
magenta: "\x1b[95m",
|
|
61
|
-
cyan: "\x1b[96m",
|
|
62
|
-
white: "\x1b[97m",
|
|
63
|
-
gray: "\x1b[90m",
|
|
64
|
-
bgCyan: "\x1b[46m",
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
function colorize(color, text) {
|
|
68
|
-
return `${C[color]}${text}${C.reset}`;
|
|
42
|
+
function cmdMarathon(flags, processArgs = process.argv) {
|
|
43
|
+
return _cmdMarathon(flags, processArgs, flags?.quiet || false);
|
|
69
44
|
}
|
|
70
45
|
|
|
71
|
-
function c(color, text) {
|
|
72
|
-
return `${C[color]}${text}${C.reset}`;
|
|
73
|
-
}
|
|
74
|
-
function bold(text) {
|
|
75
|
-
return `${C.bold}${text}${C.reset}`;
|
|
76
|
-
}
|
|
77
46
|
|
|
78
|
-
|
|
79
|
-
let quiet = false;
|
|
80
|
-
let verbose = false;
|
|
47
|
+
const PKG = require(path.resolve(__dirname, "..", "package.json"));
|
|
81
48
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
function warn(msg) {
|
|
89
|
-
if (!quiet) console.log(` ${c("yellow", "⚠")} ${msg}`);
|
|
90
|
-
}
|
|
91
|
-
function err(msg) {
|
|
92
|
-
console.error(` ${c("red", "✖")} ${msg}`);
|
|
93
|
-
}
|
|
94
|
-
function dim(msg) {
|
|
95
|
-
if (!quiet) console.log(` ${c("gray", msg)}`);
|
|
96
|
-
}
|
|
97
|
-
function dbg(msg) {
|
|
98
|
-
if (verbose) console.log(` ${c("gray", "⊡")} ${c("gray", msg)}`);
|
|
49
|
+
/**
|
|
50
|
+
* Returns true if the target directory IS the tribunal-kit package itself.
|
|
51
|
+
*/
|
|
52
|
+
function isSelfInstall(targetDir) {
|
|
53
|
+
const kitRoot = path.resolve(__dirname, "..");
|
|
54
|
+
return _isSelfInstall(targetDir, PKG.name, kitRoot);
|
|
99
55
|
}
|
|
100
56
|
|
|
101
|
-
|
|
57
|
+
/**
|
|
58
|
+
* CLI Argument Parser for backward compatibility with unit tests.
|
|
59
|
+
*/
|
|
102
60
|
function parseArgs(argv) {
|
|
103
61
|
const args = { command: null, flags: {} };
|
|
104
62
|
const raw = argv.slice(2);
|
|
105
63
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!arg.startsWith("--") && !args.command) {
|
|
64
|
+
for (let i = 0; i < raw.length; i++) {
|
|
65
|
+
const arg = raw[i];
|
|
66
|
+
if (!arg.startsWith("--") && !arg.startsWith("-") && !args.command) {
|
|
109
67
|
args.command = arg;
|
|
110
68
|
continue;
|
|
111
69
|
}
|
|
112
|
-
if (arg === "--force")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (arg === "--
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (arg
|
|
121
|
-
args.flags.verbose = true;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (arg === "--dry-run") {
|
|
125
|
-
args.flags.dryRun = true;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (arg === "--minimal") {
|
|
129
|
-
args.flags.minimal = true;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (arg === "--token-optimized") {
|
|
133
|
-
args.flags.tokenOptimized = true;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (arg === "--skip-update-check") {
|
|
137
|
-
args.flags.skipUpdateCheck = true;
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (arg === "--head") {
|
|
141
|
-
args.flags.head = true;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
if (arg.startsWith("--path=")) {
|
|
70
|
+
if (arg === "--force") args.flags.force = true;
|
|
71
|
+
else if (arg === "--quiet") args.flags.quiet = true;
|
|
72
|
+
else if (arg === "--verbose") args.flags.verbose = true;
|
|
73
|
+
else if (arg === "--dry-run") args.flags.dryRun = true;
|
|
74
|
+
else if (arg === "--minimal") args.flags.minimal = true;
|
|
75
|
+
else if (arg === "--token-optimized") args.flags.tokenOptimized = true;
|
|
76
|
+
else if (arg === "--skip-update-check") args.flags.skipUpdateCheck = true;
|
|
77
|
+
else if (arg === "--head") args.flags.head = true;
|
|
78
|
+
else if (arg.startsWith("--path=")) {
|
|
145
79
|
args.flags.path = arg.split("=").slice(1).join("=");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const nextVal = raw[idx + 1];
|
|
150
|
-
if (!nextVal || nextVal.startsWith("--")) {
|
|
151
|
-
console.error(
|
|
152
|
-
` \x1b[91m✖ --path requires a directory argument\x1b[0m`,
|
|
153
|
-
);
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
156
|
-
args.flags.path = nextVal;
|
|
157
|
-
}
|
|
158
|
-
if (arg.startsWith("--branch=")) {
|
|
80
|
+
} else if (arg === "--path" && raw[i + 1] && !raw[i + 1].startsWith("-")) {
|
|
81
|
+
args.flags.path = raw[++i];
|
|
82
|
+
} else if (arg.startsWith("--branch=")) {
|
|
159
83
|
args.flags.branch = arg.split("=").slice(1).join("=");
|
|
160
|
-
}
|
|
161
|
-
if (arg.startsWith("--log=")) {
|
|
84
|
+
} else if (arg.startsWith("--log=")) {
|
|
162
85
|
args.flags.log = arg.split("=").slice(1).join("=");
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const nextVal = raw[idx + 1];
|
|
167
|
-
if (!nextVal || nextVal.startsWith("--")) {
|
|
168
|
-
console.error(
|
|
169
|
-
` \x1b[91m✖ --log requires a file path argument\x1b[0m`,
|
|
170
|
-
);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
args.flags.log = nextVal;
|
|
174
|
-
}
|
|
175
|
-
if (arg.startsWith("--strategy=")) {
|
|
86
|
+
} else if (arg === "--log" && raw[i + 1] && !raw[i + 1].startsWith("-")) {
|
|
87
|
+
args.flags.log = raw[++i];
|
|
88
|
+
} else if (arg.startsWith("--strategy=")) {
|
|
176
89
|
args.flags.strategy = arg.split("=").slice(1).join("=");
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const idx = raw.indexOf("--strategy");
|
|
180
|
-
const nextVal = raw[idx + 1];
|
|
181
|
-
if (!nextVal || nextVal.startsWith("--")) {
|
|
182
|
-
console.error(
|
|
183
|
-
` \x1b[91m✖ --strategy requires a strategy value (balanced/harden/repair-only)\x1b[0m`,
|
|
184
|
-
);
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
args.flags.strategy = nextVal;
|
|
90
|
+
} else if (arg === "--strategy" && raw[i + 1] && !raw[i + 1].startsWith("-")) {
|
|
91
|
+
args.flags.strategy = raw[++i];
|
|
188
92
|
}
|
|
189
93
|
}
|
|
190
94
|
|
|
191
95
|
return args;
|
|
192
96
|
}
|
|
193
97
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
// Core agents to install in --minimal mode
|
|
197
|
-
const CORE_AGENTS = new Set([
|
|
198
|
-
"backend-specialist.md",
|
|
199
|
-
"frontend-specialist.md",
|
|
200
|
-
"database-architect.md",
|
|
201
|
-
"debugger.md",
|
|
202
|
-
"security-auditor.md",
|
|
203
|
-
"logic-reviewer.md",
|
|
204
|
-
"dependency-reviewer.md",
|
|
205
|
-
"type-safety-reviewer.md",
|
|
206
|
-
"performance-reviewer.md",
|
|
207
|
-
"orchestrator.md",
|
|
208
|
-
"explorer-agent.md",
|
|
209
|
-
"project-planner.md",
|
|
210
|
-
"test-engineer.md",
|
|
211
|
-
]);
|
|
212
|
-
|
|
213
|
-
// Core skills to install in --minimal mode
|
|
214
|
-
const CORE_SKILLS = new Set([
|
|
215
|
-
"clean-code",
|
|
216
|
-
"architecture",
|
|
217
|
-
"testing-patterns",
|
|
218
|
-
"systematic-debugging",
|
|
219
|
-
"frontend-design",
|
|
220
|
-
"database-design",
|
|
221
|
-
"api-patterns",
|
|
222
|
-
"nodejs-best-practices",
|
|
223
|
-
"vulnerability-scanner",
|
|
224
|
-
"typescript-advanced",
|
|
225
|
-
"python-pro",
|
|
226
|
-
"nextjs-react-expert",
|
|
227
|
-
"react-specialist",
|
|
228
|
-
"performance-profiling",
|
|
229
|
-
"lint-and-validate",
|
|
230
|
-
]);
|
|
231
|
-
|
|
232
|
-
async function copyDir(src, dest, dryRun = false, filter = null) {
|
|
233
|
-
if (!dryRun) {
|
|
234
|
-
await fs.promises.mkdir(dest, { recursive: true });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const entries = await fs.promises.readdir(src, { withFileTypes: true });
|
|
238
|
-
let count = 0;
|
|
239
|
-
|
|
240
|
-
for (const entry of entries) {
|
|
241
|
-
// Apply filter if provided (for --minimal mode)
|
|
242
|
-
if (filter && !filter(entry.name, src)) {
|
|
243
|
-
dbg(` skip: ${entry.name}`);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const srcPath = path.join(src, entry.name);
|
|
248
|
-
const destPath = path.join(dest, entry.name);
|
|
249
|
-
|
|
250
|
-
if (entry.isDirectory()) {
|
|
251
|
-
count += await copyDir(srcPath, destPath, dryRun, filter);
|
|
252
|
-
} else {
|
|
253
|
-
if (!dryRun) {
|
|
254
|
-
await fs.promises.copyFile(srcPath, destPath);
|
|
255
|
-
}
|
|
256
|
-
dbg(` copy: ${entry.name}`);
|
|
257
|
-
count++;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return count;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async function countDir(dir) {
|
|
265
|
-
let count = 0;
|
|
266
|
-
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
267
|
-
for (const e of entries) {
|
|
268
|
-
if (e.isDirectory()) count += await countDir(path.join(dir, e.name));
|
|
269
|
-
else count++;
|
|
270
|
-
}
|
|
271
|
-
return count;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ── Version Check & Auto-Update ──────────────────────────
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Compare two semver strings. Returns:
|
|
278
|
-
* 1 if a > b, -1 if a < b, 0 if equal.
|
|
279
|
-
*/
|
|
280
|
-
function compareSemver(a, b) {
|
|
281
|
-
const pa = a.replace(/^v/, "").split(".").map(Number);
|
|
282
|
-
const pb = b.replace(/^v/, "").split(".").map(Number);
|
|
283
|
-
for (let i = 0; i < 3; i++) {
|
|
284
|
-
const na = pa[i] || 0;
|
|
285
|
-
const nb = pb[i] || 0;
|
|
286
|
-
if (na > nb) return 1;
|
|
287
|
-
if (na < nb) return -1;
|
|
288
|
-
}
|
|
289
|
-
return 0;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Fetch the latest version from npm registry.
|
|
294
|
-
* Returns the version string (e.g. '4.0.0') or null on failure.
|
|
295
|
-
*/
|
|
296
|
-
function fetchLatestVersion() {
|
|
297
|
-
return new Promise((resolve) => {
|
|
298
|
-
const req = https.get(
|
|
299
|
-
"https://registry.npmjs.org/tribunal-kit/latest",
|
|
300
|
-
{
|
|
301
|
-
headers: {
|
|
302
|
-
Accept: "application/json",
|
|
303
|
-
"User-Agent": `tribunal-kit/${CURRENT_VERSION}`,
|
|
304
|
-
},
|
|
305
|
-
timeout: 5000,
|
|
306
|
-
},
|
|
307
|
-
(res) => {
|
|
308
|
-
let data = "";
|
|
309
|
-
res.on("data", (chunk) => {
|
|
310
|
-
data += chunk;
|
|
311
|
-
});
|
|
312
|
-
res.on("end", () => {
|
|
313
|
-
try {
|
|
314
|
-
const json = JSON.parse(data);
|
|
315
|
-
const version = json.version || null;
|
|
316
|
-
resolve(version);
|
|
317
|
-
} catch {
|
|
318
|
-
resolve(null);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
},
|
|
322
|
-
);
|
|
323
|
-
req.on("error", () => resolve(null));
|
|
324
|
-
req.on("timeout", () => {
|
|
325
|
-
req.destroy();
|
|
326
|
-
resolve(null);
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Check for a newer version and re-invoke with @latest if found.
|
|
333
|
-
* Uses TK_SKIP_UPDATE_CHECK env var as recursion guard.
|
|
334
|
-
* Returns true if a re-invoke happened (caller should exit), false otherwise.
|
|
335
|
-
*/
|
|
336
|
-
async function autoUpdateCheck(originalArgs) {
|
|
337
|
-
// Recursion guard: if we're already a re-invoked process, skip
|
|
338
|
-
if (process.env.TK_SKIP_UPDATE_CHECK === "1") {
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
log(" Checking for updates...");
|
|
343
|
-
const latestVersion = await fetchLatestVersion();
|
|
344
|
-
|
|
345
|
-
if (!latestVersion) {
|
|
346
|
-
// Network fail — proceed silently with current version
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (compareSemver(latestVersion, CURRENT_VERSION) <= 0) {
|
|
351
|
-
// Already up to date
|
|
352
|
-
dim(`Version ${CURRENT_VERSION} is up to date.`);
|
|
353
|
-
return false;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Newer version available — re-invoke
|
|
357
|
-
log("");
|
|
358
|
-
log(
|
|
359
|
-
colorize(
|
|
360
|
-
"cyan",
|
|
361
|
-
` ⬆ New version available: ${colorize("bold", CURRENT_VERSION)} → ${colorize("bold", latestVersion)}`,
|
|
362
|
-
),
|
|
363
|
-
);
|
|
364
|
-
log(colorize("gray", " Re-invoking with latest version..."));
|
|
365
|
-
log("");
|
|
366
|
-
|
|
367
|
-
try {
|
|
368
|
-
// Build the command pulling from npm registry
|
|
369
|
-
const args = originalArgs.join(" ");
|
|
370
|
-
const cmd = `npx -y tribunal-kit@${latestVersion} ${args}`;
|
|
371
|
-
|
|
372
|
-
execSync(cmd, {
|
|
373
|
-
stdio: "inherit",
|
|
374
|
-
env: { ...process.env, TK_SKIP_UPDATE_CHECK: "1" },
|
|
375
|
-
});
|
|
376
|
-
return true; // Re-invoke succeeded, caller should exit
|
|
377
|
-
} catch (e) {
|
|
378
|
-
warn(`Auto-update failed: ${e.message}`);
|
|
379
|
-
warn("Continuing with current version...");
|
|
380
|
-
return false; // Fall through to current version
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// ── Kit Source Location ───────────────────────────────────
|
|
385
|
-
function getKitAgent() {
|
|
386
|
-
// When installed via npm, the .agent/ folder is next to this script's package
|
|
387
|
-
const kitRoot = path.resolve(__dirname, "..");
|
|
388
|
-
const agentDir = path.join(kitRoot, ".agent");
|
|
389
|
-
|
|
390
|
-
if (!fs.existsSync(agentDir)) {
|
|
391
|
-
err(`Kit .agent/ folder not found at: ${agentDir}`);
|
|
392
|
-
err("The package may be corrupted. Try: npm install -g tribunal-kit");
|
|
393
|
-
process.exit(1);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return agentDir;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// ── Self-Install Guard ────────────────────────────────────
|
|
400
|
-
/**
|
|
401
|
-
* Returns true if the target directory IS the tribunal-kit package itself.
|
|
402
|
-
* This prevents `init --force` / `update` from deleting the package's own files
|
|
403
|
-
* when run from inside the project directory.
|
|
404
|
-
*/
|
|
405
|
-
function isSelfInstall(targetDir) {
|
|
406
|
-
const kitRoot = path.resolve(__dirname, "..");
|
|
407
|
-
const resolvedTarget = path.resolve(targetDir);
|
|
408
|
-
|
|
409
|
-
// Direct path match
|
|
410
|
-
if (resolvedTarget === kitRoot) return true;
|
|
411
|
-
|
|
412
|
-
// Check if the target's package.json is this package
|
|
413
|
-
const targetPkg = path.join(resolvedTarget, "package.json");
|
|
414
|
-
if (fs.existsSync(targetPkg)) {
|
|
415
|
-
try {
|
|
416
|
-
const targetName = JSON.parse(fs.readFileSync(targetPkg, "utf8")).name;
|
|
417
|
-
if (targetName === PKG.name) return true;
|
|
418
|
-
} catch {
|
|
419
|
-
// Unreadable package.json — not a match
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// ── Banner ────────────────────────────────────────────────
|
|
427
|
-
function banner() {
|
|
428
|
-
if (quiet) return;
|
|
429
|
-
// Big ASCII art (TRIBUNAL-KIT)
|
|
430
|
-
const art = String.raw`
|
|
431
|
-
████████╗██████╗ ██╗██████╗ ██╗ ██╗███╗ ██╗ █████╗ ██╗ ██╗ ██╗██╗████████╗
|
|
432
|
-
╚══██╔══╝██╔══██╗██║██╔══██╗██║ ██║████╗ ██║██╔══██╗██║ ██║ ██╔╝██║╚══██╔══╝
|
|
433
|
-
██║ ██████╔╝██║██████╔╝██║ ██║██╔██╗ ██║███████║██║█████╗█████╔╝ ██║ ██║
|
|
434
|
-
██║ ██╔══██╗██║██╔══██╗██║ ██║██║╚██╗██║██╔══██║██║╚════╝██╔═██╗ ██║ ██║
|
|
435
|
-
██║ ██║ ██║██║██████╔╝╚██████╔╝██║ ╚████║██║ ██║███████╗ ██║ ██╗██║ ██║
|
|
436
|
-
╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ `
|
|
437
|
-
.split("\n")
|
|
438
|
-
.filter(Boolean);
|
|
439
|
-
console.log();
|
|
440
|
-
const _maxLen = Math.max(...art.map((line) => line.length));
|
|
441
|
-
for (const line of art) {
|
|
442
|
-
let gradientLine = " " + C.bold;
|
|
443
|
-
const len = line.length;
|
|
444
|
-
for (let i = 0; i < len; i++) {
|
|
445
|
-
const char = line[i];
|
|
446
|
-
if (char === ' ') {
|
|
447
|
-
gradientLine += ' ';
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
// Horizontal gradient: flame red (left) to coral/orange (right)
|
|
451
|
-
const ratio = i / len;
|
|
452
|
-
const r = 255;
|
|
453
|
-
const g = Math.floor(30 + ratio * 100);
|
|
454
|
-
const b = Math.floor(60 - ratio * 40);
|
|
455
|
-
gradientLine += `\x1b[38;2;${r};${g};${b}m${char}`;
|
|
456
|
-
}
|
|
457
|
-
gradientLine += C.reset;
|
|
458
|
-
log(gradientLine);
|
|
459
|
-
}
|
|
460
|
-
console.log();
|
|
461
|
-
// Subtitle strip
|
|
462
|
-
const W = 84;
|
|
463
|
-
const plainSub = '🛡️ ANTI-HALLUCINATION AGENT SYSTEM';
|
|
464
|
-
const coloredSub = `${bold(c('white', '🛡️ ANTI-HALLUCINATION AGENT SYSTEM'))}`;
|
|
465
|
-
const sp = Math.max(0, W - plainSub.length);
|
|
466
|
-
const centred = ' '.repeat(Math.floor(sp / 2)) + coloredSub + ' '.repeat(Math.ceil(sp / 2));
|
|
467
|
-
log(` ${c('gray', `✦ ${'━'.repeat(W - 6)} ✦`)}`);
|
|
468
|
-
log(` ${centred}`);
|
|
469
|
-
log(` ${c('gray', `✦ ${'━'.repeat(W - 6)} ✦`)}`);
|
|
470
|
-
console.log();
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// ── Commands ──────────────────────────────────────────────
|
|
474
|
-
async function cmdInit(flags) {
|
|
475
|
-
const agentSrc = getKitAgent();
|
|
476
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
477
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
478
|
-
const dryRun = flags.dryRun || false;
|
|
479
|
-
|
|
480
|
-
// ── Self-install guard ──────────────────────────────────
|
|
481
|
-
if (isSelfInstall(targetDir)) {
|
|
482
|
-
err("Cannot run init/update inside the tribunal-kit package itself.");
|
|
483
|
-
err(`Target: ${targetDir}`);
|
|
484
|
-
err(`Package: ${path.resolve(__dirname, "..")}`);
|
|
485
|
-
console.log();
|
|
486
|
-
dim("This command is designed to install .agent/ into OTHER projects.");
|
|
487
|
-
dim("Run it from the root of the project you want to set up:");
|
|
488
|
-
dim(" cd /path/to/your-project");
|
|
489
|
-
dim(" npx tribunal-kit init");
|
|
490
|
-
console.log();
|
|
491
|
-
process.exit(1);
|
|
492
|
-
}
|
|
493
|
-
// ────────────────────────────────────────────────────────
|
|
494
|
-
|
|
495
|
-
// ── Backup / Cleanup ────────────────────────────────────
|
|
496
|
-
if (!dryRun && fs.existsSync(agentDest) && flags.force) {
|
|
497
|
-
// Backup the existing subdirectories before overwriting
|
|
498
|
-
const backupDir = path.join(agentDest, ".backups", `backup-${Date.now()}`);
|
|
499
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
500
|
-
|
|
501
|
-
const subdirs = [
|
|
502
|
-
"agents",
|
|
503
|
-
"workflows",
|
|
504
|
-
"skills",
|
|
505
|
-
"scripts",
|
|
506
|
-
".shared",
|
|
507
|
-
"rules",
|
|
508
|
-
];
|
|
509
|
-
for (const sub of subdirs) {
|
|
510
|
-
const subPath = path.join(agentDest, sub);
|
|
511
|
-
if (fs.existsSync(subPath)) {
|
|
512
|
-
// Copy to backup dir
|
|
513
|
-
await copyDir(subPath, path.join(backupDir, sub), false);
|
|
514
|
-
// Removed aggressive deletion so user custom files persist
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
log(
|
|
518
|
-
` ${c("gray", "✦ Backed up existing configurations to .agent/.backups/")}`,
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
// ────────────────────────────────────────────────────────
|
|
522
|
-
|
|
523
|
-
banner();
|
|
524
|
-
|
|
525
|
-
if (dryRun) {
|
|
526
|
-
log(colorize("yellow", " DRY RUN — no files will be written"));
|
|
527
|
-
console.log();
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Check target exists
|
|
531
|
-
if (!fs.existsSync(targetDir)) {
|
|
532
|
-
err(`Target directory not found: ${targetDir}`);
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Check if .agent already exists
|
|
537
|
-
if (fs.existsSync(agentDest) && !flags.force) {
|
|
538
|
-
warn(".agent/ already exists in this project.");
|
|
539
|
-
log(
|
|
540
|
-
` ${c("gray", "▸")} To refresh or update it, run: ${colorize("white", "tribunal-kit init --force")}`,
|
|
541
|
-
);
|
|
542
|
-
log(
|
|
543
|
-
` ${c("gray", "▸")} Or check status with: ${colorize("cyan", "tribunal-kit status")}`,
|
|
544
|
-
);
|
|
545
|
-
console.log();
|
|
546
|
-
process.exit(0);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Ensure history dirs exist (Case Law + Skill Evolution)
|
|
550
|
-
if (!dryRun) {
|
|
551
|
-
const caseDir = path.join(agentDest, "history", "case-law", "cases");
|
|
552
|
-
const evoDir = path.join(agentDest, "history", "skill-evolution");
|
|
553
|
-
fs.mkdirSync(caseDir, { recursive: true });
|
|
554
|
-
fs.mkdirSync(evoDir, { recursive: true });
|
|
555
|
-
const gkCase = path.join(caseDir, ".gitkeep");
|
|
556
|
-
const gkEvo = path.join(evoDir, ".gitkeep");
|
|
557
|
-
if (!fs.existsSync(gkCase)) fs.writeFileSync(gkCase, "");
|
|
558
|
-
if (!fs.existsSync(gkEvo)) fs.writeFileSync(gkEvo, "");
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Count what we're installing
|
|
562
|
-
const isMinimal = flags.minimal || false;
|
|
563
|
-
if (isMinimal) {
|
|
564
|
-
log(
|
|
565
|
-
` ${c("yellow", "⚡")} ${bold("Minimal mode")} — installing core agents and skills only`,
|
|
566
|
-
);
|
|
567
|
-
console.log();
|
|
568
|
-
}
|
|
569
|
-
const totalFiles = await countDir(agentSrc);
|
|
570
|
-
dbg(`Source: ${agentSrc}`);
|
|
571
|
-
dbg(`Target: ${agentDest}`);
|
|
572
|
-
dbg(`Total source files: ${totalFiles}`);
|
|
573
|
-
log(
|
|
574
|
-
` ${c("gray", "▸")} Scanning ${c("white", String(totalFiles))} files ${c("gray", "→")} ${c("gray", agentDest)}`,
|
|
575
|
-
);
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
// Build filter for --minimal mode
|
|
579
|
-
const minimalFilter = isMinimal
|
|
580
|
-
? (name, parentDir) => {
|
|
581
|
-
const parentName = path.basename(parentDir);
|
|
582
|
-
if (parentName === "agents") return CORE_AGENTS.has(name);
|
|
583
|
-
if (parentName === "skills") return CORE_SKILLS.has(name);
|
|
584
|
-
return true; // everything else passes
|
|
585
|
-
}
|
|
586
|
-
: null;
|
|
587
|
-
|
|
588
|
-
const copied = await copyDir(agentSrc, agentDest, dryRun, minimalFilter);
|
|
589
|
-
|
|
590
|
-
console.log();
|
|
591
|
-
if (dryRun) {
|
|
592
|
-
ok(
|
|
593
|
-
`${bold("DRY RUN")} complete — would install ${c("cyan", String(copied))} files`,
|
|
594
|
-
);
|
|
595
|
-
dim(`Target: ${agentDest}`);
|
|
596
|
-
} else {
|
|
597
|
-
// ── Success card — W=62, rows padded by plain-text length ──
|
|
598
|
-
const W = 62;
|
|
599
|
-
const borderCol = "red";
|
|
600
|
-
const agentsCount = fs.readdirSync(path.join(agentDest, "agents")).length;
|
|
601
|
-
const workflowsCount = fs.readdirSync(
|
|
602
|
-
path.join(agentDest, "workflows"),
|
|
603
|
-
).length;
|
|
604
|
-
const skillsCount = fs.readdirSync(path.join(agentDest, "skills")).length;
|
|
605
|
-
const scriptsCount = fs.readdirSync(
|
|
606
|
-
path.join(agentDest, "scripts"),
|
|
607
|
-
).length;
|
|
608
|
-
|
|
609
|
-
const drawRow = (plainText, styledText) => {
|
|
610
|
-
const trail = " ".repeat(Math.max(0, W - plainText.length));
|
|
611
|
-
return ` ${c(borderCol, "│")}${styledText}${trail}${c(borderCol, "│")}`;
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
const compRow = (icon, label, count, color) => {
|
|
615
|
-
const leftPlain = ` ${icon} ${label.padEnd(10)} `;
|
|
616
|
-
const rightPlain = ` [ ${String(count).padStart(3)} ]`;
|
|
617
|
-
const numDots = W - leftPlain.length - rightPlain.length - 4; // 4 spaces margin
|
|
618
|
-
const dots = ".".repeat(Math.max(0, numDots));
|
|
619
|
-
|
|
620
|
-
const plain = `${leftPlain}${dots}${rightPlain}`;
|
|
621
|
-
const styled = ` ${icon} ${c("white", label.padEnd(10))} ${c("gray", dots)} ${c("gray", "[")} ${c(color, String(count).padStart(3))} ${c("gray", "]")}`;
|
|
622
|
-
return drawRow(plain, styled);
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
const stepRow = (cmd, desc) => {
|
|
626
|
-
const leftPlain = ` ${cmd.padEnd(16)}`;
|
|
627
|
-
const rightPlain = `▸ ${desc}`;
|
|
628
|
-
const plain = `${leftPlain}${rightPlain}`;
|
|
629
|
-
const styled = ` ${c("white", cmd.padEnd(16))}${c("gray", "▸")} ${c("gray", desc)}`;
|
|
630
|
-
return drawRow(plain, styled);
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
console.log(
|
|
634
|
-
` ${c("green", "✔")} ${bold(c("green", "Installation complete"))} ${c("gray", "—")} ${c("white", String(copied))} files`,
|
|
635
|
-
);
|
|
636
|
-
console.log(` ${c("gray", " ╰─")} ${c("gray", agentDest)}`);
|
|
637
|
-
console.log();
|
|
638
|
-
console.log(` ${c(borderCol, "┌" + "─".repeat(W) + "┐")}`);
|
|
639
|
-
console.log(drawRow(" TRIBUNAL ENVIRONMENT SYNCHRONIZED", `${bold(c("white", " TRIBUNAL ENVIRONMENT SYNCHRONIZED"))}`));
|
|
640
|
-
console.log(` ${c(borderCol, "├" + "─".repeat(W) + "┤")}`);
|
|
641
|
-
console.log(drawRow(" Guarding: Active & Enforcing", `${c("gray", " Guarding:")} ${c("green", "Active & Enforcing")}`));
|
|
642
|
-
console.log(drawRow(" Manifest: Verified", `${c("gray", " Manifest:")} ${c("cyan", "Verified")}`));
|
|
643
|
-
console.log(drawRow("", ""));
|
|
644
|
-
console.log(drawRow(" Installed Components:", bold(c("white", " Installed Components:"))));
|
|
645
|
-
console.log(compRow("🤖", "Agents", agentsCount, "magenta"));
|
|
646
|
-
console.log(compRow("⚡", "Workflows", workflowsCount, "yellow"));
|
|
647
|
-
console.log(compRow("🧠", "Skills", skillsCount, "blue"));
|
|
648
|
-
console.log(compRow("🔧", "Scripts", scriptsCount, "green"));
|
|
649
|
-
console.log(` ${c(borderCol, "├" + "─".repeat(W) + "┤")}`);
|
|
650
|
-
console.log(drawRow("", ""));
|
|
651
|
-
console.log(drawRow(" Next Steps:", c("gray", " Next Steps:")));
|
|
652
|
-
console.log(stepRow("/generate", "Generate code with reviews"));
|
|
653
|
-
console.log(stepRow("/review", "Audit existing code for issues"));
|
|
654
|
-
console.log(stepRow("/tribunal-full", "Run all 20 reviewers in parallel"));
|
|
655
|
-
console.log(drawRow("", ""));
|
|
656
|
-
console.log(` ${c(borderCol, "└" + "─".repeat(W) + "┘")}`);
|
|
657
|
-
console.log();
|
|
658
|
-
log(` ${c("gray", "✦ Updating .gitignore...")}`);
|
|
659
|
-
await updateGitignore(targetDir, dryRun);
|
|
660
|
-
log(` ${c("gray", "✦ Generating IDE bridge files...")}`);
|
|
661
|
-
await generateIDEBridges(targetDir, agentDest, dryRun, flags.tokenOptimized || false);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
console.log();
|
|
665
|
-
} catch (e) {
|
|
666
|
-
err(`Failed to install: ${e.message}`);
|
|
667
|
-
process.exit(1);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// ── Gitignore Management ──────────────────────────────────
|
|
672
|
-
async function updateGitignore(targetDir, dryRun = false) {
|
|
673
|
-
if (dryRun) return;
|
|
674
|
-
const gitignorePath = path.join(targetDir, ".gitignore");
|
|
675
|
-
const entries = [".agent/.backups/", ".agent/history/"];
|
|
676
|
-
let content = "";
|
|
677
|
-
try {
|
|
678
|
-
content = await fs.promises.readFile(gitignorePath, "utf8");
|
|
679
|
-
} catch (err) {
|
|
680
|
-
if (err.code !== "ENOENT") throw err;
|
|
681
|
-
}
|
|
682
|
-
let appended = false;
|
|
683
|
-
for (const entry of entries) {
|
|
684
|
-
if (!content.includes(entry)) {
|
|
685
|
-
content +=
|
|
686
|
-
(content.length > 0 && !content.endsWith("\n") ? "\n" : "") +
|
|
687
|
-
entry +
|
|
688
|
-
"\n";
|
|
689
|
-
appended = true;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (appended) {
|
|
693
|
-
await fs.promises.writeFile(gitignorePath, content, "utf8");
|
|
694
|
-
dbg(" Updated .gitignore");
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// ── IDE Bridge Files ──────────────────────────────────────
|
|
699
|
-
// Each AI IDE reads rules from a different location.
|
|
700
|
-
// We generate bridge files that point each IDE at .agent/
|
|
701
|
-
async function generateIDEBridges(targetDir, agentDest, dryRun = false, tokenOptimized = false) {
|
|
702
|
-
const rulesFile = path.join(agentDest, "rules", "GEMINI.md");
|
|
703
|
-
let rulesContent = "";
|
|
704
|
-
try {
|
|
705
|
-
rulesContent = await fs.promises.readFile(rulesFile, "utf8");
|
|
706
|
-
} catch {
|
|
707
|
-
// rules file doesn't exist
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
let rulesToInject = rulesContent;
|
|
711
|
-
if (tokenOptimized) {
|
|
712
|
-
rulesToInject = `---
|
|
713
|
-
trigger: always_on
|
|
714
|
-
---
|
|
715
|
-
|
|
716
|
-
# Tribunal Kit — Token-Optimized Mode
|
|
717
|
-
|
|
718
|
-
You are running under Tribunal Kit in token-optimized mode. To minimize prompt token usage by 85% and avoid context poisoning:
|
|
719
|
-
|
|
720
|
-
1. **Get Sparse Context**: Call the \`get_sparse_context\` tool on the \`tribunal-kit\` MCP server at the start of any feature build, bug fix, or refactor. Pass the active task description and files to retrieve a dynamically tailored, minified ruleset.
|
|
721
|
-
2. **Dynamic Rule Enforcements**: Follow all rules returned by the MCP context tool. Do NOT load full raw markdown files from \`.agent/\` to prevent context bloat.
|
|
722
|
-
3. **Verify Precedents**: Search historical rejections using \`search_case_law\` before editing code.
|
|
723
|
-
|
|
724
|
-
## Critical Core Constraints
|
|
725
|
-
- Parameterize all SQL queries.
|
|
726
|
-
- Keep secrets in environment variables.
|
|
727
|
-
- Run tests with \`npm test\` after any changes.
|
|
728
|
-
`;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// Helper: write a bridge file or merge it if it exists
|
|
732
|
-
const writeBridge = async (filePath, content, label, isJson = false) => {
|
|
733
|
-
if (dryRun) {
|
|
734
|
-
dbg(` would create/update: ${filePath}`);
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
const dir = path.dirname(filePath);
|
|
738
|
-
await fs.promises.mkdir(dir, { recursive: true });
|
|
739
|
-
|
|
740
|
-
try {
|
|
741
|
-
const existingContent = await fs.promises.readFile(filePath, "utf8");
|
|
742
|
-
if (isJson) {
|
|
743
|
-
try {
|
|
744
|
-
const existingData = JSON.parse(existingContent);
|
|
745
|
-
const newData = JSON.parse(content);
|
|
746
|
-
|
|
747
|
-
if (!existingData.rules) existingData.rules = [];
|
|
748
|
-
const rulePath = newData.rules[0].path;
|
|
749
|
-
|
|
750
|
-
// Clear both standard and optimized paths to prevent duplicates/stale paths
|
|
751
|
-
existingData.rules = existingData.rules.filter(
|
|
752
|
-
(r) => r.path !== "GEMINI.md" && r.path !== "../.agent/rules/GEMINI.md"
|
|
753
|
-
);
|
|
754
|
-
existingData.rules.push(newData.rules[0]);
|
|
755
|
-
|
|
756
|
-
existingData.agents = { ...existingData.agents, ...newData.agents };
|
|
757
|
-
existingData.skills = { ...existingData.skills, ...newData.skills };
|
|
758
|
-
existingData.workflows = {
|
|
759
|
-
...existingData.workflows,
|
|
760
|
-
...newData.workflows,
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
await fs.promises.writeFile(
|
|
764
|
-
filePath,
|
|
765
|
-
JSON.stringify(existingData, null, 2) + "\n",
|
|
766
|
-
"utf8",
|
|
767
|
-
);
|
|
768
|
-
ok(
|
|
769
|
-
`${label} (merged) → ${c("gray", path.relative(targetDir, filePath))}`,
|
|
770
|
-
);
|
|
771
|
-
} catch (e) {
|
|
772
|
-
warn(`Failed to merge ${label}: ${e.message}`);
|
|
773
|
-
}
|
|
774
|
-
} else {
|
|
775
|
-
if (
|
|
776
|
-
!existingContent.includes("Tribunal Kit") &&
|
|
777
|
-
(!rulesToInject ||
|
|
778
|
-
!existingContent.includes(rulesToInject.slice(0, 50)))
|
|
779
|
-
) {
|
|
780
|
-
await fs.promises.appendFile(filePath, "\n" + content, "utf8");
|
|
781
|
-
ok(
|
|
782
|
-
`${label} (appended) → ${c("gray", path.relative(targetDir, filePath))}`,
|
|
783
|
-
);
|
|
784
|
-
} else {
|
|
785
|
-
dbg(` skip (rules exist): ${path.basename(filePath)}`);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
} catch (err) {
|
|
789
|
-
if (err.code === "ENOENT") {
|
|
790
|
-
await fs.promises.writeFile(filePath, content, "utf8");
|
|
791
|
-
ok(`${label} → ${c("gray", path.relative(targetDir, filePath))}`);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
};
|
|
795
|
-
|
|
796
|
-
// ── 1. Cursor (.cursorrules) ──────────────────────────
|
|
797
|
-
const cursorRules = `# Tribunal Kit — Cursor Bridge
|
|
798
|
-
# Auto-generated by tribunal-kit init. Do not edit manually.
|
|
799
|
-
# Source: .agent/rules/GEMINI.md (Optimized)
|
|
800
|
-
|
|
801
|
-
${rulesToInject}
|
|
802
|
-
`;
|
|
803
|
-
await writeBridge(
|
|
804
|
-
path.join(targetDir, ".cursorrules"),
|
|
805
|
-
cursorRules,
|
|
806
|
-
"Cursor",
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
// ── 2. Windsurf (.windsurfrules) ─────────────────────
|
|
810
|
-
const windsurfRules = `# Tribunal Kit — Windsurf Bridge
|
|
811
|
-
# Auto-generated by tribunal-kit init. Do not edit manually.
|
|
812
|
-
# Source: .agent/rules/GEMINI.md (Optimized)
|
|
813
|
-
|
|
814
|
-
${rulesToInject}
|
|
815
|
-
`;
|
|
816
|
-
await writeBridge(
|
|
817
|
-
path.join(targetDir, ".windsurfrules"),
|
|
818
|
-
windsurfRules,
|
|
819
|
-
"Windsurf",
|
|
820
|
-
);
|
|
821
|
-
|
|
822
|
-
// ── 3. Gemini / Antigravity (.gemini/settings.json) ──
|
|
823
|
-
const geminiRulesPath = tokenOptimized ? "GEMINI.md" : "../.agent/rules/GEMINI.md";
|
|
824
|
-
const geminiSettings =
|
|
825
|
-
JSON.stringify(
|
|
826
|
-
{
|
|
827
|
-
rules: [{ path: geminiRulesPath, trigger: "always_on" }],
|
|
828
|
-
agents: { directory: "../.agent/agents" },
|
|
829
|
-
skills: { directory: "../.agent/skills" },
|
|
830
|
-
workflows: { directory: "../.agent/workflows" },
|
|
831
|
-
},
|
|
832
|
-
null,
|
|
833
|
-
2,
|
|
834
|
-
) + "\n";
|
|
835
|
-
await writeBridge(
|
|
836
|
-
path.join(targetDir, ".gemini", "settings.json"),
|
|
837
|
-
geminiSettings,
|
|
838
|
-
"Gemini/Antigravity",
|
|
839
|
-
true,
|
|
840
|
-
);
|
|
841
|
-
|
|
842
|
-
// ── Also create .gemini/GEMINI.md as a direct rules file ──
|
|
843
|
-
const geminiRulesBridge = `---
|
|
844
|
-
trigger: always_on
|
|
845
|
-
---
|
|
846
|
-
|
|
847
|
-
# Tribunal Kit — Gemini Bridge
|
|
848
|
-
# Auto-generated by tribunal-kit init.
|
|
849
|
-
# Full rules: .agent/rules/GEMINI.md
|
|
850
|
-
|
|
851
|
-
${rulesToInject}
|
|
852
|
-
`;
|
|
853
|
-
await writeBridge(
|
|
854
|
-
path.join(targetDir, ".gemini", "GEMINI.md"),
|
|
855
|
-
geminiRulesBridge,
|
|
856
|
-
"Gemini rules",
|
|
857
|
-
);
|
|
858
|
-
|
|
859
|
-
// ── 4. GitHub Copilot (.github/copilot-instructions.md) ──
|
|
860
|
-
const copilotInstructions = `# Tribunal Kit — Copilot Bridge
|
|
861
|
-
# Auto-generated by tribunal-kit init. Do not edit manually.
|
|
862
|
-
# Source: .agent/rules/GEMINI.md (Optimized)
|
|
863
|
-
|
|
864
|
-
${rulesToInject}
|
|
865
|
-
`;
|
|
866
|
-
await writeBridge(
|
|
867
|
-
path.join(targetDir, ".github", "copilot-instructions.md"),
|
|
868
|
-
copilotInstructions,
|
|
869
|
-
"GitHub Copilot",
|
|
870
|
-
);
|
|
871
|
-
|
|
872
|
-
// ── 5. Claude (.claude/CLAUDE.md) ─────────────────────
|
|
873
|
-
const claudeRules = `# Tribunal Kit — Claude Bridge
|
|
874
|
-
# Auto-generated by tribunal-kit init. Do not edit manually.
|
|
875
|
-
# Source: .agent/rules/GEMINI.md (Optimized)
|
|
876
|
-
|
|
877
|
-
${rulesToInject}
|
|
878
|
-
`;
|
|
879
|
-
await writeBridge(
|
|
880
|
-
path.join(targetDir, ".claude", "CLAUDE.md"),
|
|
881
|
-
claudeRules,
|
|
882
|
-
"Claude",
|
|
883
|
-
);
|
|
884
|
-
|
|
885
|
-
console.log();
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
async function cmdSync() {
|
|
889
|
-
console.log(`\n╭─ ${c("bold", "Tribunal IDE Sync")} ──────────────────`);
|
|
890
|
-
console.log("│");
|
|
891
|
-
console.log(`│ ${c("gray", "✦ Regenerating IDE bridge files...")}`);
|
|
892
|
-
const cwd = process.cwd();
|
|
893
|
-
const agentDest = path.join(cwd, ".agent");
|
|
894
|
-
if (!fs.existsSync(agentDest)) {
|
|
895
|
-
console.error(`│ ${c("red", "✖ Error: .agent/ directory not found.")}`);
|
|
896
|
-
console.error(`│ ${c("gray", "Run `tk init` first.")}`);
|
|
897
|
-
process.exit(1);
|
|
898
|
-
}
|
|
899
|
-
await generateIDEBridges(cwd, agentDest, false);
|
|
900
|
-
console.log(`│ ${c("green", "✔ Sync complete.")}`);
|
|
901
|
-
console.log("╰────────────────────────────────────────\n");
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
async function cmdUpdate(flags) {
|
|
905
|
-
// ── Self-install guard (early, before banner) ───────────
|
|
906
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
907
|
-
if (isSelfInstall(targetDir)) {
|
|
908
|
-
err("Cannot run update inside the tribunal-kit package itself.");
|
|
909
|
-
err(`Target: ${targetDir}`);
|
|
910
|
-
console.log();
|
|
911
|
-
dim("This command is designed to update .agent/ in OTHER projects.");
|
|
912
|
-
dim("Run it from the root of the project you want to update:");
|
|
913
|
-
dim(" cd /path/to/your-project");
|
|
914
|
-
dim(" npx tribunal-kit update");
|
|
915
|
-
console.log();
|
|
916
|
-
process.exit(1);
|
|
917
|
-
}
|
|
918
|
-
// ────────────────────────────────────────────────────────
|
|
919
|
-
|
|
920
|
-
// Update = init with --force
|
|
921
|
-
flags.force = true;
|
|
922
|
-
if (!quiet) {
|
|
923
|
-
log(
|
|
924
|
-
` ${c("cyan", "↻")} ${bold("Updating")} ${c("white", ".agent/")} to latest version...`,
|
|
925
|
-
);
|
|
926
|
-
console.log();
|
|
927
|
-
}
|
|
928
|
-
await cmdInit(flags);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
async function cmdLearn(flags) {
|
|
932
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
933
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
934
|
-
|
|
935
|
-
if (!fs.existsSync(agentDest)) {
|
|
936
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
937
|
-
process.exit(1);
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
banner();
|
|
941
|
-
|
|
942
|
-
const W = 62;
|
|
943
|
-
const title = " Tribunal Learn — Supreme Court Mode";
|
|
944
|
-
const trail = " ".repeat(Math.max(0, W - title.length));
|
|
945
|
-
console.log(` ${c("cyan", "\u2554" + "\u2550".repeat(W) + "\u2557")}`);
|
|
946
|
-
console.log(
|
|
947
|
-
` ${c("cyan", "\u2551")}${c("bold", c("white", title))}${trail}${c("cyan", "\u2551")}`,
|
|
948
|
-
);
|
|
949
|
-
console.log(` ${c("cyan", "\u255a" + "\u2550".repeat(W) + "\u255d")}`);
|
|
950
|
-
console.log();
|
|
951
|
-
|
|
952
|
-
const evoArgs = ["digest"];
|
|
953
|
-
if (flags.dryRun) evoArgs.push("--dry-run");
|
|
954
|
-
if (flags.head) evoArgs.push("--head");
|
|
955
|
-
if (flags.log) evoArgs.push(`--log=${flags.log}`);
|
|
956
|
-
if (flags.strategy) evoArgs.push(`--strategy=${flags.strategy}`);
|
|
957
|
-
|
|
958
|
-
// Phase 1: Skill Evolution
|
|
959
|
-
log(
|
|
960
|
-
` ${c("cyan", "\u229b")} ${bold("Phase 1")} \u2014 Skill Evolution Forge (auto-generating project idioms)`,
|
|
961
|
-
);
|
|
962
|
-
const evoScript = path.join(agentDest, "scripts", "skill_evolution.js");
|
|
963
|
-
if (!fs.existsSync(evoScript)) {
|
|
964
|
-
warn("skill_evolution.js not found \u2014 run: npx tribunal-kit update");
|
|
965
|
-
} else {
|
|
966
|
-
try {
|
|
967
|
-
await runScriptAsync(evoScript, evoArgs, { cwd: targetDir });
|
|
968
|
-
} catch (e) {
|
|
969
|
-
warn(`Skill Evolution error: ${e.message}`);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
console.log();
|
|
974
|
-
|
|
975
|
-
// Phase 2: Case Law prompt
|
|
976
|
-
log(
|
|
977
|
-
` ${c("cyan", "\u229b")} ${bold("Phase 2")} \u2014 Case Law Engine (building precedence record)`,
|
|
978
|
-
);
|
|
979
|
-
console.log();
|
|
980
|
-
log(` ${c("gray", "\u25b8")} Record a new rejection precedent:`);
|
|
981
|
-
log(` ${c("white", "npx tribunal-kit case add")}`);
|
|
982
|
-
console.log();
|
|
983
|
-
log(` ${c("gray", "\u25b8")} Search existing case law:`);
|
|
984
|
-
log(` ${c("white", 'npx tribunal-kit case search "your query"')}`);
|
|
985
|
-
console.log();
|
|
986
|
-
log(
|
|
987
|
-
` ${c("green", "\u2714")} ${bold("Learn cycle complete.")} Your Tribunal grows smarter with every commit.`,
|
|
988
|
-
);
|
|
989
|
-
console.log();
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// ── Async Main Wrapper ───────────────────────────────────
|
|
993
|
-
async function runWithUpdateCheck(command, flags) {
|
|
994
|
-
const shouldSkip =
|
|
995
|
-
flags.skipUpdateCheck || process.env.TK_SKIP_UPDATE_CHECK === "1";
|
|
996
|
-
|
|
997
|
-
if (!shouldSkip && (command === "init" || command === "update")) {
|
|
998
|
-
// Pass through the original args (minus the node/script path)
|
|
999
|
-
const originalArgs = process.argv.slice(2);
|
|
1000
|
-
const didReInvoke = await autoUpdateCheck(originalArgs);
|
|
1001
|
-
if (didReInvoke) {
|
|
1002
|
-
process.exit(0); // Latest version handled it
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// Proceed with current version
|
|
1007
|
-
switch (command) {
|
|
1008
|
-
case "init":
|
|
1009
|
-
await cmdInit(flags);
|
|
1010
|
-
break;
|
|
1011
|
-
case "update":
|
|
1012
|
-
await cmdUpdate(flags);
|
|
1013
|
-
break;
|
|
1014
|
-
case "status":
|
|
1015
|
-
cmdStatus(flags);
|
|
1016
|
-
break;
|
|
1017
|
-
case "learn":
|
|
1018
|
-
await cmdLearn(flags);
|
|
1019
|
-
break;
|
|
1020
|
-
case "case":
|
|
1021
|
-
await cmdCase(flags);
|
|
1022
|
-
break;
|
|
1023
|
-
case "hook":
|
|
1024
|
-
cmdHook(flags);
|
|
1025
|
-
break;
|
|
1026
|
-
case "graph":
|
|
1027
|
-
await cmdGraph(flags);
|
|
1028
|
-
break;
|
|
1029
|
-
case "mutate":
|
|
1030
|
-
await cmdMutate(flags);
|
|
1031
|
-
break;
|
|
1032
|
-
case "context":
|
|
1033
|
-
cmdContext(flags);
|
|
1034
|
-
break;
|
|
1035
|
-
case "sync":
|
|
1036
|
-
await cmdSync();
|
|
1037
|
-
break;
|
|
1038
|
-
case "marathon":
|
|
1039
|
-
await cmdMarathon(flags);
|
|
1040
|
-
break;
|
|
1041
|
-
case "uninstall":
|
|
1042
|
-
cmdUninstall(flags);
|
|
1043
|
-
break;
|
|
1044
|
-
case "help":
|
|
1045
|
-
case "--help":
|
|
1046
|
-
case "-h":
|
|
1047
|
-
case null:
|
|
1048
|
-
cmdHelp();
|
|
1049
|
-
break;
|
|
1050
|
-
default:
|
|
1051
|
-
err(`Unknown command: "${command}"`);
|
|
1052
|
-
console.log();
|
|
1053
|
-
dim("Run tribunal-kit --help for usage");
|
|
1054
|
-
process.exit(1);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
async function cmdCase(flags) {
|
|
1059
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1060
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1061
|
-
|
|
1062
|
-
if (!fs.existsSync(agentDest)) {
|
|
1063
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
1064
|
-
process.exit(1);
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
const args = process.argv.slice(3);
|
|
1068
|
-
if (
|
|
1069
|
-
args.length === 0 ||
|
|
1070
|
-
args[0] === "help" ||
|
|
1071
|
-
args[0] === "--help" ||
|
|
1072
|
-
args[0] === "-h"
|
|
1073
|
-
) {
|
|
1074
|
-
banner();
|
|
1075
|
-
log(` ${c("cyan", "\u2554" + "\u2550".repeat(60) + "\u2557")}`);
|
|
1076
|
-
log(
|
|
1077
|
-
` ${c("cyan", "\u2551")}${c("bold", c("white", " Tribunal Case Law Engine \u2014 Supreme Court "))}${c("cyan", "\u2551")}`,
|
|
1078
|
-
);
|
|
1079
|
-
log(` ${c("cyan", "\u255a" + "\u2550".repeat(60) + "\u255d")}`);
|
|
1080
|
-
console.log();
|
|
1081
|
-
log(
|
|
1082
|
-
` ${c("cyan", "add".padEnd(10))} ${c("gray", "Record a new Case Law rejection pattern")}`,
|
|
1083
|
-
);
|
|
1084
|
-
log(
|
|
1085
|
-
` ${c("cyan", "search".padEnd(10))} ${c("gray", 'Search existing cases (e.g., search "query")')}`,
|
|
1086
|
-
);
|
|
1087
|
-
log(
|
|
1088
|
-
` ${c("cyan", "list".padEnd(10))} ${c("gray", "List all recorded case law")}`,
|
|
1089
|
-
);
|
|
1090
|
-
log(
|
|
1091
|
-
` ${c("cyan", "show".padEnd(10))} ${c("gray", "Show full diff for a case (e.g., show --id 1)")}`,
|
|
1092
|
-
);
|
|
1093
|
-
log(
|
|
1094
|
-
` ${c("cyan", "stats".padEnd(10))} ${c("gray", "Show case law stats by domain/verdict")}`,
|
|
1095
|
-
);
|
|
1096
|
-
log(
|
|
1097
|
-
` ${c("cyan", "export".padEnd(10))} ${c("gray", "Export all cases to Markdown")}`,
|
|
1098
|
-
);
|
|
1099
|
-
log(
|
|
1100
|
-
` ${c("cyan", "overrule".padEnd(10))} ${c("gray", "Overrule a past precedent (e.g., overrule --id 1)")}`,
|
|
1101
|
-
);
|
|
1102
|
-
console.log();
|
|
1103
|
-
process.exit(1);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
const caseLawScript = path.join(agentDest, "scripts", "case_law_manager.js");
|
|
1107
|
-
|
|
1108
|
-
// Make shorthand aliases for the subcommand (first arg only)
|
|
1109
|
-
const caseArgs = [...args];
|
|
1110
|
-
if (caseArgs[0] === "add") caseArgs[0] = "add-case";
|
|
1111
|
-
if (caseArgs[0] === "search") caseArgs[0] = "search-cases";
|
|
1112
|
-
|
|
1113
|
-
try {
|
|
1114
|
-
await runScriptAsync(caseLawScript, caseArgs, { cwd: targetDir });
|
|
1115
|
-
} catch {
|
|
1116
|
-
process.exit(1); // Script already prints errors
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
async function cmdGraph(flags) {
|
|
1121
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1122
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1123
|
-
|
|
1124
|
-
if (!fs.existsSync(agentDest)) {
|
|
1125
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
1126
|
-
process.exit(1);
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
banner();
|
|
1130
|
-
const builderScript = path.join(agentDest, "scripts", "graph_builder.js");
|
|
1131
|
-
const visualizerScript = path.join(
|
|
1132
|
-
agentDest,
|
|
1133
|
-
"scripts",
|
|
1134
|
-
"graph_visualizer.js",
|
|
1135
|
-
);
|
|
1136
|
-
const htmlFile = path.join(
|
|
1137
|
-
agentDest,
|
|
1138
|
-
"history",
|
|
1139
|
-
"architecture-explorer.html",
|
|
1140
|
-
);
|
|
1141
|
-
|
|
1142
|
-
try {
|
|
1143
|
-
await runScriptAsync(builderScript, [], { cwd: targetDir });
|
|
1144
|
-
await runScriptAsync(visualizerScript, [], { cwd: targetDir });
|
|
1145
|
-
|
|
1146
|
-
log(` ${c("cyan", "▸")} Opening visualizer in browser...`);
|
|
1147
|
-
// Open browser safely without shell interpolation
|
|
1148
|
-
const { opener, openerArgs } = (() => {
|
|
1149
|
-
if (process.platform === "win32")
|
|
1150
|
-
return { opener: "cmd", openerArgs: ["/c", "start", "", htmlFile] };
|
|
1151
|
-
if (process.platform === "darwin")
|
|
1152
|
-
return { opener: "open", openerArgs: [htmlFile] };
|
|
1153
|
-
return { opener: "xdg-open", openerArgs: [htmlFile] };
|
|
1154
|
-
})();
|
|
1155
|
-
spawn(opener, openerArgs, { stdio: "ignore", detached: true }).unref();
|
|
1156
|
-
} catch (e) {
|
|
1157
|
-
err(`Graph generation failed: ${e.message}`);
|
|
1158
|
-
process.exit(1);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
function cmdHook(flags) {
|
|
1163
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1164
|
-
const gitDir = path.join(targetDir, ".git");
|
|
1165
|
-
|
|
1166
|
-
if (!fs.existsSync(gitDir)) {
|
|
1167
|
-
err("Not a git repository. Cannot install git hooks here.");
|
|
1168
|
-
process.exit(1);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
const hooksDir = path.join(gitDir, "hooks");
|
|
1172
|
-
if (!fs.existsSync(hooksDir)) {
|
|
1173
|
-
fs.mkdirSync(hooksDir, { recursive: true });
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
const prePushPath = path.join(hooksDir, "pre-push");
|
|
1177
|
-
const hookScript = `#!/bin/sh\n# Supreme Court - Auto Learn on Push\necho "⚖️ Tribunal Supreme Court: Evolving Skills..."\nnpx tribunal-kit learn --head\necho "✦ Synchronizing IDE bridges..."\nnpx tribunal-kit sync\n`;
|
|
1178
|
-
|
|
1179
|
-
fs.writeFileSync(prePushPath, hookScript, { mode: 0o755 });
|
|
1180
|
-
|
|
1181
|
-
console.log();
|
|
1182
|
-
log(` ${c("green", "✔")} Installed pre-push git hook.`);
|
|
1183
|
-
log(
|
|
1184
|
-
` ${c("gray", "▸")} Skill Evolution and IDE Sync will now run automatically every time you git push.`,
|
|
1185
|
-
);
|
|
1186
|
-
console.log();
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
async function cmdMutate(flags) {
|
|
1190
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1191
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1192
|
-
|
|
1193
|
-
if (!fs.existsSync(agentDest)) {
|
|
1194
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
1195
|
-
process.exit(1);
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
const args = process.argv.slice(3);
|
|
1199
|
-
if (args.length < 2) {
|
|
1200
|
-
err("Usage: npx tribunal-kit mutate <target_file> <test_command>");
|
|
1201
|
-
process.exit(1);
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
const mutateScript = path.join(agentDest, "scripts", "mutation_runner.js");
|
|
1205
|
-
try {
|
|
1206
|
-
await runScriptAsync(mutateScript, args, { cwd: targetDir });
|
|
1207
|
-
} catch {
|
|
1208
|
-
process.exit(1);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
function cmdUninstall(flags) {
|
|
1213
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1214
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1215
|
-
|
|
1216
|
-
banner();
|
|
1217
|
-
|
|
1218
|
-
if (!fs.existsSync(agentDest)) {
|
|
1219
|
-
log(
|
|
1220
|
-
` ${c("yellow", "⚠")} ${bold(".agent/")} is not installed in this project.`,
|
|
1221
|
-
);
|
|
1222
|
-
console.log();
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (flags.dryRun) {
|
|
1227
|
-
log(colorize("yellow", " DRY RUN — would remove:"));
|
|
1228
|
-
log(` ${c("gray", " ╰─")} ${agentDest}`);
|
|
1229
|
-
console.log();
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
try {
|
|
1234
|
-
fs.rmSync(agentDest, { recursive: true, force: true });
|
|
1235
|
-
log(
|
|
1236
|
-
` ${c("green", "✔")} ${bold(".agent/")} has been removed from this project.`,
|
|
1237
|
-
);
|
|
1238
|
-
console.log();
|
|
1239
|
-
log(
|
|
1240
|
-
` ${c("gray", "▸")} To reinstall: ${c("cyan", "npx tribunal-kit init")}`,
|
|
1241
|
-
);
|
|
1242
|
-
console.log();
|
|
1243
|
-
} catch (e) {
|
|
1244
|
-
err(`Failed to remove .agent/: ${e.message}`);
|
|
1245
|
-
process.exit(1);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
function cmdStatus(flags) {
|
|
1250
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1251
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1252
|
-
|
|
1253
|
-
banner();
|
|
1254
|
-
|
|
1255
|
-
if (!fs.existsSync(agentDest)) {
|
|
1256
|
-
log(` ${c("red", "✖")} ${bold("Not installed")} in this project`);
|
|
1257
|
-
console.log();
|
|
1258
|
-
log(` ${c("gray", "Run:")} ${c("cyan", "npx tribunal-kit init")}`);
|
|
1259
|
-
console.log();
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
log(
|
|
1264
|
-
` ${c("green", "✔")} ${bold(c("green", "Installed"))} ${c("gray", "→")} ${c("gray", agentDest)}`,
|
|
1265
|
-
);
|
|
1266
|
-
console.log();
|
|
1267
|
-
|
|
1268
|
-
const icons = { agents: "🤖", workflows: "⚡", skills: "🧠", scripts: "🔧" };
|
|
1269
|
-
const colors = {
|
|
1270
|
-
agents: "magenta",
|
|
1271
|
-
workflows: "yellow",
|
|
1272
|
-
skills: "blue",
|
|
1273
|
-
scripts: "green",
|
|
1274
|
-
};
|
|
1275
|
-
const subdirs = ["agents", "workflows", "skills", "scripts"];
|
|
1276
|
-
for (const sub of subdirs) {
|
|
1277
|
-
const subPath = path.join(agentDest, sub);
|
|
1278
|
-
if (fs.existsSync(subPath)) {
|
|
1279
|
-
const count = fs
|
|
1280
|
-
.readdirSync(subPath)
|
|
1281
|
-
.filter(
|
|
1282
|
-
(f) => !fs.statSync(path.join(subPath, f)).isDirectory(),
|
|
1283
|
-
).length;
|
|
1284
|
-
log(
|
|
1285
|
-
` ${icons[sub]} ${c(colors[sub], sub.padEnd(12))}${c("white", String(count).padStart(3))} files`,
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
console.log();
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
function cmdHelp() {
|
|
1293
|
-
banner();
|
|
1294
|
-
const cmd = (name, desc) =>
|
|
1295
|
-
` ${c("cyan", name.padEnd(10))} ${c("gray", desc)}`;
|
|
1296
|
-
const opt = (flag, desc) =>
|
|
1297
|
-
` ${c("yellow", flag.padEnd(22))} ${c("gray", desc)}`;
|
|
1298
|
-
const ex = (s) => ` ${c("gray", "▸")} ${c("white", s)}`;
|
|
1299
|
-
|
|
1300
|
-
log(bold(" Commands"));
|
|
1301
|
-
log(` ${c("gray", "─".repeat(40))}`);
|
|
1302
|
-
log(cmd("init", "Install .agent/ into current project"));
|
|
1303
|
-
log(cmd("update", "Re-install to get latest version"));
|
|
1304
|
-
log(cmd("status", "Check if .agent/ is installed"));
|
|
1305
|
-
log(cmd("learn", "Evolve project idioms based on git diffs"));
|
|
1306
|
-
log(
|
|
1307
|
-
cmd(
|
|
1308
|
-
"case",
|
|
1309
|
-
"Manage Case Law precedents (add, search, list, show, stats, overrule)",
|
|
1310
|
-
),
|
|
1311
|
-
);
|
|
1312
|
-
log(cmd("graph", "Build and visualize the architecture graph"));
|
|
1313
|
-
log(cmd("mutate", "Run the Mutation Engine to test test-suite reliability"));
|
|
1314
|
-
log(
|
|
1315
|
-
cmd("context", "Retrieve a highly-optimized Context Snapshot for a file"),
|
|
1316
|
-
);
|
|
1317
|
-
log(cmd("sync", "Synchronize IDE bridge files with current rules"));
|
|
1318
|
-
log(cmd("marathon", "Long-running agent harness (init, status, next, mark)"));
|
|
1319
|
-
log(cmd("hook", "Install pre-push git hook for auto-learning"));
|
|
1320
|
-
log(cmd("uninstall", "Remove .agent/ folder from project"));
|
|
1321
|
-
console.log();
|
|
1322
|
-
log(bold(" Options"));
|
|
1323
|
-
log(` ${c("gray", "─".repeat(40))}`);
|
|
1324
|
-
log(opt("--force", "Overwrite existing .agent/ folder"));
|
|
1325
|
-
log(opt("--path <dir>", "Install in specific directory"));
|
|
1326
|
-
log(opt("--quiet", "Suppress all output"));
|
|
1327
|
-
log(opt("--verbose", "Show detailed debug logging"));
|
|
1328
|
-
log(opt("--dry-run", "Preview actions without executing"));
|
|
1329
|
-
log(opt("--minimal", "Install core agents/skills only (~13 agents)"));
|
|
1330
|
-
log(opt("--skip-update-check", "Skip auto-update version check"));
|
|
1331
|
-
log(opt("--head", "(learn) Diff against last commit instead of staged"));
|
|
1332
|
-
log(opt("--log <file>", "(learn) Extract signals and evolve from a log/transcript file"));
|
|
1333
|
-
log(opt("--strategy <val>", "(learn) Evolution strategy: balanced, harden, repair-only"));
|
|
1334
|
-
console.log();
|
|
1335
|
-
log(bold(" Aliases"));
|
|
1336
|
-
log(` ${c("gray", "─".repeat(40))}`);
|
|
1337
|
-
log(
|
|
1338
|
-
` ${c("cyan", "tk")} ${c("gray", "Shorthand for tribunal-kit (e.g., tk init, tk status)")}`,
|
|
1339
|
-
);
|
|
1340
|
-
console.log();
|
|
1341
|
-
log(bold(" Examples"));
|
|
1342
|
-
log(` ${c("gray", "─".repeat(40))}`);
|
|
1343
|
-
log(ex("npx tribunal-kit init"));
|
|
1344
|
-
log(ex("tk init --force"));
|
|
1345
|
-
log(ex("tk init --path ./my-app"));
|
|
1346
|
-
log(ex("npx tribunal-kit init --dry-run"));
|
|
1347
|
-
log(ex("tk update"));
|
|
1348
|
-
log(ex("tk status"));
|
|
1349
|
-
log(ex("tk learn"));
|
|
1350
|
-
log(ex("tk learn --dry-run"));
|
|
1351
|
-
log(ex("tk learn --head"));
|
|
1352
|
-
log(ex("tk learn --log=run.log"));
|
|
1353
|
-
log(ex("tk learn --log=eslint.log --strategy=harden"));
|
|
1354
|
-
log(ex("tk case add"));
|
|
1355
|
-
log(ex('tk case search "useEffect"'));
|
|
1356
|
-
log(ex("tk case list"));
|
|
1357
|
-
log(ex("tk case show --id 1"));
|
|
1358
|
-
log(ex("tk case stats"));
|
|
1359
|
-
log(ex("tk case export"));
|
|
1360
|
-
log(ex("tk case overrule --id 1"));
|
|
1361
|
-
log(ex("tk graph"));
|
|
1362
|
-
log(ex('tk mutate src/utils.js "npm test"'));
|
|
1363
|
-
log(ex('tk marathon init "Build a todo app"'));
|
|
1364
|
-
log(ex("tk marathon status"));
|
|
1365
|
-
log(ex("tk marathon next"));
|
|
1366
|
-
log(ex("tk marathon mark 5 pass"));
|
|
1367
|
-
log(ex("tk hook"));
|
|
1368
|
-
log(ex("tk uninstall"));
|
|
1369
|
-
console.log();
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
async function cmdMarathon(flags) {
|
|
1373
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1374
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1375
|
-
|
|
1376
|
-
if (!fs.existsSync(agentDest)) {
|
|
1377
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
1378
|
-
process.exit(1);
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
const args = process.argv.slice(3);
|
|
1382
|
-
if (
|
|
1383
|
-
args.length === 0 ||
|
|
1384
|
-
args[0] === "help" ||
|
|
1385
|
-
args[0] === "--help" ||
|
|
1386
|
-
args[0] === "-h"
|
|
1387
|
-
) {
|
|
1388
|
-
banner();
|
|
1389
|
-
log(` ${c("cyan", "╔" + "═".repeat(60) + "╗")}`);
|
|
1390
|
-
log(
|
|
1391
|
-
` ${c("cyan", "║")}${c("bold", c("white", " Marathon — Long-Running Agent Harness "))}${c("cyan", "║")}`,
|
|
1392
|
-
);
|
|
1393
|
-
log(` ${c("cyan", "╚" + "═".repeat(60) + "╝")}`);
|
|
1394
|
-
console.log();
|
|
1395
|
-
log(
|
|
1396
|
-
` ${c("cyan", "init".padEnd(16))} ${c("gray", 'Start a new marathon (init "spec")')}`,
|
|
1397
|
-
);
|
|
1398
|
-
log(
|
|
1399
|
-
` ${c("cyan", "status".padEnd(16))} ${c("gray", "Show progress dashboard")}`,
|
|
1400
|
-
);
|
|
1401
|
-
log(
|
|
1402
|
-
` ${c("cyan", "next".padEnd(16))} ${c("gray", "Show next unfinished feature")}`,
|
|
1403
|
-
);
|
|
1404
|
-
log(
|
|
1405
|
-
` ${c("cyan", "mark".padEnd(16))} ${c("gray", "Mark feature pass/fail (mark <id> pass)")}`,
|
|
1406
|
-
);
|
|
1407
|
-
log(
|
|
1408
|
-
` ${c("cyan", "log".padEnd(16))} ${c("gray", "Add a progress note")}`,
|
|
1409
|
-
);
|
|
1410
|
-
log(
|
|
1411
|
-
` ${c("cyan", "session-start".padEnd(16))} ${c("gray", "Begin a new work session")}`,
|
|
1412
|
-
);
|
|
1413
|
-
log(
|
|
1414
|
-
` ${c("cyan", "session-end".padEnd(16))} ${c("gray", "End session with summary")}`,
|
|
1415
|
-
);
|
|
1416
|
-
log(
|
|
1417
|
-
` ${c("cyan", "add-feature".padEnd(16))} ${c("gray", 'Add feature: "category" "desc" "step1" ...')}`,
|
|
1418
|
-
);
|
|
1419
|
-
log(
|
|
1420
|
-
` ${c("cyan", "reset".padEnd(16))} ${c("gray", "Archive and start fresh")}`,
|
|
1421
|
-
);
|
|
1422
|
-
console.log();
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
const marathonScript = path.join(agentDest, "scripts", "marathon_harness.js");
|
|
1427
|
-
try {
|
|
1428
|
-
await runScriptAsync(marathonScript, args, { cwd: targetDir });
|
|
1429
|
-
} catch {
|
|
1430
|
-
process.exit(1);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
function cmdContext(flags) {
|
|
1435
|
-
const targetDir = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
1436
|
-
const agentDest = path.join(targetDir, ".agent");
|
|
1437
|
-
|
|
1438
|
-
if (!fs.existsSync(agentDest)) {
|
|
1439
|
-
err(".agent/ not found. Run: npx tribunal-kit init");
|
|
1440
|
-
process.exit(1);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
const args = process.argv.slice(3);
|
|
1444
|
-
if (args.length === 0 || args[0] === "help" || args[0] === "--help") {
|
|
1445
|
-
console.error("Usage: npx tribunal-kit context <target_file>");
|
|
1446
|
-
process.exit(1);
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
const targetFile = args[0].replace(/\\/g, "/");
|
|
1450
|
-
const snapshotName = targetFile.replace(/[\\\/]/g, "__") + ".json";
|
|
1451
|
-
const snapshotPath = require("path").join(
|
|
1452
|
-
agentDest,
|
|
1453
|
-
"history",
|
|
1454
|
-
"snapshots",
|
|
1455
|
-
snapshotName,
|
|
1456
|
-
);
|
|
1457
|
-
|
|
1458
|
-
if (!require("fs").existsSync(snapshotPath)) {
|
|
1459
|
-
console.error(
|
|
1460
|
-
" \x1b[91m✖\x1b[0m Context Snapshot not found for: " + targetFile,
|
|
1461
|
-
);
|
|
1462
|
-
console.log(" Run: npx tribunal-kit graph (to generate snapshots)");
|
|
1463
|
-
process.exit(1);
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
try {
|
|
1467
|
-
const snapshot = JSON.parse(
|
|
1468
|
-
require("fs").readFileSync(snapshotPath, "utf8"),
|
|
1469
|
-
);
|
|
1470
|
-
|
|
1471
|
-
console.log("\n# Context Snapshot: " + snapshot.file);
|
|
1472
|
-
process.stdout.write(
|
|
1473
|
-
"> Size Estimate: " + (snapshot["estimatedTokens"] || "Unknown") + "\n",
|
|
1474
|
-
);
|
|
1475
|
-
console.log(
|
|
1476
|
-
"> Risk Score: " +
|
|
1477
|
-
snapshot.riskScore +
|
|
1478
|
-
" (Blast Radius: " +
|
|
1479
|
-
snapshot.blastRadius +
|
|
1480
|
-
")\n",
|
|
1481
|
-
);
|
|
1482
|
-
|
|
1483
|
-
if (Object.keys(snapshot.imports).length > 0) {
|
|
1484
|
-
console.log("## Imports");
|
|
1485
|
-
for (const [imp, exports] of Object.entries(snapshot.imports)) {
|
|
1486
|
-
if (exports && exports.length > 0) {
|
|
1487
|
-
console.log("- `" + imp + "` (exports: " + exports.join(", ") + ")");
|
|
1488
|
-
} else {
|
|
1489
|
-
console.log("- `" + imp + "`");
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
console.log();
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
if (snapshot.dependents && snapshot.dependents.length > 0) {
|
|
1496
|
-
console.log("## Dependents");
|
|
1497
|
-
for (const dep of snapshot.dependents) {
|
|
1498
|
-
console.log("- `" + dep + "`");
|
|
1499
|
-
}
|
|
1500
|
-
console.log();
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
console.log("## Source Code");
|
|
1504
|
-
console.log("```javascript\n" + snapshot.content + "\n```\n");
|
|
1505
|
-
} catch (e) {
|
|
1506
|
-
console.error("Failed to read snapshot: " + e.message);
|
|
1507
|
-
process.exit(1);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
// ── Main ──────────────────────────────────────────────────
|
|
98
|
+
// Execute CLI when run directly
|
|
1512
99
|
if (require.main === module) {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
isSelfInstall,
|
|
1529
|
-
CORE_AGENTS,
|
|
1530
|
-
CORE_SKILLS,
|
|
1531
|
-
generateIDEBridges,
|
|
1532
|
-
cmdMarathon,
|
|
1533
|
-
};
|
|
1534
|
-
}
|
|
100
|
+
main();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Module exports for unit test suite backward compatibility
|
|
104
|
+
module.exports = {
|
|
105
|
+
parseArgs,
|
|
106
|
+
compareSemver,
|
|
107
|
+
copyDir,
|
|
108
|
+
countDir,
|
|
109
|
+
isSelfInstall,
|
|
110
|
+
CORE_AGENTS,
|
|
111
|
+
CORE_SKILLS,
|
|
112
|
+
generateIDEBridges,
|
|
113
|
+
cmdMarathon,
|
|
114
|
+
};
|