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/mcp-server.js
CHANGED
|
@@ -2,32 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Tribunal-Kit MCP Server (Performance-Optimized)
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
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
|
|
7
|
+
* over standard I/O, allowing AI clients (Cursor, Windsurf, Claude) to natively
|
|
8
8
|
* invoke tribunal checks.
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* PERF: Commands are loaded in-process via require() — no child process spawn.
|
|
11
11
|
* This eliminates ~200-500ms overhead per tool call that spawnSync introduced.
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
13
|
* Protocol: MCP 2024-11-05 over JSON-RPC 2.0 / stdio
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
const path = require(
|
|
17
|
-
const { spawnSync } = require(
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const { spawnSync } = require("child_process");
|
|
18
18
|
|
|
19
|
-
const PKG = require(path.resolve(__dirname,
|
|
19
|
+
const PKG = require(path.resolve(__dirname, "../package.json"));
|
|
20
20
|
|
|
21
21
|
// Timeout for spawned processes (30 seconds) — only used for Rust binary calls
|
|
22
22
|
const SPAWN_TIMEOUT_MS = 30000;
|
|
23
23
|
|
|
24
24
|
// Minimal JSON-RPC 2.0 over stdio
|
|
25
|
-
const readline = require(
|
|
25
|
+
const readline = require("readline");
|
|
26
26
|
|
|
27
27
|
const rl = readline.createInterface({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout,
|
|
30
|
+
terminal: false,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -35,195 +35,518 @@ const rl = readline.createInterface({
|
|
|
35
35
|
* This is the only command that still benefits from process spawn (Rust speed).
|
|
36
36
|
*/
|
|
37
37
|
function runValidateCommand() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Try Rust binary first
|
|
46
|
-
const pkgName = `@tribunal-kit/core-${platform}-${arch}`;
|
|
47
|
-
let binPath = null;
|
|
48
|
-
try {
|
|
49
|
-
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
50
|
-
const candidatePath = path.resolve(path.dirname(pkgPath), `bin/tribunal-core${ext}`);
|
|
51
|
-
if (fs.existsSync(candidatePath)) binPath = candidatePath;
|
|
52
|
-
} catch (_) {}
|
|
53
|
-
if (!binPath) {
|
|
54
|
-
const devPath = path.resolve(__dirname, '..', 'target', 'release', `tribunal-core${ext}`);
|
|
55
|
-
if (fs.existsSync(devPath)) binPath = devPath;
|
|
56
|
-
}
|
|
38
|
+
const os = require("os");
|
|
39
|
+
const fs = require("fs");
|
|
40
|
+
const isWindows = os.platform() === "win32";
|
|
41
|
+
const ext = isWindows ? ".exe" : "";
|
|
42
|
+
const platform = os.platform();
|
|
43
|
+
const arch = os.arch();
|
|
57
44
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
// Try Rust binary first
|
|
46
|
+
const pkgName = `@tribunal-kit/core-${platform}-${arch}`;
|
|
47
|
+
let binPath = null;
|
|
48
|
+
try {
|
|
49
|
+
const pkgPath = require.resolve(`${pkgName}/package.json`);
|
|
50
|
+
const candidatePath = path.resolve(
|
|
51
|
+
path.dirname(pkgPath),
|
|
52
|
+
`bin/tribunal-core${ext}`,
|
|
53
|
+
);
|
|
54
|
+
if (fs.existsSync(candidatePath)) binPath = candidatePath;
|
|
55
|
+
} catch (_) {}
|
|
56
|
+
if (!binPath) {
|
|
57
|
+
const devPath = path.resolve(
|
|
58
|
+
__dirname,
|
|
59
|
+
"..",
|
|
60
|
+
"target",
|
|
61
|
+
"release",
|
|
62
|
+
`tribunal-core${ext}`,
|
|
63
|
+
);
|
|
64
|
+
if (fs.existsSync(devPath)) binPath = devPath;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (binPath) {
|
|
68
|
+
const result = spawnSync(binPath, ["validate"], {
|
|
69
|
+
encoding: "utf8",
|
|
70
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
71
|
+
});
|
|
72
|
+
return result.stdout || result.stderr || "No output";
|
|
73
|
+
}
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
// JS fallback — in-process
|
|
76
|
+
return "Validate command requires the Rust binary. Run: cargo build --release";
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
/**
|
|
71
80
|
* Search case law — loaded in-process for zero-spawn latency.
|
|
72
81
|
*/
|
|
73
82
|
function searchCaseLaw(query) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const caseLawScript = path.resolve(
|
|
84
|
+
__dirname,
|
|
85
|
+
"../.agent/scripts/case_law_manager.js",
|
|
86
|
+
);
|
|
87
|
+
// We still spawn for case_law_manager since it's a standalone script
|
|
88
|
+
// that modifies global state, but we use spawn with minimal overhead
|
|
89
|
+
const result = spawnSync(
|
|
90
|
+
process.execPath,
|
|
91
|
+
[caseLawScript, "search-cases", "--query", query],
|
|
92
|
+
{
|
|
93
|
+
encoding: "utf8",
|
|
94
|
+
timeout: SPAWN_TIMEOUT_MS,
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
return result.stdout || result.stderr || "No results";
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
let output = '';
|
|
93
|
-
console.log = (...args) => { output += args.join(' ') + '\n'; };
|
|
94
|
-
await cmdSync();
|
|
95
|
-
console.log = originalLog;
|
|
96
|
-
return output || "Sync complete";
|
|
97
|
-
} catch (e) {
|
|
98
|
-
return `Sync failed: ${e.message}`;
|
|
99
|
-
}
|
|
100
|
+
|
|
101
|
+
function stripBoilerplate(text) {
|
|
102
|
+
if (!text) return text;
|
|
103
|
+
let minified = text.replace(/AI coding assistants often fall into specific bad habits[\s\S]*$/g, "");
|
|
104
|
+
minified = minified.replace(/## 🤖 LLM-Specific Traps[\s\S]*$/g, "");
|
|
105
|
+
minified = minified.replace(/## 🏛️ Tribunal Integration[\s\S]*$/g, "");
|
|
106
|
+
minified = minified.replace(/## Pre-Flight Checklist[\s\S]*$/g, "");
|
|
107
|
+
return minified.trim();
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
function handleRequest(req) {
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
// MCP spec: method names follow path-style convention
|
|
112
|
+
if (req.method === "initialize") {
|
|
113
|
+
return {
|
|
114
|
+
protocolVersion: "2024-11-05",
|
|
115
|
+
capabilities: {
|
|
116
|
+
tools: {},
|
|
117
|
+
},
|
|
118
|
+
serverInfo: {
|
|
119
|
+
name: "tribunal-kit-mcp",
|
|
120
|
+
version: PKG.version,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (req.method === "tools/list") {
|
|
126
|
+
return {
|
|
127
|
+
tools: [
|
|
128
|
+
{
|
|
129
|
+
name: "run_tribunal_audit",
|
|
130
|
+
description:
|
|
131
|
+
"Runs a full anti-hallucination audit across the workspace.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {},
|
|
135
|
+
additionalProperties: false,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "sync_ide_bridges",
|
|
140
|
+
description:
|
|
141
|
+
"Synchronize IDE bridge files with the current GEMINI.md rules.",
|
|
142
|
+
inputSchema: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {},
|
|
145
|
+
additionalProperties: false,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "search_case_law",
|
|
150
|
+
description:
|
|
151
|
+
"Search historical code rejections and legal precedent. Use this before writing code to avoid past mistakes.",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
query: {
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "Search query (e.g. 'useEffect state')",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
required: ["query"],
|
|
161
|
+
additionalProperties: false,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "list_tribunal_agents",
|
|
166
|
+
description: "List all available Tribunal Kit agents.",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {},
|
|
170
|
+
additionalProperties: false,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "get_tribunal_agent",
|
|
175
|
+
description: "Get the full markdown rules for a specific Tribunal agent.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: {
|
|
179
|
+
name: { type: "string", description: "The agent name (e.g. 'frontend-specialist')" },
|
|
180
|
+
},
|
|
181
|
+
required: ["name"],
|
|
182
|
+
additionalProperties: false,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "list_tribunal_skills",
|
|
187
|
+
description: "List all available Tribunal Kit skills.",
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: {},
|
|
191
|
+
additionalProperties: false,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "get_tribunal_skill",
|
|
196
|
+
description: "Get the full markdown instructions for a specific Tribunal skill.",
|
|
197
|
+
inputSchema: {
|
|
198
|
+
type: "object",
|
|
199
|
+
properties: {
|
|
200
|
+
name: { type: "string", description: "The skill name (e.g. 'react-specialist')" },
|
|
201
|
+
},
|
|
202
|
+
required: ["name"],
|
|
203
|
+
additionalProperties: false,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "recall_memory",
|
|
208
|
+
description: "Budget-constrained memory recall from the 4-Type Taxonomy Persistent Memory Engine. Returns the most relevant memories that fit within the token budget, ranked by relevance × recency × priority. Use this BEFORE writing code to recall project guidelines without bloating context.",
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
query: {
|
|
213
|
+
type: "string",
|
|
214
|
+
description: "Search query (e.g. 'database', 'auth', 'deploy')",
|
|
215
|
+
},
|
|
216
|
+
budget: {
|
|
217
|
+
type: "number",
|
|
218
|
+
description: "Maximum token budget for recall (default: 2000). Only the top-ranked memories that fit within this budget are returned.",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
required: ["query"],
|
|
222
|
+
additionalProperties: false,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "store_memory",
|
|
227
|
+
description: "Store a new memory entry in the 4-Type Taxonomy Persistent Memory Engine. Memories are schema-validated and persisted across sessions. Types: semantic (permanent facts), procedural (how-to recipes), episodic (30-day TTL events), working (session scratch).",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
properties: {
|
|
231
|
+
type: {
|
|
232
|
+
type: "string",
|
|
233
|
+
enum: ["semantic", "procedural", "episodic", "working"],
|
|
234
|
+
description: "Memory type: semantic (facts), procedural (recipes), episodic (events), working (scratch)",
|
|
235
|
+
},
|
|
236
|
+
content: {
|
|
237
|
+
type: "string",
|
|
238
|
+
description: "The memory content to store",
|
|
239
|
+
},
|
|
240
|
+
tags: {
|
|
241
|
+
type: "array",
|
|
242
|
+
items: { type: "string" },
|
|
243
|
+
description: "Searchable tags for this memory",
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ["type", "content"],
|
|
247
|
+
additionalProperties: false,
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "get_sparse_context",
|
|
252
|
+
description: "Get a JIT, token-optimized context prompt tailored to the active task and files. Uses the Context Density Broker to score and select relevant skills, stripping duplicate boilerplate and saving up to 85% in prompt tokens.",
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: "object",
|
|
255
|
+
properties: {
|
|
256
|
+
task: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "The user task description (e.g. 'JWT auth API')",
|
|
259
|
+
},
|
|
260
|
+
files: {
|
|
261
|
+
type: "array",
|
|
262
|
+
items: { type: "string" },
|
|
263
|
+
description: "List of files being touched (e.g. ['src/auth.js'])",
|
|
264
|
+
},
|
|
265
|
+
model: {
|
|
266
|
+
type: "string",
|
|
267
|
+
enum: ["large", "small"],
|
|
268
|
+
description: "Model tier: large (default, includes key rules of supplementary skills) or small (essential skills only)",
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
required: ["task"],
|
|
272
|
+
additionalProperties: false,
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "align_output",
|
|
277
|
+
description: "Align model outputs to Fabel-5 constraints: strips conversational introductions and conclusions, collapses single/double bullet items to prose, and checks for code traps (unawaited dynamic functions in Next.js 15, deprecated hooks in React 19, or non-existent models).",
|
|
278
|
+
inputSchema: {
|
|
279
|
+
type: "object",
|
|
280
|
+
properties: {
|
|
281
|
+
text: {
|
|
282
|
+
type: "string",
|
|
283
|
+
description: "The raw output text generated by the model to be aligned.",
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
required: ["text"],
|
|
287
|
+
additionalProperties: false,
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (req.method === "tools/call") {
|
|
295
|
+
const toolName = req.params && req.params.name;
|
|
296
|
+
if (!toolName) {
|
|
297
|
+
throw {
|
|
298
|
+
code: -32602,
|
|
299
|
+
message: "Missing required parameter: params.name",
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (toolName === "run_tribunal_audit") {
|
|
304
|
+
const text = runValidateCommand();
|
|
305
|
+
return { content: [{ type: "text", text }] };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (toolName === "sync_ide_bridges") {
|
|
309
|
+
const fs = require("fs");
|
|
310
|
+
const cwd = process.cwd();
|
|
311
|
+
const agentDest = path.join(cwd, ".agent");
|
|
312
|
+
if (!fs.existsSync(agentDest)) {
|
|
105
313
|
return {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
314
|
+
content: [
|
|
315
|
+
{
|
|
316
|
+
type: "text",
|
|
317
|
+
text: "Error: .agent/ directory not found. Run `tk init` first.",
|
|
109
318
|
},
|
|
110
|
-
|
|
111
|
-
name: "tribunal-kit-mcp",
|
|
112
|
-
version: PKG.version
|
|
113
|
-
}
|
|
319
|
+
],
|
|
114
320
|
};
|
|
321
|
+
}
|
|
322
|
+
// Run synchronously by spawning a minimal script
|
|
323
|
+
const result = spawnSync(
|
|
324
|
+
process.execPath,
|
|
325
|
+
[
|
|
326
|
+
"-e",
|
|
327
|
+
`
|
|
328
|
+
const { generateIDEBridges } = require('${path.resolve(__dirname, "../dist/commands/init.js").replace(/\\/g, "\\\\")}');
|
|
329
|
+
generateIDEBridges('${cwd.replace(/\\/g, "\\\\")}', '${agentDest.replace(/\\/g, "\\\\")}', false).then(() => console.log('Sync complete'));
|
|
330
|
+
`,
|
|
331
|
+
],
|
|
332
|
+
{ encoding: "utf8", timeout: SPAWN_TIMEOUT_MS },
|
|
333
|
+
);
|
|
334
|
+
return {
|
|
335
|
+
content: [
|
|
336
|
+
{
|
|
337
|
+
type: "text",
|
|
338
|
+
text: result.stdout || result.stderr || "Sync complete",
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
};
|
|
115
342
|
}
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: "sync_ide_bridges",
|
|
127
|
-
description: "Synchronize IDE bridge files with the current GEMINI.md rules.",
|
|
128
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: "search_case_law",
|
|
132
|
-
description: "Search historical code rejections and legal precedent. Use this before writing code to avoid past mistakes.",
|
|
133
|
-
inputSchema: {
|
|
134
|
-
type: "object",
|
|
135
|
-
properties: {
|
|
136
|
-
query: { type: "string", description: "Search query (e.g. 'useEffect state')" }
|
|
137
|
-
},
|
|
138
|
-
required: ["query"],
|
|
139
|
-
additionalProperties: false
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
]
|
|
343
|
+
|
|
344
|
+
if (toolName === "search_case_law") {
|
|
345
|
+
const query =
|
|
346
|
+
req.params && req.params.arguments && req.params.arguments.query;
|
|
347
|
+
if (!query || typeof query !== "string") {
|
|
348
|
+
throw {
|
|
349
|
+
code: -32602,
|
|
350
|
+
message: "Missing or invalid required argument: query (string)",
|
|
143
351
|
};
|
|
352
|
+
}
|
|
353
|
+
const text = searchCaseLaw(query);
|
|
354
|
+
return { content: [{ type: "text", text }] };
|
|
144
355
|
}
|
|
145
356
|
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
357
|
+
if (toolName === "list_tribunal_agents") {
|
|
358
|
+
const fs = require("fs");
|
|
359
|
+
const agentDir = path.join(process.cwd(), ".agent", "agents");
|
|
360
|
+
if (!fs.existsSync(agentDir)) return { content: [{ type: "text", text: "No agents found or .agent directory missing." }] };
|
|
361
|
+
const agents = fs.readdirSync(agentDir).filter(f => f.endsWith('.md')).map(f => f.replace('.md', ''));
|
|
362
|
+
return { content: [{ type: "text", text: "Available Agents:\n- " + agents.join("\n- ") }] };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (toolName === "get_tribunal_agent") {
|
|
366
|
+
const fs = require("fs");
|
|
367
|
+
const name = req.params?.arguments?.name;
|
|
368
|
+
if (!name) throw { code: -32602, message: "Missing argument: name" };
|
|
369
|
+
const path = require("path");
|
|
370
|
+
const sanitizedName = path.basename(name);
|
|
371
|
+
const agentPath = path.join(process.cwd(), ".agent", "agents", `${sanitizedName}.md`);
|
|
372
|
+
if (!fs.existsSync(agentPath)) return { content: [{ type: "text", text: `Agent '${sanitizedName}' not found.` }] };
|
|
373
|
+
const text = fs.readFileSync(agentPath, "utf8");
|
|
374
|
+
return { content: [{ type: "text", text: stripBoilerplate(text) }] };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (toolName === "list_tribunal_skills") {
|
|
378
|
+
const fs = require("fs");
|
|
379
|
+
const skillsDir = path.join(process.cwd(), ".agent", "skills");
|
|
380
|
+
if (!fs.existsSync(skillsDir)) return { content: [{ type: "text", text: "No skills found or .agent directory missing." }] };
|
|
381
|
+
const skills = fs.readdirSync(skillsDir, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name);
|
|
382
|
+
return { content: [{ type: "text", text: "Available Skills:\n- " + skills.join("\n- ") }] };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (toolName === "get_tribunal_skill") {
|
|
386
|
+
const fs = require("fs");
|
|
387
|
+
const name = req.params?.arguments?.name;
|
|
388
|
+
if (!name) throw { code: -32602, message: "Missing argument: name" };
|
|
389
|
+
const path = require("path");
|
|
390
|
+
const sanitizedName = path.basename(name);
|
|
391
|
+
const skillPath = path.join(process.cwd(), ".agent", "skills", sanitizedName, "SKILL.md");
|
|
392
|
+
if (!fs.existsSync(skillPath)) return { content: [{ type: "text", text: `Skill '${sanitizedName}' not found.` }] };
|
|
393
|
+
const text = fs.readFileSync(skillPath, "utf8");
|
|
394
|
+
return { content: [{ type: "text", text: stripBoilerplate(text) }] };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (toolName === "get_sparse_context") {
|
|
398
|
+
const task = req.params?.arguments?.task;
|
|
399
|
+
const files = req.params?.arguments?.files || [];
|
|
400
|
+
const model = req.params?.arguments?.model || "large";
|
|
401
|
+
|
|
402
|
+
if (!task) throw { code: -32602, message: "Missing required argument: task" };
|
|
403
|
+
|
|
404
|
+
const agentDest = path.join(process.cwd(), ".agent");
|
|
405
|
+
const fs = require("fs");
|
|
406
|
+
if (!fs.existsSync(agentDest)) {
|
|
407
|
+
return { content: [{ type: "text", text: "Error: .agent/ directory not found. Run `tk init` first." }] };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
const { broker } = require("../.agent/scripts/context_broker.js");
|
|
412
|
+
const brokerResult = broker(task, files, model, agentDest);
|
|
413
|
+
return { content: [{ type: "text", text: stripBoilerplate(brokerResult.promptText) }] };
|
|
414
|
+
} catch (e) {
|
|
415
|
+
return { content: [{ type: "text", text: `Failed to retrieve sparse context: ${e.message}` }] };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (toolName === "recall_memory") {
|
|
420
|
+
const query = req.params?.arguments?.query;
|
|
421
|
+
if (!query || typeof query !== "string") {
|
|
422
|
+
throw { code: -32602, message: "Missing or invalid required argument: query (string)" };
|
|
423
|
+
}
|
|
424
|
+
const budget = req.params?.arguments?.budget || 2000;
|
|
425
|
+
const agentDest = path.join(process.cwd(), ".agent");
|
|
426
|
+
const fs = require("fs");
|
|
427
|
+
if (!fs.existsSync(agentDest)) {
|
|
428
|
+
return { content: [{ type: "text", text: "Error: .agent/ directory not found. Run `tk init` first." }] };
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
const { _memoryRecall } = require("../dist/commands/memory.js");
|
|
432
|
+
const { results, tokens_used } = _memoryRecall(agentDest, query, budget);
|
|
433
|
+
if (results.length === 0) {
|
|
434
|
+
return { content: [{ type: "text", text: `No memories match query: "${query}"` }] };
|
|
155
435
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const fs = require('fs');
|
|
162
|
-
const cwd = process.cwd();
|
|
163
|
-
const agentDest = path.join(cwd, '.agent');
|
|
164
|
-
if (!fs.existsSync(agentDest)) {
|
|
165
|
-
return { content: [{ type: "text", text: "Error: .agent/ directory not found. Run `tk init` first." }] };
|
|
166
|
-
}
|
|
167
|
-
// Direct in-process IDE bridge generation
|
|
168
|
-
const { generateIDEBridges } = require('../dist/commands/init.js');
|
|
169
|
-
// Run synchronously by spawning a minimal script
|
|
170
|
-
const result = spawnSync(process.execPath, ['-e', `
|
|
171
|
-
const { generateIDEBridges } = require('${path.resolve(__dirname, '../dist/commands/init.js').replace(/\\/g, '\\\\')}');
|
|
172
|
-
generateIDEBridges('${cwd.replace(/\\/g, '\\\\')}', '${agentDest.replace(/\\/g, '\\\\')}', false).then(() => console.log('Sync complete'));
|
|
173
|
-
`], { encoding: 'utf8', timeout: SPAWN_TIMEOUT_MS });
|
|
174
|
-
return { content: [{ type: "text", text: result.stdout || result.stderr || "Sync complete" }] };
|
|
436
|
+
let text = `## Memory Recall (${results.length} results, ~${tokens_used}/${budget} tokens)\n\n`;
|
|
437
|
+
for (const entry of results) {
|
|
438
|
+
text += `- **[${entry.memory_type.toUpperCase()}]** #${entry.id}: ${entry.content}`;
|
|
439
|
+
if (entry.tags.length > 0) text += ` _(${entry.tags.join(", ")})_`;
|
|
440
|
+
text += `\n`;
|
|
175
441
|
}
|
|
442
|
+
return { content: [{ type: "text", text }] };
|
|
443
|
+
} catch (e) {
|
|
444
|
+
return { content: [{ type: "text", text: `Memory recall failed: ${e.message}` }] };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (toolName === "store_memory") {
|
|
449
|
+
const memType = req.params?.arguments?.type;
|
|
450
|
+
const content = req.params?.arguments?.content;
|
|
451
|
+
const tags = req.params?.arguments?.tags || [];
|
|
452
|
+
if (!memType || !content) {
|
|
453
|
+
throw { code: -32602, message: "Missing required arguments: type (string), content (string)" };
|
|
454
|
+
}
|
|
455
|
+
const validTypes = ["semantic", "procedural", "episodic", "working"];
|
|
456
|
+
if (!validTypes.includes(memType)) {
|
|
457
|
+
throw { code: -32602, message: `Invalid memory type: "${memType}". Must be one of: ${validTypes.join(", ")}` };
|
|
458
|
+
}
|
|
459
|
+
const agentDest = path.join(process.cwd(), ".agent");
|
|
460
|
+
const fs = require("fs");
|
|
461
|
+
if (!fs.existsSync(agentDest)) {
|
|
462
|
+
return { content: [{ type: "text", text: "Error: .agent/ directory not found. Run `tk init` first." }] };
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
const { _memoryStore } = require("../dist/commands/memory.js");
|
|
466
|
+
const result = _memoryStore(agentDest, memType, content, tags, null);
|
|
467
|
+
return { content: [{ type: "text", text: `Memory stored: #${result.id} (${memType}, ~${result.token_estimate} tokens)` }] };
|
|
468
|
+
} catch (e) {
|
|
469
|
+
return { content: [{ type: "text", text: `Memory store failed: ${e.message}` }] };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (toolName === "align_output") {
|
|
474
|
+
const text = req.params?.arguments?.text;
|
|
475
|
+
if (typeof text !== "string") {
|
|
476
|
+
throw { code: -32602, message: "Missing or invalid required argument: text (string)" };
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
const { alignText, validateCodeContent } = require("../dist/commands/align.js");
|
|
480
|
+
const aligned = alignText(text);
|
|
481
|
+
const warnings = validateCodeContent(aligned);
|
|
176
482
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return { content: [{ type: "text", text }] };
|
|
483
|
+
let outputText = aligned;
|
|
484
|
+
if (warnings.length > 0) {
|
|
485
|
+
outputText += "\n\n⚠️ OCAE Alignment Validator Warnings:\n";
|
|
486
|
+
for (const warnMsg of warnings) {
|
|
487
|
+
outputText += `● ${warnMsg}\n`;
|
|
488
|
+
}
|
|
184
489
|
}
|
|
185
|
-
|
|
186
|
-
|
|
490
|
+
return { content: [{ type: "text", text: outputText }] };
|
|
491
|
+
} catch (e) {
|
|
492
|
+
return { content: [{ type: "text", text: `Alignment failed: ${e.message}` }] };
|
|
493
|
+
}
|
|
187
494
|
}
|
|
188
495
|
|
|
189
|
-
throw { code: -32601, message: `Unknown
|
|
496
|
+
throw { code: -32601, message: `Unknown tool: ${toolName}` };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
throw { code: -32601, message: `Unknown method: ${req.method}` };
|
|
190
500
|
}
|
|
191
501
|
|
|
192
|
-
rl.on(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
jsonrpc: "2.0",
|
|
224
|
-
id: req.id || null,
|
|
225
|
-
error: { code, message }
|
|
226
|
-
};
|
|
227
|
-
console.log(JSON.stringify(errorRes));
|
|
502
|
+
rl.on("line", (line) => {
|
|
503
|
+
if (!line.trim()) return;
|
|
504
|
+
|
|
505
|
+
let req;
|
|
506
|
+
try {
|
|
507
|
+
req = JSON.parse(line);
|
|
508
|
+
} catch (parseErr) {
|
|
509
|
+
// Invalid JSON — send a parse error
|
|
510
|
+
const errorRes = {
|
|
511
|
+
jsonrpc: "2.0",
|
|
512
|
+
id: null,
|
|
513
|
+
error: { code: -32700, message: "Parse error: " + parseErr.message },
|
|
514
|
+
};
|
|
515
|
+
console.log(JSON.stringify(errorRes));
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
try {
|
|
520
|
+
const result = handleRequest(req);
|
|
521
|
+
const res = { jsonrpc: "2.0", id: req.id, result };
|
|
522
|
+
console.log(JSON.stringify(res));
|
|
523
|
+
|
|
524
|
+
// After initialize, send the initialized notification per MCP spec
|
|
525
|
+
if (req.method === "initialize") {
|
|
526
|
+
console.log(
|
|
527
|
+
JSON.stringify({
|
|
528
|
+
jsonrpc: "2.0",
|
|
529
|
+
method: "notifications/initialized",
|
|
530
|
+
params: {},
|
|
531
|
+
}),
|
|
532
|
+
);
|
|
228
533
|
}
|
|
534
|
+
} catch (e) {
|
|
535
|
+
// Send proper JSON-RPC error response
|
|
536
|
+
const code = e && typeof e.code === "number" ? e.code : -32603;
|
|
537
|
+
const message = e && e.message ? e.message : "Internal server error";
|
|
538
|
+
const errorRes = {
|
|
539
|
+
jsonrpc: "2.0",
|
|
540
|
+
id: req.id || null,
|
|
541
|
+
error: { code, message },
|
|
542
|
+
};
|
|
543
|
+
console.log(JSON.stringify(errorRes));
|
|
544
|
+
}
|
|
229
545
|
});
|
|
546
|
+
|
|
547
|
+
if (process.env.NODE_ENV === "test") {
|
|
548
|
+
module.exports = {
|
|
549
|
+
handleRequest,
|
|
550
|
+
stripBoilerplate,
|
|
551
|
+
};
|
|
552
|
+
}
|