tribunal-kit 4.5.0 → 4.5.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/README.md +1 -1
- package/bin/mcp-server.js +159 -0
- package/bin/tribunal-kit.js +105 -29
- package/bin/wrapper.js +16 -7
- package/mcp_config.json +11 -0
- package/package.json +14 -6
- package/scripts/changelog.js +4 -3
- package/scripts/validate-payload.js +6 -1
- package/scripts/postinstall.js +0 -127
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/tribunal-kit)
|
|
12
12
|
[](LICENSE)
|
|
13
|
-
[](CHANGELOG.md)
|
|
14
14
|
[](mcp_config.json)
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tribunal-Kit MCP Server
|
|
5
|
+
*
|
|
6
|
+
* This file exposes tribunal-kit tools via the Model Context Protocol (MCP)
|
|
7
|
+
* over standard I/O, allowing AI clients (Cursor, Windsurf, Claude) to natively
|
|
8
|
+
* invoke tribunal checks.
|
|
9
|
+
*
|
|
10
|
+
* Protocol: MCP 2024-11-05 over JSON-RPC 2.0 / stdio
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { spawnSync } = require('child_process');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const CLI = path.resolve(__dirname, './tribunal-kit.js');
|
|
17
|
+
const PKG = require(path.resolve(__dirname, '../package.json'));
|
|
18
|
+
|
|
19
|
+
// Timeout for spawned processes (30 seconds)
|
|
20
|
+
const SPAWN_TIMEOUT_MS = 30000;
|
|
21
|
+
|
|
22
|
+
// Minimal JSON-RPC 2.0 over stdio
|
|
23
|
+
const readline = require('readline');
|
|
24
|
+
|
|
25
|
+
const rl = readline.createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
terminal: false
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Run a CLI command with timeout protection.
|
|
33
|
+
* Returns { stdout, stderr, status }.
|
|
34
|
+
*/
|
|
35
|
+
function runCommand(args) {
|
|
36
|
+
return spawnSync(process.execPath, [CLI, ...args], {
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleRequest(req) {
|
|
43
|
+
// MCP spec: method names follow path-style convention
|
|
44
|
+
if (req.method === 'initialize') {
|
|
45
|
+
return {
|
|
46
|
+
protocolVersion: "2024-11-05",
|
|
47
|
+
capabilities: {
|
|
48
|
+
tools: {}
|
|
49
|
+
},
|
|
50
|
+
serverInfo: {
|
|
51
|
+
name: "tribunal-kit-mcp",
|
|
52
|
+
version: PKG.version
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (req.method === 'tools/list') {
|
|
58
|
+
return {
|
|
59
|
+
tools: [
|
|
60
|
+
{
|
|
61
|
+
name: "run_tribunal_audit",
|
|
62
|
+
description: "Runs a full anti-hallucination audit across the workspace.",
|
|
63
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "sync_ide_bridges",
|
|
67
|
+
description: "Synchronize IDE bridge files with the current GEMINI.md rules.",
|
|
68
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "search_case_law",
|
|
72
|
+
description: "Search historical code rejections and legal precedent. Use this before writing code to avoid past mistakes.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
query: { type: "string", description: "Search query (e.g. 'useEffect state')" }
|
|
77
|
+
},
|
|
78
|
+
required: ["query"],
|
|
79
|
+
additionalProperties: false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (req.method === 'tools/call') {
|
|
87
|
+
const toolName = req.params && req.params.name;
|
|
88
|
+
if (!toolName) {
|
|
89
|
+
throw { code: -32602, message: "Missing required parameter: params.name" };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (toolName === 'run_tribunal_audit') {
|
|
93
|
+
const result = runCommand(['validate']);
|
|
94
|
+
return { content: [{ type: "text", text: result.stdout || result.stderr || "No output" }] };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (toolName === 'sync_ide_bridges') {
|
|
98
|
+
const result = runCommand(['sync']);
|
|
99
|
+
return { content: [{ type: "text", text: result.stdout || result.stderr || "No output" }] };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (toolName === 'search_case_law') {
|
|
103
|
+
const query = req.params && req.params.arguments && req.params.arguments.query;
|
|
104
|
+
if (!query || typeof query !== 'string') {
|
|
105
|
+
throw { code: -32602, message: "Missing or invalid required argument: query (string)" };
|
|
106
|
+
}
|
|
107
|
+
const script = path.resolve(__dirname, '../.agent/scripts/case_law_manager.js');
|
|
108
|
+
// Arguments passed as array — no shell interpolation
|
|
109
|
+
const result = spawnSync(process.execPath, [script, 'search-cases', '--query', query], {
|
|
110
|
+
encoding: 'utf8',
|
|
111
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
112
|
+
});
|
|
113
|
+
return { content: [{ type: "text", text: result.stdout || result.stderr || "No results" }] };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw { code: -32601, message: `Unknown tool: ${toolName}` };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw { code: -32601, message: `Unknown method: ${req.method}` };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
rl.on('line', (line) => {
|
|
123
|
+
if (!line.trim()) return;
|
|
124
|
+
|
|
125
|
+
let req;
|
|
126
|
+
try {
|
|
127
|
+
req = JSON.parse(line);
|
|
128
|
+
} catch (parseErr) {
|
|
129
|
+
// Invalid JSON — send a parse error
|
|
130
|
+
const errorRes = {
|
|
131
|
+
jsonrpc: "2.0",
|
|
132
|
+
id: null,
|
|
133
|
+
error: { code: -32700, message: "Parse error: " + parseErr.message }
|
|
134
|
+
};
|
|
135
|
+
console.log(JSON.stringify(errorRes));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const result = handleRequest(req);
|
|
141
|
+
const res = { jsonrpc: "2.0", id: req.id, result };
|
|
142
|
+
console.log(JSON.stringify(res));
|
|
143
|
+
|
|
144
|
+
// After initialize, send the initialized notification per MCP spec
|
|
145
|
+
if (req.method === 'initialize') {
|
|
146
|
+
console.log(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized", params: {} }));
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// Send proper JSON-RPC error response
|
|
150
|
+
const code = (e && typeof e.code === 'number') ? e.code : -32603;
|
|
151
|
+
const message = (e && e.message) ? e.message : "Internal server error";
|
|
152
|
+
const errorRes = {
|
|
153
|
+
jsonrpc: "2.0",
|
|
154
|
+
id: req.id || null,
|
|
155
|
+
error: { code, message }
|
|
156
|
+
};
|
|
157
|
+
console.log(JSON.stringify(errorRes));
|
|
158
|
+
}
|
|
159
|
+
});
|
package/bin/tribunal-kit.js
CHANGED
|
@@ -38,6 +38,24 @@ function runShellAsync(command, options) {
|
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Safely run a Node.js script with arguments as an array.
|
|
43
|
+
* No shell interpolation — immune to injection.
|
|
44
|
+
*/
|
|
45
|
+
function runScriptAsync(scriptPath, args = [], options = {}) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const child = spawn(process.execPath, [scriptPath, ...args], {
|
|
48
|
+
stdio: 'inherit',
|
|
49
|
+
...options,
|
|
50
|
+
});
|
|
51
|
+
child.on('close', code => {
|
|
52
|
+
if (code !== 0) reject(new Error(`Script failed with exit code ${code}`));
|
|
53
|
+
else resolve();
|
|
54
|
+
});
|
|
55
|
+
child.on('error', reject);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
const PKG = require(path.resolve(__dirname, '..', 'package.json'));
|
|
42
60
|
const CURRENT_VERSION = PKG.version;
|
|
43
61
|
|
|
@@ -245,6 +263,7 @@ async function autoUpdateCheck(originalArgs) {
|
|
|
245
263
|
return false;
|
|
246
264
|
}
|
|
247
265
|
|
|
266
|
+
log(' Checking for updates...');
|
|
248
267
|
const latestVersion = await fetchLatestVersion();
|
|
249
268
|
|
|
250
269
|
if (!latestVersion) {
|
|
@@ -385,15 +404,13 @@ async function cmdInit(flags) {
|
|
|
385
404
|
const backupDir = path.join(agentDest, '.backups', `backup-${Date.now()}`);
|
|
386
405
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
387
406
|
|
|
388
|
-
// PRESERVE_DIRS: user-generated content that must survive updates
|
|
389
|
-
const _PRESERVE_DIRS = ['history', 'patterns', 'mcp_config.json'];
|
|
390
407
|
const subdirs = ['agents', 'workflows', 'skills', 'scripts', '.shared', 'rules'];
|
|
391
408
|
for (const sub of subdirs) {
|
|
392
409
|
const subPath = path.join(agentDest, sub);
|
|
393
410
|
if (fs.existsSync(subPath)) {
|
|
394
411
|
// Copy to backup dir
|
|
395
412
|
await copyDir(subPath, path.join(backupDir, sub), false);
|
|
396
|
-
|
|
413
|
+
// Removed aggressive deletion so user custom files persist
|
|
397
414
|
}
|
|
398
415
|
}
|
|
399
416
|
log(` ${c('gray', '✦ Backed up existing configurations to .agent/.backups/')}`);
|
|
@@ -509,6 +526,8 @@ async function cmdInit(flags) {
|
|
|
509
526
|
console.log(plainRow('', () => ''));
|
|
510
527
|
console.log(` ${c('cyan', '╚' + '═'.repeat(W) + '╝')}`);
|
|
511
528
|
console.log();
|
|
529
|
+
log(` ${c('gray', '✦ Updating .gitignore...')}`);
|
|
530
|
+
await updateGitignore(targetDir, dryRun);
|
|
512
531
|
log(` ${c('gray', '✦ Generating IDE bridge files...')}`);
|
|
513
532
|
await generateIDEBridges(targetDir, agentDest, dryRun);
|
|
514
533
|
}
|
|
@@ -520,6 +539,30 @@ async function cmdInit(flags) {
|
|
|
520
539
|
}
|
|
521
540
|
}
|
|
522
541
|
|
|
542
|
+
// ── Gitignore Management ──────────────────────────────────
|
|
543
|
+
async function updateGitignore(targetDir, dryRun = false) {
|
|
544
|
+
if (dryRun) return;
|
|
545
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
546
|
+
const entries = ['.agent/.backups/', '.agent/history/'];
|
|
547
|
+
let content = '';
|
|
548
|
+
try {
|
|
549
|
+
content = await fs.promises.readFile(gitignorePath, 'utf8');
|
|
550
|
+
} catch (err) {
|
|
551
|
+
if (err.code !== 'ENOENT') throw err;
|
|
552
|
+
}
|
|
553
|
+
let appended = false;
|
|
554
|
+
for (const entry of entries) {
|
|
555
|
+
if (!content.includes(entry)) {
|
|
556
|
+
content += (content.length > 0 && !content.endsWith('\n') ? '\n' : '') + entry + '\n';
|
|
557
|
+
appended = true;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (appended) {
|
|
561
|
+
await fs.promises.writeFile(gitignorePath, content, 'utf8');
|
|
562
|
+
dbg(' Updated .gitignore');
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
523
566
|
// ── IDE Bridge Files ──────────────────────────────────────
|
|
524
567
|
// Each AI IDE reads rules from a different location.
|
|
525
568
|
// We generate bridge files that point each IDE at .agent/
|
|
@@ -532,17 +575,46 @@ async function generateIDEBridges(targetDir, agentDest, dryRun = false) {
|
|
|
532
575
|
// rules file doesn't exist
|
|
533
576
|
}
|
|
534
577
|
|
|
535
|
-
// Helper: write a bridge file
|
|
536
|
-
const writeBridge = async (filePath, content, label) => {
|
|
578
|
+
// Helper: write a bridge file or merge it if it exists
|
|
579
|
+
const writeBridge = async (filePath, content, label, isJson = false) => {
|
|
537
580
|
if (dryRun) {
|
|
538
|
-
dbg(` would create: ${filePath}`);
|
|
581
|
+
dbg(` would create/update: ${filePath}`);
|
|
539
582
|
return;
|
|
540
583
|
}
|
|
541
584
|
const dir = path.dirname(filePath);
|
|
585
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
586
|
+
|
|
542
587
|
try {
|
|
543
|
-
await fs.promises.
|
|
544
|
-
|
|
545
|
-
|
|
588
|
+
const existingContent = await fs.promises.readFile(filePath, 'utf8');
|
|
589
|
+
if (isJson) {
|
|
590
|
+
try {
|
|
591
|
+
const existingData = JSON.parse(existingContent);
|
|
592
|
+
const newData = JSON.parse(content);
|
|
593
|
+
|
|
594
|
+
if (!existingData.rules) existingData.rules = [];
|
|
595
|
+
const rulePath = newData.rules[0].path;
|
|
596
|
+
const ruleExists = existingData.rules.some(r => r.path === rulePath);
|
|
597
|
+
if (!ruleExists) {
|
|
598
|
+
existingData.rules.push(newData.rules[0]);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
existingData.agents = { ...existingData.agents, ...newData.agents };
|
|
602
|
+
existingData.skills = { ...existingData.skills, ...newData.skills };
|
|
603
|
+
existingData.workflows = { ...existingData.workflows, ...newData.workflows };
|
|
604
|
+
|
|
605
|
+
await fs.promises.writeFile(filePath, JSON.stringify(existingData, null, 2) + '\n', 'utf8');
|
|
606
|
+
ok(`${label} (merged) → ${c('gray', path.relative(targetDir, filePath))}`);
|
|
607
|
+
} catch (e) {
|
|
608
|
+
warn(`Failed to merge ${label}: ${e.message}`);
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
if (!existingContent.includes('Tribunal Kit') && (!rulesContent || !existingContent.includes(rulesContent.slice(0, 50)))) {
|
|
612
|
+
await fs.promises.appendFile(filePath, '\n' + content, 'utf8');
|
|
613
|
+
ok(`${label} (appended) → ${c('gray', path.relative(targetDir, filePath))}`);
|
|
614
|
+
} else {
|
|
615
|
+
dbg(` skip (rules exist): ${path.basename(filePath)}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
546
618
|
} catch (err) {
|
|
547
619
|
if (err.code === 'ENOENT') {
|
|
548
620
|
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
@@ -579,7 +651,6 @@ ${rulesContent}
|
|
|
579
651
|
|
|
580
652
|
// ── 3. Gemini / Antigravity (.gemini/settings.json) ──
|
|
581
653
|
const geminiSettings = JSON.stringify({
|
|
582
|
-
"$schema": "https://raw.githubusercontent.com/anthropics/anthropic-cookbook/main/.gemini/settings.schema.json",
|
|
583
654
|
"rules": [
|
|
584
655
|
{ "path": "../.agent/rules/GEMINI.md", "trigger": "always_on" }
|
|
585
656
|
],
|
|
@@ -590,7 +661,8 @@ ${rulesContent}
|
|
|
590
661
|
await writeBridge(
|
|
591
662
|
path.join(targetDir, '.gemini', 'settings.json'),
|
|
592
663
|
geminiSettings,
|
|
593
|
-
'Gemini/Antigravity'
|
|
664
|
+
'Gemini/Antigravity',
|
|
665
|
+
true
|
|
594
666
|
);
|
|
595
667
|
|
|
596
668
|
// ── Also create .gemini/GEMINI.md as a direct rules file ──
|
|
@@ -700,8 +772,9 @@ async function cmdLearn(flags) {
|
|
|
700
772
|
console.log(` ${c('cyan', '\u255a' + '\u2550'.repeat(W) + '\u255d')}`);
|
|
701
773
|
console.log();
|
|
702
774
|
|
|
703
|
-
const
|
|
704
|
-
|
|
775
|
+
const evoArgs = ['digest'];
|
|
776
|
+
if (flags.dryRun) evoArgs.push('--dry-run');
|
|
777
|
+
if (flags.head) evoArgs.push('--head');
|
|
705
778
|
|
|
706
779
|
|
|
707
780
|
// Phase 1: Skill Evolution
|
|
@@ -711,8 +784,7 @@ async function cmdLearn(flags) {
|
|
|
711
784
|
warn('skill_evolution.js not found \u2014 run: npx tribunal-kit update');
|
|
712
785
|
} else {
|
|
713
786
|
try {
|
|
714
|
-
|
|
715
|
-
await runShellAsync(cmd, { stdio: 'inherit', cwd: targetDir });
|
|
787
|
+
await runScriptAsync(evoScript, evoArgs, { cwd: targetDir });
|
|
716
788
|
} catch (e) {
|
|
717
789
|
warn(`Skill Evolution error: ${e.message}`);
|
|
718
790
|
}
|
|
@@ -807,8 +879,8 @@ async function cmdCase(flags) {
|
|
|
807
879
|
process.exit(1);
|
|
808
880
|
}
|
|
809
881
|
|
|
810
|
-
const args = process.argv.slice(3)
|
|
811
|
-
if (
|
|
882
|
+
const args = process.argv.slice(3);
|
|
883
|
+
if (args.length === 0 || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
812
884
|
banner();
|
|
813
885
|
log(` ${c('cyan', '\u2554' + '\u2550'.repeat(60) + '\u2557')}`);
|
|
814
886
|
log(` ${c('cyan', '\u2551')}${c('bold', c('white', ' Tribunal Case Law Engine \u2014 Supreme Court '))}${c('cyan', '\u2551')}`);
|
|
@@ -827,13 +899,13 @@ async function cmdCase(flags) {
|
|
|
827
899
|
|
|
828
900
|
const caseLawScript = path.join(agentDest, 'scripts', 'case_law_manager.js');
|
|
829
901
|
|
|
830
|
-
// Make shorthand aliases
|
|
831
|
-
|
|
832
|
-
if (
|
|
833
|
-
if (
|
|
902
|
+
// Make shorthand aliases for the subcommand (first arg only)
|
|
903
|
+
const caseArgs = [...args];
|
|
904
|
+
if (caseArgs[0] === 'add') caseArgs[0] = 'add-case';
|
|
905
|
+
if (caseArgs[0] === 'search') caseArgs[0] = 'search-cases';
|
|
834
906
|
|
|
835
907
|
try {
|
|
836
|
-
await
|
|
908
|
+
await runScriptAsync(caseLawScript, caseArgs, { cwd: targetDir });
|
|
837
909
|
} catch {
|
|
838
910
|
process.exit(1); // Script already prints errors
|
|
839
911
|
}
|
|
@@ -854,12 +926,17 @@ async function cmdGraph(flags) {
|
|
|
854
926
|
const htmlFile = path.join(agentDest, 'history', 'architecture-explorer.html');
|
|
855
927
|
|
|
856
928
|
try {
|
|
857
|
-
await
|
|
858
|
-
await
|
|
929
|
+
await runScriptAsync(builderScript, [], { cwd: targetDir });
|
|
930
|
+
await runScriptAsync(visualizerScript, [], { cwd: targetDir });
|
|
859
931
|
|
|
860
932
|
log(` ${c('cyan', '▸')} Opening visualizer in browser...`);
|
|
861
|
-
|
|
862
|
-
|
|
933
|
+
// Open browser safely without shell interpolation
|
|
934
|
+
const { opener, openerArgs } = (() => {
|
|
935
|
+
if (process.platform === 'win32') return { opener: 'cmd', openerArgs: ['/c', 'start', '', htmlFile] };
|
|
936
|
+
if (process.platform === 'darwin') return { opener: 'open', openerArgs: [htmlFile] };
|
|
937
|
+
return { opener: 'xdg-open', openerArgs: [htmlFile] };
|
|
938
|
+
})();
|
|
939
|
+
spawn(opener, openerArgs, { stdio: 'ignore', detached: true }).unref();
|
|
863
940
|
} catch (e) {
|
|
864
941
|
err(`Graph generation failed: ${e.message}`);
|
|
865
942
|
process.exit(1);
|
|
@@ -908,7 +985,7 @@ async function cmdMutate(flags) {
|
|
|
908
985
|
|
|
909
986
|
const mutateScript = path.join(agentDest, 'scripts', 'mutation_runner.js');
|
|
910
987
|
try {
|
|
911
|
-
await
|
|
988
|
+
await runScriptAsync(mutateScript, args, { cwd: targetDir });
|
|
912
989
|
} catch {
|
|
913
990
|
process.exit(1);
|
|
914
991
|
}
|
|
@@ -1051,7 +1128,6 @@ async function cmdMarathon(flags) {
|
|
|
1051
1128
|
}
|
|
1052
1129
|
|
|
1053
1130
|
const args = process.argv.slice(3);
|
|
1054
|
-
const argsStr = args.join(' ');
|
|
1055
1131
|
if (args.length === 0 || args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
1056
1132
|
banner();
|
|
1057
1133
|
log(` ${c('cyan', '╔' + '═'.repeat(60) + '╗')}`);
|
|
@@ -1073,7 +1149,7 @@ async function cmdMarathon(flags) {
|
|
|
1073
1149
|
|
|
1074
1150
|
const marathonScript = path.join(agentDest, 'scripts', 'marathon_harness.js');
|
|
1075
1151
|
try {
|
|
1076
|
-
await
|
|
1152
|
+
await runScriptAsync(marathonScript, args, { cwd: targetDir });
|
|
1077
1153
|
} catch {
|
|
1078
1154
|
process.exit(1);
|
|
1079
1155
|
}
|
package/bin/wrapper.js
CHANGED
|
@@ -21,14 +21,23 @@ const RUST_COMMANDS = new Set(['init', 'validate', 'status']);
|
|
|
21
21
|
function getBinaryPath() {
|
|
22
22
|
const isWindows = os.platform() === 'win32';
|
|
23
23
|
const ext = isWindows ? '.exe' : '';
|
|
24
|
+
const platform = os.platform();
|
|
25
|
+
const arch = os.arch();
|
|
24
26
|
|
|
25
|
-
// First,
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// First, try production resolution (from optionalDependencies)
|
|
28
|
+
const pkgName = `@tribunal-kit/core-${platform}-${arch}`;
|
|
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
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
// Second, try to find the binary compiled from crates/core/Cargo.toml
|
|
40
|
+
// Second, try to find the binary compiled from crates/core/Cargo.toml (Local dev)
|
|
32
41
|
const devPath = path.resolve(__dirname, '..', 'target', 'release', `tribunal-core${ext}`);
|
|
33
42
|
if (fs.existsSync(devPath)) {
|
|
34
43
|
return devPath;
|
|
@@ -40,13 +49,13 @@ function getBinaryPath() {
|
|
|
40
49
|
return debugPath;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
// Production resolution (from optionalDependencies) would go here
|
|
44
52
|
return null;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
function runRustBinary(binPath, args) {
|
|
56
|
+
const stdio = ['inherit', process.stdout.isTTY ? 'ignore' : 'inherit', 'inherit'];
|
|
48
57
|
const result = spawnSync(binPath, args, {
|
|
49
|
-
stdio:
|
|
58
|
+
stdio: stdio,
|
|
50
59
|
env: process.env
|
|
51
60
|
});
|
|
52
61
|
|
package/mcp_config.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tribunal-kit",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Anti-Hallucination AI Agent Kit — 40 specialist agents, 32 slash commands, 16 parallel Tribunal reviewers, Performance Swarm engine, Supreme Court case law pipeline, and long-running agent harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"scripts/",
|
|
51
51
|
".agent/",
|
|
52
52
|
"README.md",
|
|
53
|
-
"LICENSE"
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"mcp_config.json"
|
|
54
55
|
],
|
|
55
56
|
"engines": {
|
|
56
57
|
"node": ">=18.0.0"
|
|
@@ -65,13 +66,20 @@
|
|
|
65
66
|
"changelog:preview": "node scripts/changelog.js --preview",
|
|
66
67
|
"sync": "node scripts/sync-version.js",
|
|
67
68
|
"validate-payload": "node scripts/validate-payload.js",
|
|
68
|
-
"postinstall": "node scripts/postinstall.js",
|
|
69
69
|
"build": "echo 'No build step required for this project'"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
72
|
+
"eslint": "^9.1.1",
|
|
73
|
+
"jest": "^25.0.0",
|
|
74
|
+
"typescript": "^5.4.5"
|
|
75
|
+
},
|
|
76
|
+
"optionalDependencies": {
|
|
77
|
+
"@tribunal-kit/core-darwin-arm64": "^4.5.1",
|
|
78
|
+
"@tribunal-kit/core-darwin-x64": "^4.5.1",
|
|
79
|
+
"@tribunal-kit/core-linux-arm64": "^4.5.1",
|
|
80
|
+
"@tribunal-kit/core-linux-x64": "^4.5.1",
|
|
81
|
+
"@tribunal-kit/core-win32-arm64": "^4.5.1",
|
|
82
|
+
"@tribunal-kit/core-win32-x64": "^4.5.1"
|
|
75
83
|
},
|
|
76
84
|
"jest": {
|
|
77
85
|
"testMatch": [
|
package/scripts/changelog.js
CHANGED
|
@@ -56,13 +56,14 @@ function getLatestTag() {
|
|
|
56
56
|
|
|
57
57
|
function getCommits(since) {
|
|
58
58
|
const range = since ? `${since}..HEAD` : 'HEAD';
|
|
59
|
-
const
|
|
60
|
-
const
|
|
59
|
+
const MAX_COMMITS = 500;
|
|
60
|
+
const format = '--format=%H||%s||%an||%ai';
|
|
61
|
+
const raw = git(`log ${range} ${format} --no-merges -n ${MAX_COMMITS}`);
|
|
61
62
|
if (!raw) return [];
|
|
62
63
|
|
|
63
64
|
return raw.split('\n').filter(Boolean).map(line => {
|
|
64
65
|
const [hash, subject, author, date] = line.split('||');
|
|
65
|
-
return { hash: hash?.slice(0, 7), subject, author, date: date?.slice(0, 10) };
|
|
66
|
+
return { hash: hash?.replace(/^"/, '').slice(0, 7), subject, author, date: date?.replace(/"$/, '').slice(0, 10) };
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -3,7 +3,12 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
// Simple Markdown frontmatter and Tribunal header validator
|
|
5
5
|
function validateMarkdownFile(filePath) {
|
|
6
|
-
|
|
6
|
+
let content;
|
|
7
|
+
try {
|
|
8
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
9
|
+
} catch (e) {
|
|
10
|
+
return [`Failed to read file: ${e.message}`];
|
|
11
|
+
}
|
|
7
12
|
let errors = [];
|
|
8
13
|
|
|
9
14
|
// Check for frontmatter
|
package/scripts/postinstall.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* postinstall.js — Binary Download Script
|
|
4
|
-
*
|
|
5
|
-
* After `npm install`, this script downloads the pre-compiled Rust binary
|
|
6
|
-
* for the user's platform from the latest GitHub Release.
|
|
7
|
-
*
|
|
8
|
-
* If the download fails (offline, unsupported platform, etc.), it's a soft failure.
|
|
9
|
-
* The wrapper.js will detect the missing binary and fall back to the JS engine.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const https = require('https');
|
|
15
|
-
const os = require('os');
|
|
16
|
-
const crypto = require('crypto');
|
|
17
|
-
|
|
18
|
-
const PKG = require(path.resolve(__dirname, '..', 'package.json'));
|
|
19
|
-
const VERSION = PKG.version;
|
|
20
|
-
|
|
21
|
-
const BINARY_DIR = path.resolve(__dirname, '..', 'bin');
|
|
22
|
-
const REPO = 'Harmitx7/tribunal-kit';
|
|
23
|
-
|
|
24
|
-
function getPlatformBinary() {
|
|
25
|
-
const platform = os.platform();
|
|
26
|
-
const arch = os.arch();
|
|
27
|
-
|
|
28
|
-
const map = {
|
|
29
|
-
'win32-x64': 'tribunal-core-win-x64.exe',
|
|
30
|
-
'win32-arm64': 'tribunal-core-win-arm64.exe',
|
|
31
|
-
'darwin-x64': 'tribunal-core-darwin-x64',
|
|
32
|
-
'darwin-arm64': 'tribunal-core-darwin-arm64',
|
|
33
|
-
'linux-x64': 'tribunal-core-linux-x64',
|
|
34
|
-
'linux-arm64': 'tribunal-core-linux-arm64',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const key = `${platform}-${arch}`;
|
|
38
|
-
return map[key] || null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getLocalBinaryPath() {
|
|
42
|
-
const isWindows = os.platform() === 'win32';
|
|
43
|
-
return path.join(BINARY_DIR, `tribunal-core${isWindows ? '.exe' : ''}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function download(url) {
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
const request = https.get(url, { headers: { 'User-Agent': `tribunal-kit/${VERSION}` } }, (res) => {
|
|
49
|
-
// Handle redirects (GitHub sends 302 to the actual download URL)
|
|
50
|
-
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
51
|
-
download(res.headers.location).then(resolve).catch(reject);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (res.statusCode !== 200) {
|
|
56
|
-
reject(new Error(`HTTP ${res.statusCode}`));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const chunks = [];
|
|
61
|
-
res.on('data', (chunk) => chunks.push(chunk));
|
|
62
|
-
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
63
|
-
res.on('error', reject);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
request.on('error', reject);
|
|
67
|
-
request.setTimeout(30000, () => { request.destroy(); reject(new Error('Timeout')); });
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function main() {
|
|
72
|
-
const binaryName = getPlatformBinary();
|
|
73
|
-
|
|
74
|
-
if (!binaryName) {
|
|
75
|
-
console.log(`[tribunal-kit] No pre-built binary for ${os.platform()}-${os.arch()}. Using JS fallback.`);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const localPath = getLocalBinaryPath();
|
|
80
|
-
|
|
81
|
-
// Skip if binary already exists (e.g. dev environment with cargo build)
|
|
82
|
-
if (fs.existsSync(localPath)) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${binaryName}`;
|
|
87
|
-
const checksumsUrl = `https://github.com/${REPO}/releases/download/v${VERSION}/checksums.txt`;
|
|
88
|
-
|
|
89
|
-
console.log(`[tribunal-kit] Downloading native binary for ${os.platform()}-${os.arch()}...`);
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
// 1. Download checksums file
|
|
93
|
-
const checksumsData = await download(checksumsUrl);
|
|
94
|
-
const checksumsText = checksumsData.toString('utf-8');
|
|
95
|
-
|
|
96
|
-
// 2. Extract expected hash
|
|
97
|
-
const hashMatch = checksumsText.split('\n').find(line => line.includes(binaryName));
|
|
98
|
-
if (!hashMatch) {
|
|
99
|
-
throw new Error(`Checksum for ${binaryName} not found in checksums.txt`);
|
|
100
|
-
}
|
|
101
|
-
const expectedHash = hashMatch.split(' ')[0].trim();
|
|
102
|
-
|
|
103
|
-
// 3. Download binary
|
|
104
|
-
const data = await download(url);
|
|
105
|
-
|
|
106
|
-
// 4. Verify checksum
|
|
107
|
-
const actualHash = crypto.createHash('sha256').update(data).digest('hex');
|
|
108
|
-
if (actualHash !== expectedHash) {
|
|
109
|
-
throw new Error(`Checksum mismatch! Expected ${expectedHash}, got ${actualHash}. Potential tamper detected.`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 5. Write to disk
|
|
113
|
-
fs.writeFileSync(localPath, data);
|
|
114
|
-
|
|
115
|
-
// Make executable on Unix
|
|
116
|
-
if (os.platform() !== 'win32') {
|
|
117
|
-
fs.chmodSync(localPath, 0o755);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(`[tribunal-kit] Native binary installed and verified successfully.`);
|
|
121
|
-
} catch (e) {
|
|
122
|
-
// Soft failure — wrapper.js will use the JS engine
|
|
123
|
-
console.log(`[tribunal-kit] Binary download skipped (${e.message}). Using JS fallback.`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
main();
|