tribunal-kit 5.7.0 → 5.8.1
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 +6 -7
- package/.agent/agents/frontend-reviewer.md +13 -0
- package/.agent/agents/frontend-specialist.md +14 -0
- package/.agent/agents/logic-reviewer.md +11 -0
- package/.agent/agents/orchestrator.md +15 -0
- package/.agent/agents/project-planner.md +5 -0
- package/.agent/agents/security-auditor.md +13 -0
- package/.agent/agents/ui-ux-auditor.md +7 -31
- package/.agent/history/memory/.memory.idx +1693 -0
- package/.agent/history/memory/MEMORY.md +123 -0
- package/.agent/routing_index.json +694 -714
- package/.agent/rules/GEMINI.md +88 -13
- package/.agent/scripts/_colors.js +131 -89
- package/.agent/scripts/_utils.js +163 -128
- package/.agent/scripts/auto_preview.js +207 -197
- package/.agent/scripts/bundle_analyzer.js +227 -192
- package/.agent/scripts/case_law_manager.js +991 -689
- package/.agent/scripts/checklist.js +233 -190
- package/.agent/scripts/context_broker.js +930 -605
- package/.agent/scripts/dependency_analyzer.js +275 -184
- package/.agent/scripts/graph_builder.js +412 -341
- package/.agent/scripts/graph_visualizer.js +392 -390
- package/.agent/scripts/graph_zoom.js +198 -156
- package/.agent/scripts/inner_loop_validator.js +523 -445
- package/.agent/scripts/lint_runner.js +199 -157
- package/.agent/scripts/marathon_harness.js +819 -661
- package/.agent/scripts/minify_context.js +115 -100
- package/.agent/scripts/mutation_runner.js +321 -280
- package/.agent/scripts/prompt_compiler.js +62 -42
- package/.agent/scripts/schema_validator.js +373 -280
- package/.agent/scripts/security_scan.js +333 -190
- package/.agent/scripts/session_manager.js +306 -270
- package/.agent/scripts/skill_evolution.js +810 -637
- package/.agent/scripts/skill_integrator.js +327 -307
- package/.agent/scripts/strengthen_skills.js +203 -193
- package/.agent/scripts/swarm_dispatcher.js +558 -457
- package/.agent/scripts/test_runner.js +178 -152
- package/.agent/scripts/verify_all.js +200 -168
- package/.agent/skills/fabel-protocol/SKILL.md +271 -0
- package/.agent/skills/thinking-protocol/SKILL.md +27 -0
- package/.agent/workflows/generate.md +2 -1
- package/.agent/workflows/tribunal-full.md +4 -3
- package/.agent/workflows/tribunal-speed.md +1 -1
- package/README.md +184 -58
- package/bin/mcp-server.js +496 -173
- package/bin/tribunal-kit.js +1245 -987
- package/bin/wrapper.js +108 -74
- package/dist/cli.js +44 -0
- package/dist/commands/align.js +201 -0
- package/dist/commands/case.js +23 -0
- package/dist/commands/compile.js +84 -0
- package/dist/commands/init.js +42 -0
- package/dist/commands/learn.js +57 -0
- package/dist/commands/memory.js +456 -0
- package/package.json +22 -10
- package/scripts/benchmark.js +162 -125
- package/scripts/changelog.js +196 -168
- package/scripts/sync-version.js +94 -81
- package/scripts/validate-payload.js +85 -78
package/bin/wrapper.js
CHANGED
|
@@ -1,108 +1,142 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Tribunal-Kit Core Wrapper
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* This script routes commands to the ultra-fast Rust binary if available and supported.
|
|
6
6
|
* For legacy commands (or if the binary isn't available/compiled yet), it gracefully
|
|
7
7
|
* falls back to the original JavaScript implementation.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const fs = require(
|
|
11
|
-
const path = require(
|
|
12
|
-
const { spawnSync } = require(
|
|
13
|
-
const os = require(
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const { spawnSync } = require("child_process");
|
|
13
|
+
const os = require("os");
|
|
14
14
|
|
|
15
15
|
// Commands that have been fully ported to Rust so far
|
|
16
|
-
const RUST_COMMANDS = new Set([
|
|
16
|
+
const RUST_COMMANDS = new Set([
|
|
17
|
+
"init",
|
|
18
|
+
"validate",
|
|
19
|
+
"status",
|
|
20
|
+
"sync",
|
|
21
|
+
"hook",
|
|
22
|
+
"uninstall",
|
|
23
|
+
"memory",
|
|
24
|
+
]);
|
|
17
25
|
|
|
18
26
|
// Determine the path to the compiled Rust binary
|
|
19
27
|
// In a full production release, this checks optionalDependencies in node_modules
|
|
20
28
|
// For development, it checks the local target/release folder
|
|
21
29
|
function getBinaryPath() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
// Try to resolve the binary from the optional dependency package
|
|
31
|
-
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
32
|
-
const binPath = path.resolve(path.dirname(pkgPath), `bin/tribunal-core${ext}`);
|
|
33
|
-
if (fs.existsSync(binPath)) {
|
|
34
|
-
return binPath;
|
|
35
|
-
}
|
|
36
|
-
} catch (e) {
|
|
37
|
-
// Package not found, ignore and fall back to local dev targets
|
|
38
|
-
}
|
|
30
|
+
if (process.env.TRIBUNAL_FORCE_JS === "1") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const isWindows = os.platform() === "win32";
|
|
34
|
+
const ext = isWindows ? ".exe" : "";
|
|
35
|
+
const platform = os.platform();
|
|
36
|
+
const arch = os.arch();
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
// First, try production resolution (from optionalDependencies)
|
|
39
|
+
const pkgName = `@tribunal-kit/core-${platform}-${arch}`;
|
|
40
|
+
try {
|
|
41
|
+
// Try to resolve the binary from the optional dependency package
|
|
42
|
+
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
43
|
+
const binPath = path.resolve(
|
|
44
|
+
path.dirname(pkgPath),
|
|
45
|
+
`bin/tribunal-core${ext}`,
|
|
46
|
+
);
|
|
47
|
+
if (fs.existsSync(binPath)) {
|
|
48
|
+
return binPath;
|
|
44
49
|
}
|
|
50
|
+
} catch {
|
|
51
|
+
// Package not found, ignore and fall back to local dev targets
|
|
52
|
+
}
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
// Second, try to find the binary compiled from crates/core/Cargo.toml (Local dev)
|
|
55
|
+
const devPath = path.resolve(
|
|
56
|
+
__dirname,
|
|
57
|
+
"..",
|
|
58
|
+
"target",
|
|
59
|
+
"release",
|
|
60
|
+
`tribunal-core${ext}`,
|
|
61
|
+
);
|
|
62
|
+
if (fs.existsSync(devPath)) {
|
|
63
|
+
return devPath;
|
|
64
|
+
}
|
|
51
65
|
|
|
52
|
-
|
|
66
|
+
// Third, try target/debug (if they ran `cargo build` instead of `--release`)
|
|
67
|
+
const debugPath = path.resolve(
|
|
68
|
+
__dirname,
|
|
69
|
+
"..",
|
|
70
|
+
"target",
|
|
71
|
+
"debug",
|
|
72
|
+
`tribunal-core${ext}`,
|
|
73
|
+
);
|
|
74
|
+
if (fs.existsSync(debugPath)) {
|
|
75
|
+
return debugPath;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null;
|
|
53
79
|
}
|
|
54
80
|
|
|
55
81
|
function runRustBinary(binPath, args) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
82
|
+
const stdio = [
|
|
83
|
+
"inherit",
|
|
84
|
+
"inherit",
|
|
85
|
+
"inherit",
|
|
86
|
+
];
|
|
87
|
+
const result = spawnSync(binPath, args, {
|
|
88
|
+
stdio: stdio,
|
|
89
|
+
env: process.env,
|
|
90
|
+
});
|
|
66
91
|
|
|
67
|
-
|
|
92
|
+
if (result.error) {
|
|
93
|
+
console.error(
|
|
94
|
+
`\x1b[91m✖ Failed to execute Rust engine:\x1b[0m ${result.error.message}`,
|
|
95
|
+
);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
process.exit(result.status || 0);
|
|
68
100
|
}
|
|
69
101
|
|
|
70
102
|
function runLegacyFallback() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
// Use the modular dist/ CLI with lazy-loaded commands for faster cold-start.
|
|
104
|
+
// Each command module is require()'d only when invoked (~70% fewer files loaded).
|
|
105
|
+
const { main } = require("../dist/cli.js");
|
|
106
|
+
main();
|
|
75
107
|
}
|
|
76
108
|
|
|
77
109
|
function main() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
// Skip 'node' and 'wrapper.js'
|
|
111
|
+
const args = process.argv.slice(2);
|
|
112
|
+
|
|
113
|
+
// Extract the command (the first non-flag argument)
|
|
114
|
+
const command = args.find((a) => !a.startsWith("-"));
|
|
115
|
+
|
|
116
|
+
if (command && RUST_COMMANDS.has(command)) {
|
|
117
|
+
const binPath = getBinaryPath();
|
|
118
|
+
|
|
119
|
+
if (binPath) {
|
|
120
|
+
// For the init command, Rust needs to know where the .agent template folder is.
|
|
121
|
+
if (command === "init") {
|
|
122
|
+
const sourceDir = path.resolve(__dirname, "..", ".agent");
|
|
123
|
+
args.push("--source-dir", sourceDir);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Route to Rust engine
|
|
127
|
+
// console.log('\x1b[90m⚡ Executing via Rust Core Engine\x1b[0m');
|
|
128
|
+
runRustBinary(binPath, args);
|
|
129
|
+
return;
|
|
130
|
+
} else {
|
|
131
|
+
// Warn if Rust command was requested but binary is missing
|
|
132
|
+
console.warn(
|
|
133
|
+
"\x1b[93m⚠ Rust binary not found in target/. Falling back to JS engine.\x1b[0m",
|
|
134
|
+
);
|
|
102
135
|
}
|
|
136
|
+
}
|
|
103
137
|
|
|
104
|
-
|
|
105
|
-
|
|
138
|
+
// Fall back to JS logic for un-ported commands (e.g. `learn`, `case`, `marathon`)
|
|
139
|
+
runLegacyFallback();
|
|
106
140
|
}
|
|
107
141
|
|
|
108
142
|
main();
|
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,10 @@ function parseArgs(argv) {
|
|
|
46
46
|
args.flags.skipUpdateCheck = true;
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
|
+
if (arg === '--write') {
|
|
50
|
+
args.flags.write = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
49
53
|
if (arg === '--head') {
|
|
50
54
|
args.flags.head = true;
|
|
51
55
|
continue;
|
|
@@ -62,6 +66,18 @@ function parseArgs(argv) {
|
|
|
62
66
|
}
|
|
63
67
|
args.flags.path = nextVal;
|
|
64
68
|
}
|
|
69
|
+
if (arg.startsWith('--target=')) {
|
|
70
|
+
args.flags.target = arg.split('=').slice(1).join('=');
|
|
71
|
+
}
|
|
72
|
+
if (arg === '--target') {
|
|
73
|
+
const idx = raw.indexOf('--target');
|
|
74
|
+
const nextVal = raw[idx + 1];
|
|
75
|
+
if (!nextVal || nextVal.startsWith('--')) {
|
|
76
|
+
console.error(` \x1b[91m✖ --target requires an argument\x1b[0m`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
args.flags.target = nextVal;
|
|
80
|
+
}
|
|
65
81
|
if (arg.startsWith('--branch=')) {
|
|
66
82
|
args.flags.branch = arg.split('=').slice(1).join('=');
|
|
67
83
|
}
|
|
@@ -84,20 +100,25 @@ function cmdHelp(quiet = false) {
|
|
|
84
100
|
(0, logger_1.log)(cmd('mutate', 'Run the Mutation Engine to test test-suite reliability'));
|
|
85
101
|
(0, logger_1.log)(cmd('context', 'Retrieve a highly-optimized Context Snapshot for a file'));
|
|
86
102
|
(0, logger_1.log)(cmd('sync', 'Synchronize IDE bridge files with current rules'));
|
|
103
|
+
(0, logger_1.log)(cmd('align', 'Clean AI outputs (strip slop, collapse lists, validate code traps)'));
|
|
87
104
|
(0, logger_1.log)(cmd('marathon', 'Long-running agent harness (init, status, next, mark)'));
|
|
88
105
|
(0, logger_1.log)(cmd('hook', 'Install pre-push git hook for auto-learning'));
|
|
106
|
+
(0, logger_1.log)(cmd('compile', 'Compile rules into a static instruction file for terminal agents'));
|
|
107
|
+
(0, logger_1.log)(cmd('memory', '4-Type Taxonomy Persistent Memory Engine (store, recall, gc, stats, export)'));
|
|
89
108
|
(0, logger_1.log)(cmd('uninstall', 'Remove .agent/ folder from project'));
|
|
90
109
|
console.log();
|
|
91
110
|
(0, logger_1.log)((0, logger_1.bold)(' Options'));
|
|
92
111
|
(0, logger_1.log)(` ${(0, logger_1.c)('gray', '─'.repeat(40))}`);
|
|
93
112
|
(0, logger_1.log)(opt('--force', 'Overwrite existing .agent/ folder'));
|
|
94
113
|
(0, logger_1.log)(opt('--path <dir>', 'Install in specific directory'));
|
|
114
|
+
(0, logger_1.log)(opt('--target <name>', 'Target terminal agent for compile (e.g. aider)'));
|
|
95
115
|
(0, logger_1.log)(opt('--quiet', 'Suppress all output'));
|
|
96
116
|
(0, logger_1.log)(opt('--verbose', 'Show detailed debug logging'));
|
|
97
117
|
(0, logger_1.log)(opt('--dry-run', 'Preview actions without executing'));
|
|
98
118
|
(0, logger_1.log)(opt('--minimal', 'Install core agents/skills only (~13 agents)'));
|
|
99
119
|
(0, logger_1.log)(opt('--skip-update-check', 'Skip auto-update version check'));
|
|
100
120
|
(0, logger_1.log)(opt('--head', '(learn) Diff against last commit instead of staged'));
|
|
121
|
+
(0, logger_1.log)(opt('--write', '(align) Write aligned output in-place to the target file'));
|
|
101
122
|
console.log();
|
|
102
123
|
(0, logger_1.log)((0, logger_1.bold)(' Aliases'));
|
|
103
124
|
(0, logger_1.log)(` ${(0, logger_1.c)('gray', '─'.repeat(40))}`);
|
|
@@ -128,6 +149,14 @@ function cmdHelp(quiet = false) {
|
|
|
128
149
|
(0, logger_1.log)(ex('tk marathon next'));
|
|
129
150
|
(0, logger_1.log)(ex('tk marathon mark 5 pass'));
|
|
130
151
|
(0, logger_1.log)(ex('tk hook'));
|
|
152
|
+
(0, logger_1.log)(ex('tk compile'));
|
|
153
|
+
(0, logger_1.log)(ex('echo "Certainly! Here is - item 1" | tk align'));
|
|
154
|
+
(0, logger_1.log)(ex('tk align output.md --write'));
|
|
155
|
+
(0, logger_1.log)(ex('tk memory store --type semantic --content "Uses PostgreSQL" --tags db,orm'));
|
|
156
|
+
(0, logger_1.log)(ex('tk memory recall --query "database" --budget 2000'));
|
|
157
|
+
(0, logger_1.log)(ex('tk memory gc'));
|
|
158
|
+
(0, logger_1.log)(ex('tk memory stats'));
|
|
159
|
+
(0, logger_1.log)(ex('tk memory export'));
|
|
131
160
|
(0, logger_1.log)(ex('tk uninstall'));
|
|
132
161
|
console.log();
|
|
133
162
|
}
|
|
@@ -200,16 +229,31 @@ async function runWithUpdateCheck(command, flags) {
|
|
|
200
229
|
await cmdSync();
|
|
201
230
|
break;
|
|
202
231
|
}
|
|
232
|
+
case 'align': {
|
|
233
|
+
const cmdAlign = loadCmd('./commands/align', 'cmdAlign');
|
|
234
|
+
await cmdAlign(flags, process.argv, quiet);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
203
237
|
case 'marathon': {
|
|
204
238
|
const cmdMarathon = loadCmd('./commands/marathon', 'cmdMarathon');
|
|
205
239
|
await cmdMarathon(flags, process.argv, quiet);
|
|
206
240
|
break;
|
|
207
241
|
}
|
|
242
|
+
case 'compile': {
|
|
243
|
+
const cmdCompile = loadCmd('./commands/compile', 'cmdCompile');
|
|
244
|
+
await cmdCompile(flags, quiet);
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
208
247
|
case 'uninstall': {
|
|
209
248
|
const cmdUninstall = loadCmd('./commands/uninstall', 'cmdUninstall');
|
|
210
249
|
cmdUninstall(flags, quiet);
|
|
211
250
|
break;
|
|
212
251
|
}
|
|
252
|
+
case 'memory': {
|
|
253
|
+
const cmdMemory = loadCmd('./commands/memory', 'cmdMemory');
|
|
254
|
+
await cmdMemory(flags, process.argv, quiet);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
213
257
|
case 'help':
|
|
214
258
|
case '--help':
|
|
215
259
|
case '-h':
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const readline = require("readline");
|
|
6
|
+
const { log, err, ok, c, bold, warn } = require("../utils/logger");
|
|
7
|
+
|
|
8
|
+
function alignText(text) {
|
|
9
|
+
if (!text) return "";
|
|
10
|
+
|
|
11
|
+
let cleaned = text.trim();
|
|
12
|
+
let matches = true;
|
|
13
|
+
|
|
14
|
+
// 1. Strip Conversational Introduction Slop step-by-step
|
|
15
|
+
while (matches) {
|
|
16
|
+
matches = false;
|
|
17
|
+
const prefixes = [
|
|
18
|
+
/^(?:sure|certainly|okay|absolutely|great|of course|as requested|as you asked|happy to help|here is|here's|let's|i can help)(?:[^\n]*?)(?:[.!?;:]|\n)\s*/i,
|
|
19
|
+
/^(?:I'd be happy to help with that\.|I can certainly help you with that\.|Let me help you with that\.|Here's what you requested:)\s*/i,
|
|
20
|
+
/^(?:here is the implementation|here is the code|here are the details|here is your code|here's the implementation|here's the code|here's the solution|here are the details:)(?:[^\n]*?)(?:[.!?;:]|\n)\s*/i
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
for (const regex of prefixes) {
|
|
24
|
+
const temp = cleaned.replace(regex, "");
|
|
25
|
+
if (temp !== cleaned) {
|
|
26
|
+
cleaned = temp.trim();
|
|
27
|
+
matches = true;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Strip Conversational Conclusion Slop
|
|
34
|
+
const outroRegexes = [
|
|
35
|
+
/[\r\n\s]*(?:i hope this helps|let me know if you need|let me know if this works|please review the code|let me know if you have any questions|feel free to ask|hope that helps|happy coding)(?:.*)$/gi,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
for (const regex of outroRegexes) {
|
|
39
|
+
cleaned = cleaned.replace(regex, "");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 3. Bullet-point collapse logic (1-2 item list -> prose)
|
|
43
|
+
const lines = cleaned.split(/\r?\n/);
|
|
44
|
+
const resultLines = [];
|
|
45
|
+
let i = 0;
|
|
46
|
+
|
|
47
|
+
while (i < lines.length) {
|
|
48
|
+
const line = lines[i];
|
|
49
|
+
const bulletMatch = line.match(/^(\s*)([-*]|\d+\.)\s+(.*)$/);
|
|
50
|
+
|
|
51
|
+
if (bulletMatch) {
|
|
52
|
+
const listItems = [];
|
|
53
|
+
const indent = bulletMatch[1];
|
|
54
|
+
let j = i;
|
|
55
|
+
|
|
56
|
+
while (j < lines.length) {
|
|
57
|
+
const nextLine = lines[j];
|
|
58
|
+
const nextMatch = nextLine.match(/^(\s*)([-*]|\d+\.)\s+(.*)$/);
|
|
59
|
+
if (nextMatch && nextMatch[1].length === indent.length) {
|
|
60
|
+
listItems.push({ index: j, content: nextMatch[3] });
|
|
61
|
+
j++;
|
|
62
|
+
} else if (nextLine.trim() === "") {
|
|
63
|
+
if (j + 1 < lines.length) {
|
|
64
|
+
const lookahead = lines[j + 1];
|
|
65
|
+
const lookaheadMatch = lookahead.match(/^(\s*)([-*]|\d+\.)\s+(.*)$/);
|
|
66
|
+
if (lookaheadMatch && lookaheadMatch[1].length === indent.length) {
|
|
67
|
+
j++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
} else {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (listItems.length > 0 && listItems.length <= 2) {
|
|
78
|
+
const collapsedProse = listItems.map(item => {
|
|
79
|
+
let content = item.content.trim();
|
|
80
|
+
if (content && !/[.!?]$/.test(content)) {
|
|
81
|
+
content += ".";
|
|
82
|
+
}
|
|
83
|
+
if (content) {
|
|
84
|
+
content = content.charAt(0).toUpperCase() + content.slice(1);
|
|
85
|
+
}
|
|
86
|
+
return content;
|
|
87
|
+
}).join(" ");
|
|
88
|
+
resultLines.push(indent + collapsedProse);
|
|
89
|
+
i = j;
|
|
90
|
+
} else {
|
|
91
|
+
for (let k = i; k < j; k++) {
|
|
92
|
+
resultLines.push(lines[k]);
|
|
93
|
+
}
|
|
94
|
+
i = j;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
resultLines.push(line);
|
|
98
|
+
i++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
cleaned = resultLines.join("\n");
|
|
103
|
+
return cleaned.trim();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function validateCodeContent(text) {
|
|
107
|
+
const warnings = [];
|
|
108
|
+
|
|
109
|
+
// Next.js 15 unawaited dynamic properties check
|
|
110
|
+
const unawaitedNext15Regex = /(?<!await\s+)(cookies|headers|params)\s*\(\s*\)\s*\.\s*(get|has|set|delete|toString)/g;
|
|
111
|
+
if (unawaitedNext15Regex.test(text)) {
|
|
112
|
+
warnings.push("Next.js 15: Found unawaited call to cookies(), headers(), or params(). In Next.js 15+, these are async and must be awaited.");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// React 19 useFormState check
|
|
116
|
+
if (text.includes("useFormState")) {
|
|
117
|
+
warnings.push("React 19: Found useFormState. In React 19, this is renamed to useActionState.");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Drizzle .filter() check
|
|
121
|
+
const drizzleFilterRegex = /\.from\s*\([^)]*\)\s*\.\s*filter\s*\(/;
|
|
122
|
+
if (drizzleFilterRegex.test(text)) {
|
|
123
|
+
warnings.push("Drizzle ORM: Found .from().filter(). Drizzle does not use .filter(), use .where() instead.");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// OpenAI gpt-5 or claude-4-opus checks
|
|
127
|
+
if (text.includes("gpt-5") || text.includes("claude-4-opus")) {
|
|
128
|
+
warnings.push("LLM Models: Found references to non-existent models (gpt-5, claude-4-opus).");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return warnings;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function cmdAlign(flags, argv, quiet) {
|
|
135
|
+
let inputSource = null;
|
|
136
|
+
|
|
137
|
+
// Find if a file path is specified
|
|
138
|
+
const positionalArgs = argv.slice(3).filter(arg => !arg.startsWith("--"));
|
|
139
|
+
if (positionalArgs.length > 0) {
|
|
140
|
+
inputSource = positionalArgs[0];
|
|
141
|
+
} else if (flags.path) {
|
|
142
|
+
inputSource = flags.path;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let textContent = "";
|
|
146
|
+
|
|
147
|
+
if (inputSource) {
|
|
148
|
+
// Read from file
|
|
149
|
+
const resolvedPath = path.resolve(inputSource);
|
|
150
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
151
|
+
err(`File not found: ${inputSource}`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
textContent = fs.readFileSync(resolvedPath, "utf8");
|
|
155
|
+
} else {
|
|
156
|
+
// Read from stdin
|
|
157
|
+
textContent = await new Promise((resolve) => {
|
|
158
|
+
let data = "";
|
|
159
|
+
const rl = readline.createInterface({
|
|
160
|
+
input: process.stdin,
|
|
161
|
+
output: process.stdout,
|
|
162
|
+
terminal: false
|
|
163
|
+
});
|
|
164
|
+
rl.on("line", (line) => {
|
|
165
|
+
data += line + "\n";
|
|
166
|
+
});
|
|
167
|
+
rl.on("close", () => {
|
|
168
|
+
resolve(data);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const aligned = alignText(textContent);
|
|
174
|
+
const warnings = validateCodeContent(aligned);
|
|
175
|
+
|
|
176
|
+
// Print warnings to stderr so they don't corrupt stdout piping
|
|
177
|
+
if (warnings.length > 0 && !quiet) {
|
|
178
|
+
process.stderr.write("\n" + bold(c("yellow", "⚠️ OCAE Alignment Validator Warnings:")) + "\n");
|
|
179
|
+
for (const warnMsg of warnings) {
|
|
180
|
+
process.stderr.write(` ${c("yellow", "●")} ${warnMsg}\n`);
|
|
181
|
+
}
|
|
182
|
+
process.stderr.write("\n");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (flags.write && inputSource) {
|
|
186
|
+
// Write in-place to the file
|
|
187
|
+
fs.writeFileSync(path.resolve(inputSource), aligned, "utf8");
|
|
188
|
+
if (!quiet) {
|
|
189
|
+
ok(`Aligned output written in-place to: ${c("cyan", inputSource)}`);
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
// Print to stdout
|
|
193
|
+
process.stdout.write(aligned + "\n");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
cmdAlign,
|
|
199
|
+
alignText,
|
|
200
|
+
validateCodeContent
|
|
201
|
+
};
|
package/dist/commands/case.js
CHANGED
|
@@ -41,6 +41,29 @@ async function cmdCase(flags, processArgs, quiet = false) {
|
|
|
41
41
|
pyArgs = pyArgs.replace(/^search/, 'search-cases');
|
|
42
42
|
try {
|
|
43
43
|
await (0, helpers_1.runShellAsync)(`node "${caseLawScript}" ${pyArgs}`, { stdio: 'inherit', cwd: targetDir });
|
|
44
|
+
// Memory Bridge: When a case is added, auto-store a SEMANTIC memory
|
|
45
|
+
if (args.startsWith('add')) {
|
|
46
|
+
try {
|
|
47
|
+
const { _memoryStore } = require('./memory');
|
|
48
|
+
// Extract a summary from the add-case args (best effort)
|
|
49
|
+
const contentMatch = args.match(/--violation\s+"([^"]+)"/i) || args.match(/--violation\s+(\S+)/i);
|
|
50
|
+
const domainMatch = args.match(/--domain\s+"([^"]+)"/i) || args.match(/--domain\s+(\S+)/i);
|
|
51
|
+
const violation = contentMatch ? contentMatch[1] : 'Unknown violation';
|
|
52
|
+
const domain = domainMatch ? domainMatch[1] : 'general';
|
|
53
|
+
_memoryStore(
|
|
54
|
+
agentDest,
|
|
55
|
+
'semantic',
|
|
56
|
+
`CASE LAW REJECTION: ${violation} (domain: ${domain})`,
|
|
57
|
+
['case-law', domain, 'rejection'],
|
|
58
|
+
null
|
|
59
|
+
);
|
|
60
|
+
if (!quiet) {
|
|
61
|
+
(0, logger_1.log)(` ${(0, logger_1.c)('cyan', '\u25b8')} Memory bridge: case law rejection stored to memory index`);
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Non-critical — case law was still saved, memory bridge is a bonus
|
|
65
|
+
}
|
|
66
|
+
}
|
|
44
67
|
}
|
|
45
68
|
catch {
|
|
46
69
|
process.exit(1); // Script already prints errors
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cmdCompile = cmdCompile;
|
|
4
|
+
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const { log, err, ok, c, bold, dbg } = require("../utils/logger");
|
|
8
|
+
|
|
9
|
+
async function cmdCompile(flags, quiet) {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const agentDir = path.join(cwd, ".agent");
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(agentDir)) {
|
|
14
|
+
err("Tribunal Kit is not installed in this directory (.agent folder missing). Run 'tk init' first.");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
log(bold("Compiling Tribunal Kit rules..."));
|
|
19
|
+
|
|
20
|
+
let compiledContext = "# Tribunal Kit Context\n\n";
|
|
21
|
+
compiledContext += "The following are rules, agents, and skills from the Tribunal Kit.\n\n";
|
|
22
|
+
|
|
23
|
+
// 1. Read GEMINI.md
|
|
24
|
+
const geminiPath = path.join(agentDir, "rules", "GEMINI.md");
|
|
25
|
+
if (fs.existsSync(geminiPath)) {
|
|
26
|
+
compiledContext += "## Global Rules (GEMINI.md)\n\n";
|
|
27
|
+
compiledContext += fs.readFileSync(geminiPath, "utf-8") + "\n\n";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Read Agents
|
|
31
|
+
const agentsDir = path.join(agentDir, "agents");
|
|
32
|
+
if (fs.existsSync(agentsDir)) {
|
|
33
|
+
compiledContext += "## Agents\n\n";
|
|
34
|
+
const agents = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
|
|
35
|
+
for (const file of agents) {
|
|
36
|
+
const content = fs.readFileSync(path.join(agentsDir, file), "utf-8");
|
|
37
|
+
compiledContext += `### Agent: ${file}\n\n`;
|
|
38
|
+
compiledContext += content + "\n\n";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 3. Read Skills
|
|
43
|
+
const skillsDir = path.join(agentDir, "skills");
|
|
44
|
+
if (fs.existsSync(skillsDir)) {
|
|
45
|
+
compiledContext += "## Skills\n\n";
|
|
46
|
+
const skills = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
47
|
+
for (const dirent of skills) {
|
|
48
|
+
if (dirent.isDirectory()) {
|
|
49
|
+
const skillPath = path.join(skillsDir, dirent.name, "SKILL.md");
|
|
50
|
+
if (fs.existsSync(skillPath)) {
|
|
51
|
+
const content = fs.readFileSync(skillPath, "utf-8");
|
|
52
|
+
compiledContext += `### Skill: ${dirent.name}\n\n`;
|
|
53
|
+
compiledContext += content + "\n\n";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 4. Read Workflows
|
|
60
|
+
const workflowsDir = path.join(agentDir, "workflows");
|
|
61
|
+
if (fs.existsSync(workflowsDir)) {
|
|
62
|
+
compiledContext += "## Workflows\n\n";
|
|
63
|
+
const workflows = fs.readdirSync(workflowsDir).filter(f => f.endsWith('.md'));
|
|
64
|
+
for (const file of workflows) {
|
|
65
|
+
const content = fs.readFileSync(path.join(workflowsDir, file), "utf-8");
|
|
66
|
+
compiledContext += `### Workflow: ${file}\n\n`;
|
|
67
|
+
compiledContext += content + "\n\n";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let targetFile = ".tribunal-compiled.md";
|
|
72
|
+
if (flags.target) {
|
|
73
|
+
if (flags.target === "aider") targetFile = ".aider.conf.yml";
|
|
74
|
+
if (flags.target === "claude") targetFile = ".claude.json";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const outputPath = path.join(cwd, targetFile);
|
|
78
|
+
fs.writeFileSync(outputPath, compiledContext, "utf-8");
|
|
79
|
+
|
|
80
|
+
ok(`Compiled rules written to ${c('cyan', targetFile)}`);
|
|
81
|
+
if (!quiet) {
|
|
82
|
+
log(` ${c('gray', 'Load this file into your terminal agent (e.g. Claude Code, Aider, OpenCode)')}`);
|
|
83
|
+
}
|
|
84
|
+
}
|