@jigyasudham/veto 1.2.19 → 1.4.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 +303 -251
- package/dist/cli.js +35 -9
- package/dist/cli.js.map +1 -1
- package/dist/context/reader.d.ts.map +1 -1
- package/dist/context/reader.js +4 -1
- package/dist/context/reader.js.map +1 -1
- package/dist/council/decision-engine.d.ts.map +1 -1
- package/dist/council/decision-engine.js +21 -1
- package/dist/council/decision-engine.js.map +1 -1
- package/dist/council/decision-extractor.d.ts +13 -0
- package/dist/council/decision-extractor.d.ts.map +1 -0
- package/dist/council/decision-extractor.js +66 -0
- package/dist/council/decision-extractor.js.map +1 -0
- package/dist/council/devil-advocate.d.ts.map +1 -1
- package/dist/council/devil-advocate.js +12 -1
- package/dist/council/devil-advocate.js.map +1 -1
- package/dist/council/index.d.ts.map +1 -1
- package/dist/council/index.js +26 -5
- package/dist/council/index.js.map +1 -1
- package/dist/council/lead-developer.d.ts.map +1 -1
- package/dist/council/lead-developer.js +34 -14
- package/dist/council/lead-developer.js.map +1 -1
- package/dist/council/legal-compliance.d.ts.map +1 -1
- package/dist/council/legal-compliance.js +44 -24
- package/dist/council/legal-compliance.js.map +1 -1
- package/dist/council/llm-council.d.ts +4 -0
- package/dist/council/llm-council.d.ts.map +1 -0
- package/dist/council/llm-council.js +160 -0
- package/dist/council/llm-council.js.map +1 -0
- package/dist/council/product-manager.d.ts.map +1 -1
- package/dist/council/product-manager.js +33 -13
- package/dist/council/product-manager.js.map +1 -1
- package/dist/council/security.d.ts.map +1 -1
- package/dist/council/security.js +44 -24
- package/dist/council/security.js.map +1 -1
- package/dist/council/system-architect.d.ts.map +1 -1
- package/dist/council/system-architect.js +33 -13
- package/dist/council/system-architect.js.map +1 -1
- package/dist/council/types.d.ts +2 -0
- package/dist/council/types.d.ts.map +1 -1
- package/dist/council/ux-designer.d.ts.map +1 -1
- package/dist/council/ux-designer.js +47 -26
- package/dist/council/ux-designer.js.map +1 -1
- package/dist/memory/local.d.ts +40 -1
- package/dist/memory/local.d.ts.map +1 -1
- package/dist/memory/local.js +95 -4
- package/dist/memory/local.js.map +1 -1
- package/dist/router/complexity-scorer.js +2 -2
- package/dist/router/complexity-scorer.js.map +1 -1
- package/dist/server.js +232 -793
- package/dist/server.js.map +1 -1
- package/dist/skills/memory/skill-session-restore.d.ts.map +1 -1
- package/dist/skills/memory/skill-session-restore.js +19 -24
- package/dist/skills/memory/skill-session-restore.js.map +1 -1
- package/dist/skills/memory/skill-session-save.js +4 -4
- package/dist/skills/memory/skill-session-save.js.map +1 -1
- package/dist/tools/definitions.d.ts +1073 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +797 -0
- package/dist/tools/definitions.js.map +1 -0
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Veto MCP Server —
|
|
2
|
+
// Veto MCP Server — 49 tools, 23 phases complete, LLM council + auto-learning router
|
|
3
3
|
// Suppress node:sqlite experimental warning — it would corrupt the MCP stdio protocol
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
7
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
8
8
|
import { buildContextString } from './context/reader.js';
|
|
9
|
-
import {
|
|
9
|
+
import { TOOL_DEFINITIONS } from './tools/definitions.js';
|
|
10
|
+
import { saveSession, restoreSession, listSessions, closeSession, getDbPath, saveCouncilOutcome, storeKnowledge, searchKnowledge, deleteKnowledge, updateProjectMap, getProjectMap, upsertPattern, getPatterns, getContextStatus, fetchAndCacheDocs, saveTaskPlan, getTaskPlan, getUsageStatus, getAuditLog, getHealthStats, CONTEXT_WINDOWS, logUsage, getUsageLogs, getDb, storeScanDiagnostics, clearScanDiagnostics, updateSession, resolveContextWindow, getMetrics, } from './memory/local.js';
|
|
10
11
|
import { exportMemory, importMemory } from './memory/sync.js';
|
|
11
|
-
import {
|
|
12
|
+
import { runLlmDebate } from './council/llm-council.js';
|
|
12
13
|
import { routeTask, getRateStatus, trackTokens, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent } from './router/index.js';
|
|
13
14
|
import { getConfig, setConfig } from './memory/config.js';
|
|
14
15
|
import { executeParallel, executeOne } from './agents/executor.js';
|
|
@@ -19,7 +20,7 @@ import { loadPlugins, listPlugins } from './plugins/loader.js';
|
|
|
19
20
|
import { fetchPrDiff } from './github/pr-fetcher.js';
|
|
20
21
|
import { discoverProject } from './discover.js';
|
|
21
22
|
import { readFileSync, statSync } from 'node:fs';
|
|
22
|
-
import { extname, basename, join, dirname } from 'node:path';
|
|
23
|
+
import { extname, basename, join, dirname, resolve } from 'node:path';
|
|
23
24
|
import { createHash } from 'node:crypto';
|
|
24
25
|
import { fileURLToPath } from 'node:url';
|
|
25
26
|
import { execSync as execSyncTop } from 'node:child_process';
|
|
@@ -40,10 +41,10 @@ const autoSave = {
|
|
|
40
41
|
last_session_id: null,
|
|
41
42
|
cached: null,
|
|
42
43
|
};
|
|
43
|
-
function maybeAutoSave(token_count, platform) {
|
|
44
|
+
function maybeAutoSave(token_count, platform, model) {
|
|
44
45
|
if (!autoSave.cached)
|
|
45
46
|
return { triggered: false };
|
|
46
|
-
const window_size =
|
|
47
|
+
const window_size = autoSave.cached.context_window ?? resolveContextWindow(platform, model);
|
|
47
48
|
const usage_pct = Math.round((token_count / window_size) * 100);
|
|
48
49
|
if (usage_pct < autoSave.threshold_pct)
|
|
49
50
|
return { triggered: false };
|
|
@@ -120,735 +121,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
120
121
|
};
|
|
121
122
|
// ─── Tool Definitions ─────────────────────────────────────────────────────────
|
|
122
123
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
123
|
-
const tools =
|
|
124
|
-
{
|
|
125
|
-
name: 'veto_status',
|
|
126
|
-
description: 'Returns Veto server status, version, and database info. Pass token_count to trigger auto-save if context usage crosses 70%.',
|
|
127
|
-
inputSchema: {
|
|
128
|
-
type: 'object',
|
|
129
|
-
properties: {
|
|
130
|
-
token_count: {
|
|
131
|
-
type: 'number',
|
|
132
|
-
description: 'Current session token count. If provided and context usage ≥ 70%, Veto auto-saves the last known session context in the background.',
|
|
133
|
-
},
|
|
134
|
-
platform: {
|
|
135
|
-
type: 'string',
|
|
136
|
-
description: 'AI platform (claude, gemini, codex). Used to select the correct context window for threshold calculation. Defaults to "claude".',
|
|
137
|
-
enum: ['claude', 'gemini', 'codex'],
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
required: [],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: 'veto_autosave_status',
|
|
145
|
-
description: 'Returns the current auto-save state: whether a context is cached, the threshold, the last auto-save time, and the session ID.',
|
|
146
|
-
inputSchema: {
|
|
147
|
-
type: 'object',
|
|
148
|
-
properties: {},
|
|
149
|
-
required: [],
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: 'veto_session_save',
|
|
154
|
-
description: 'Saves the current session context to SQLite. Pass session_id to update an existing session instead of creating a new one — use this when refreshing context mid-conversation rather than starting a new snapshot.',
|
|
155
|
-
inputSchema: {
|
|
156
|
-
type: 'object',
|
|
157
|
-
properties: {
|
|
158
|
-
summary: {
|
|
159
|
-
type: 'string',
|
|
160
|
-
description: 'A brief summary of what was accomplished this session.',
|
|
161
|
-
},
|
|
162
|
-
context: {
|
|
163
|
-
type: 'string',
|
|
164
|
-
description: 'Key context to restore (decisions, current task, file list, etc.).',
|
|
165
|
-
},
|
|
166
|
-
task_state: {
|
|
167
|
-
type: 'string',
|
|
168
|
-
description: 'Current task state — what is done and what is next.',
|
|
169
|
-
},
|
|
170
|
-
platform: {
|
|
171
|
-
type: 'string',
|
|
172
|
-
description: 'AI platform used (claude, gemini, codex). Defaults to "claude".',
|
|
173
|
-
enum: ['claude', 'gemini', 'codex'],
|
|
174
|
-
},
|
|
175
|
-
project_dir: {
|
|
176
|
-
type: 'string',
|
|
177
|
-
description: 'Absolute path to the current project directory.',
|
|
178
|
-
},
|
|
179
|
-
connection_type: {
|
|
180
|
-
type: 'string',
|
|
181
|
-
description: 'How you are connected to this AI — "subscription" (Claude Pro, Gemini Advanced) or "api" (API key). Used for usage tracking.',
|
|
182
|
-
enum: ['subscription', 'api'],
|
|
183
|
-
},
|
|
184
|
-
token_count: {
|
|
185
|
-
type: 'number',
|
|
186
|
-
description: 'Approximate tokens used this session. Veto uses this for context window monitoring.',
|
|
187
|
-
},
|
|
188
|
-
session_id: {
|
|
189
|
-
type: 'string',
|
|
190
|
-
description: 'Optional. UUID of an existing session to update in-place. When provided, Veto updates that row instead of inserting a new one — prevents session inflation when refreshing mid-conversation.',
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
required: ['summary', 'context'],
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: 'veto_session_restore',
|
|
198
|
-
description: 'Restores a previously saved session by ID. Use veto_sessions_list to find IDs.',
|
|
199
|
-
inputSchema: {
|
|
200
|
-
type: 'object',
|
|
201
|
-
properties: {
|
|
202
|
-
session_id: {
|
|
203
|
-
type: 'string',
|
|
204
|
-
description: 'UUID of the session to restore.',
|
|
205
|
-
},
|
|
206
|
-
resuming_as: {
|
|
207
|
-
type: 'string',
|
|
208
|
-
description: 'The AI client resuming this session (e.g. "claude", "gemini", "codex"). Recorded as active_client.',
|
|
209
|
-
enum: ['claude', 'gemini', 'codex'],
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
required: ['session_id'],
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: 'veto_sessions_list',
|
|
217
|
-
description: 'Lists the most recent saved sessions (up to 10).',
|
|
218
|
-
inputSchema: {
|
|
219
|
-
type: 'object',
|
|
220
|
-
properties: {
|
|
221
|
-
limit: {
|
|
222
|
-
type: 'number',
|
|
223
|
-
description: 'Number of sessions to return (default 10, max 50).',
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
required: [],
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
name: 'veto_route_task',
|
|
231
|
-
description: 'Scores a task for complexity (0-100) and returns the optimal tier, model recommendation, and rate status. Use before any substantial task to let the router decide which model to use.',
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: 'object',
|
|
234
|
-
properties: {
|
|
235
|
-
task: {
|
|
236
|
-
type: 'string',
|
|
237
|
-
description: 'The task description to score and route.',
|
|
238
|
-
},
|
|
239
|
-
agent_type: {
|
|
240
|
-
type: 'string',
|
|
241
|
-
description: 'Optional agent type — some agents are tier-locked regardless of score.',
|
|
242
|
-
enum: [
|
|
243
|
-
'lead-developer', 'system-architect', 'security-scanner',
|
|
244
|
-
'devil-advocate', 'decision-engine', 'risk-assessor',
|
|
245
|
-
'coder', 'tester', 'reviewer', 'database', 'documentation',
|
|
246
|
-
'file-manager', 'git-agent', 'search-agent', 'secrets', 'reporter',
|
|
247
|
-
'dynamic',
|
|
248
|
-
],
|
|
249
|
-
},
|
|
250
|
-
files_affected: {
|
|
251
|
-
type: 'number',
|
|
252
|
-
description: 'Number of files the task will touch (influences complexity score).',
|
|
253
|
-
},
|
|
254
|
-
force_council: {
|
|
255
|
-
type: 'boolean',
|
|
256
|
-
description: 'Set true to force a Tier 3 / council-required routing.',
|
|
257
|
-
},
|
|
258
|
-
context: {
|
|
259
|
-
type: 'string',
|
|
260
|
-
description: 'Current context text — router will return a compression plan.',
|
|
261
|
-
},
|
|
262
|
-
preferred_platform: {
|
|
263
|
-
type: 'string',
|
|
264
|
-
description: 'Preferred AI platform. Router may override if rate-limited.',
|
|
265
|
-
enum: ['claude', 'gemini', 'codex'],
|
|
266
|
-
},
|
|
267
|
-
},
|
|
268
|
-
required: ['task'],
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: 'veto_rate_status',
|
|
273
|
-
description: 'Returns current request counts and rate limit status for all AI platforms tracked by Veto.',
|
|
274
|
-
inputSchema: {
|
|
275
|
-
type: 'object',
|
|
276
|
-
properties: {},
|
|
277
|
-
required: [],
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
name: 'veto_council_debate',
|
|
282
|
-
description: 'Runs the Veto Council — 5 specialist agents (Lead Dev, PM, Architect, UX, Devil\'s Advocate) debate your task in parallel and return a GREEN / YELLOW / RED / DEADLOCK verdict before any code is written. Call this before architecture decisions, security-sensitive work, database migrations, or any task the router scores above 71.',
|
|
283
|
-
inputSchema: {
|
|
284
|
-
type: 'object',
|
|
285
|
-
properties: {
|
|
286
|
-
task: {
|
|
287
|
-
type: 'string',
|
|
288
|
-
description: 'The task or decision to debate. Be specific — include approach, tech stack, and constraints.',
|
|
289
|
-
},
|
|
290
|
-
context: {
|
|
291
|
-
type: 'string',
|
|
292
|
-
description: 'Optional: additional context such as codebase state, prior decisions, or constraints.',
|
|
293
|
-
},
|
|
294
|
-
project_dir: {
|
|
295
|
-
type: 'string',
|
|
296
|
-
description: 'Optional: absolute path to the project directory. Veto will auto-read package.json, git diff, and stack info to give the council real project context.',
|
|
297
|
-
},
|
|
298
|
-
session_id: {
|
|
299
|
-
type: 'string',
|
|
300
|
-
description: 'Optional: session ID to associate this council outcome with an active session.',
|
|
301
|
-
},
|
|
302
|
-
max_tokens: {
|
|
303
|
-
type: 'number',
|
|
304
|
-
description: 'Optional: token budget for this operation. Veto estimates output tokens and warns in the response if the estimate exceeds this limit. Logged to usage_log for tracking.',
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
required: ['task'],
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
name: 'veto_agent_plan',
|
|
312
|
-
description: 'Gets a domain-expert execution plan from a specific worker agent. Returns approach, ordered steps, checklist, patterns, and pitfalls for the task.',
|
|
313
|
-
inputSchema: {
|
|
314
|
-
type: 'object',
|
|
315
|
-
properties: {
|
|
316
|
-
agent: {
|
|
317
|
-
type: 'string',
|
|
318
|
-
description: 'The worker agent to consult.',
|
|
319
|
-
enum: ['coder', 'reviewer', 'tester', 'debugger', 'refactor', 'database', 'api', 'frontend', 'backend', 'devops', 'performance', 'migration', 'security-scanner', 'auth', 'privacy', 'secrets', 'dependency-audit', 'penetration', 'context-manager', 'decision-logger', 'project-mapper', 'pattern-learner', 'knowledge-base', 'researcher', 'tech-advisor', 'cost-analyzer', 'competitor-analyzer', 'risk-assessor', 'estimator', 'ethics-bias', 'code-quality', 'documentation', 'accessibility', 'compatibility', 'error-handling', 'task-planner', 'task-coordinator', 'file-manager', 'git-agent', 'search-agent', 'reporter', 'automation'],
|
|
320
|
-
},
|
|
321
|
-
task: { type: 'string', description: 'The task for the agent to plan.' },
|
|
322
|
-
context: { type: 'string', description: 'Optional additional context.' },
|
|
323
|
-
project_dir: { type: 'string', description: 'Optional: absolute path to the project directory. Auto-injects package.json, git diff, and stack info into the agent context.' },
|
|
324
|
-
},
|
|
325
|
-
required: ['agent', 'task'],
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
name: 'veto_code_review',
|
|
330
|
-
description: 'Runs the Code Reviewer agent on provided code. Returns scored findings (complexity, error handling, magic numbers, nesting, dead code) with severity and fixes. Pass file_path to surface findings as VS Code inline diagnostics (squiggles).',
|
|
331
|
-
inputSchema: {
|
|
332
|
-
type: 'object',
|
|
333
|
-
properties: {
|
|
334
|
-
code: { type: 'string', description: 'The code to review.' },
|
|
335
|
-
context: { type: 'string', description: 'Optional: file name, module description, or review focus.' },
|
|
336
|
-
file_path: { type: 'string', description: 'Optional: absolute path to the file being reviewed. When provided, findings are stored as VS Code inline diagnostics.' },
|
|
337
|
-
},
|
|
338
|
-
required: ['code'],
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
name: 'veto_diff_review',
|
|
343
|
-
description: 'Reviews a git diff — runs code review, security scan, and secrets scan in parallel across all changed files. Returns a structured verdict (pass/warn/fail), per-file findings, and a CI-ready summary. Pass diff directly or let Veto read it from project_dir automatically.',
|
|
344
|
-
inputSchema: {
|
|
345
|
-
type: 'object',
|
|
346
|
-
properties: {
|
|
347
|
-
diff: { type: 'string', description: 'The git diff to review. If omitted, Veto runs git diff HEAD in project_dir.' },
|
|
348
|
-
project_dir: { type: 'string', description: 'Absolute project path. Used to auto-read git diff if diff is not provided, and to inject codebase context.' },
|
|
349
|
-
context: { type: 'string', description: 'Optional: PR description, ticket number, or focus area.' },
|
|
350
|
-
},
|
|
351
|
-
required: [],
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
name: 'veto_security_scan',
|
|
356
|
-
description: 'Runs the Security Scanner (OWASP Top 10) on provided code. Returns vulnerabilities with severity, CWE/OWASP category, and remediation steps. Pass file_path to surface findings as VS Code inline diagnostics.',
|
|
357
|
-
inputSchema: {
|
|
358
|
-
type: 'object',
|
|
359
|
-
properties: {
|
|
360
|
-
code: { type: 'string', description: 'The code to scan.' },
|
|
361
|
-
context: { type: 'string', description: 'Optional: language, framework, or specific concerns.' },
|
|
362
|
-
file_path: { type: 'string', description: 'Optional: absolute path to the file being scanned. When provided, findings are stored as VS Code inline diagnostics.' },
|
|
363
|
-
},
|
|
364
|
-
required: ['code'],
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
name: 'veto_secrets_scan',
|
|
369
|
-
description: 'Scans text or code for exposed credentials — API keys, tokens, passwords, connection strings, private keys. Returns findings with masked values and line numbers. Pass file_path to surface findings as VS Code inline diagnostics.',
|
|
370
|
-
inputSchema: {
|
|
371
|
-
type: 'object',
|
|
372
|
-
properties: {
|
|
373
|
-
text: { type: 'string', description: 'The text or code to scan for secrets.' },
|
|
374
|
-
file_path: { type: 'string', description: 'Optional: absolute path to the file being scanned. When provided, findings are stored as VS Code inline diagnostics.' },
|
|
375
|
-
},
|
|
376
|
-
required: ['text'],
|
|
377
|
-
},
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
name: 'veto_execute_parallel',
|
|
381
|
-
description: 'Runs multiple worker agents simultaneously via Promise.all. Use to get domain expert input from several agents in one round-trip — e.g. coder + tester + security-scanner all planning the same feature together.',
|
|
382
|
-
inputSchema: {
|
|
383
|
-
type: 'object',
|
|
384
|
-
properties: {
|
|
385
|
-
tasks: {
|
|
386
|
-
type: 'array',
|
|
387
|
-
description: 'List of agent tasks to run in parallel.',
|
|
388
|
-
items: {
|
|
389
|
-
type: 'object',
|
|
390
|
-
properties: {
|
|
391
|
-
id: { type: 'string', description: 'Unique ID for this task (use any string).' },
|
|
392
|
-
agent: { type: 'string', description: 'Worker agent type.' },
|
|
393
|
-
task: { type: 'string', description: 'Task description for this agent.' },
|
|
394
|
-
code: { type: 'string', description: 'Optional code to analyze (triggers analyze() instead of plan()).' },
|
|
395
|
-
context: { type: 'string', description: 'Optional additional context.' },
|
|
396
|
-
project_dir: { type: 'string', description: 'Optional: per-task project dir override.' },
|
|
397
|
-
},
|
|
398
|
-
required: ['id', 'agent', 'task'],
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
project_dir: { type: 'string', description: 'Optional: project directory applied to all tasks (per-task project_dir overrides this). Auto-injects codebase context.' },
|
|
402
|
-
max_tokens: {
|
|
403
|
-
type: 'number',
|
|
404
|
-
description: 'Optional: token budget for this parallel execution. Veto estimates combined output tokens and warns if the estimate exceeds this limit. Logged to usage_log.',
|
|
405
|
-
},
|
|
406
|
-
},
|
|
407
|
-
required: ['tasks'],
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
name: 'veto_memory_store',
|
|
412
|
-
description: 'Stores a knowledge entry (solution, pattern, error, reference, or decision) in the local knowledge base for retrieval across sessions. Search before storing to avoid duplicates.',
|
|
413
|
-
inputSchema: {
|
|
414
|
-
type: 'object',
|
|
415
|
-
properties: {
|
|
416
|
-
title: { type: 'string', description: 'Precise, searchable title. Bad: "Fixed bug". Good: "Fix: Node sqlite fails on Windows without --experimental-sqlite".' },
|
|
417
|
-
content: { type: 'string', description: 'Self-contained content: problem → root cause → solution. Future agents must understand it without original context.' },
|
|
418
|
-
type: {
|
|
419
|
-
type: 'string',
|
|
420
|
-
description: 'Entry type.',
|
|
421
|
-
enum: ['solution', 'pattern', 'context', 'error', 'reference', 'decision'],
|
|
422
|
-
},
|
|
423
|
-
tags: { type: 'array', items: { type: 'string' }, description: 'Search tags (3–5 recommended). Examples: ["typescript", "auth", "jwt"].' },
|
|
424
|
-
project_dir: { type: 'string', description: 'Absolute project path. Include for project-specific knowledge; omit for general programming knowledge.' },
|
|
425
|
-
session_id: { type: 'string', description: 'Optional: associate this knowledge entry with an active session.' },
|
|
426
|
-
relevance: { type: 'number', description: 'Initial relevance score 0.0–1.0 (default 1.0).' },
|
|
427
|
-
},
|
|
428
|
-
required: ['title', 'content'],
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: 'veto_memory_search',
|
|
433
|
-
description: 'Searches the local knowledge base for entries matching a query. Call at the start of every task to find prior solutions before solving from scratch.',
|
|
434
|
-
inputSchema: {
|
|
435
|
-
type: 'object',
|
|
436
|
-
properties: {
|
|
437
|
-
query: { type: 'string', description: 'Search terms (full-text search on title and content).' },
|
|
438
|
-
type: {
|
|
439
|
-
type: 'string',
|
|
440
|
-
description: 'Filter by entry type.',
|
|
441
|
-
enum: ['solution', 'pattern', 'context', 'error', 'reference', 'decision'],
|
|
442
|
-
},
|
|
443
|
-
project_dir: { type: 'string', description: 'Filter to a specific project directory.' },
|
|
444
|
-
limit: { type: 'number', description: 'Max results to return (default 10, max 50).' },
|
|
445
|
-
},
|
|
446
|
-
required: [],
|
|
447
|
-
},
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
name: 'veto_memory_delete',
|
|
451
|
-
description: 'Deletes a knowledge entry by ID. Use to remove stale or duplicate entries found via veto_memory_search.',
|
|
452
|
-
inputSchema: {
|
|
453
|
-
type: 'object',
|
|
454
|
-
properties: {
|
|
455
|
-
id: { type: 'string', description: 'The knowledge entry ID (from veto_memory_search results).' },
|
|
456
|
-
},
|
|
457
|
-
required: ['id'],
|
|
458
|
-
},
|
|
459
|
-
},
|
|
460
|
-
{
|
|
461
|
-
name: 'veto_project_map_update',
|
|
462
|
-
description: 'Updates the project structure map for a directory. Call after creating, deleting, or moving files. The map enables fast codebase navigation without filesystem scans.',
|
|
463
|
-
inputSchema: {
|
|
464
|
-
type: 'object',
|
|
465
|
-
properties: {
|
|
466
|
-
project_dir: { type: 'string', description: 'Absolute path to the project root.' },
|
|
467
|
-
structure: { type: 'string', description: 'JSON string representing the directory tree. Example: {"src/":{"agents/":["coder.ts","reviewer.ts"],"router/":["index.ts"]}}' },
|
|
468
|
-
key_modules: {
|
|
469
|
-
type: 'array',
|
|
470
|
-
items: { type: 'string' },
|
|
471
|
-
description: 'The 10–20 most important files with their roles. Example: ["src/server.ts (MCP entry point)", "src/router/index.ts (task router)"].',
|
|
472
|
-
},
|
|
473
|
-
tech_stack: {
|
|
474
|
-
type: 'array',
|
|
475
|
-
items: { type: 'string' },
|
|
476
|
-
description: 'Frameworks and key libraries. Example: ["TypeScript", "Node.js 22", "Express", "SQLite"].',
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
required: ['project_dir', 'structure'],
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
name: 'veto_project_map_get',
|
|
484
|
-
description: 'Returns the stored project structure map for a directory. Use to navigate the codebase without scanning the filesystem.',
|
|
485
|
-
inputSchema: {
|
|
486
|
-
type: 'object',
|
|
487
|
-
properties: {
|
|
488
|
-
project_dir: { type: 'string', description: 'Absolute path to the project root.' },
|
|
489
|
-
},
|
|
490
|
-
required: ['project_dir'],
|
|
491
|
-
},
|
|
492
|
-
},
|
|
493
|
-
{
|
|
494
|
-
name: 'veto_pattern_store',
|
|
495
|
-
description: 'Stores or updates a coding pattern observed in the codebase. Patterns are keyed by category.pattern-name and confidence increases with repeated observation.',
|
|
496
|
-
inputSchema: {
|
|
497
|
-
type: 'object',
|
|
498
|
-
properties: {
|
|
499
|
-
pattern_key: { type: 'string', description: 'Pattern identifier in category.pattern-name format. Example: "code.async-pattern" or "naming.variable-case".' },
|
|
500
|
-
pattern_val: { type: 'string', description: 'The observed pattern value. Example: "async/await with try/catch, no raw Promise chains".' },
|
|
501
|
-
confidence: { type: 'number', description: 'Confidence score 0.0–1.0 (default 1.0). Increases automatically on repeated observation.' },
|
|
502
|
-
},
|
|
503
|
-
required: ['pattern_key', 'pattern_val'],
|
|
504
|
-
},
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
name: 'veto_patterns_list',
|
|
508
|
-
description: 'Returns stored coding patterns. Filter by prefix to get patterns in a specific category (e.g. prefix="naming." for all naming conventions).',
|
|
509
|
-
inputSchema: {
|
|
510
|
-
type: 'object',
|
|
511
|
-
properties: {
|
|
512
|
-
prefix: { type: 'string', description: 'Optional prefix filter. Example: "code." or "naming." or "testing.".' },
|
|
513
|
-
limit: { type: 'number', description: 'Max patterns to return (default 20).' },
|
|
514
|
-
},
|
|
515
|
-
required: [],
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
name: 'veto_memory_export',
|
|
520
|
-
description: 'Exports all local memory (sessions, knowledge, patterns, decisions, project maps) to a portable JSON file. Copy the file to another machine and run veto_memory_import there to resume work. No external services required.',
|
|
521
|
-
inputSchema: {
|
|
522
|
-
type: 'object',
|
|
523
|
-
properties: {
|
|
524
|
-
output_path: {
|
|
525
|
-
type: 'string',
|
|
526
|
-
description: 'Where to write the export file. Defaults to ~/.veto/veto-export.json. Use a path on shared storage (Dropbox, OneDrive, USB) to make transfer easy.',
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
required: [],
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
name: 'veto_memory_import',
|
|
534
|
-
description: 'Imports memory from a JSON file exported by veto_memory_export on another machine. Merges into local SQLite using INSERT OR IGNORE — existing local rows are never overwritten. Call veto_sessions_list after import to confirm sessions arrived.',
|
|
535
|
-
inputSchema: {
|
|
536
|
-
type: 'object',
|
|
537
|
-
properties: {
|
|
538
|
-
input_path: {
|
|
539
|
-
type: 'string',
|
|
540
|
-
description: 'Path to the export JSON file. Defaults to ~/.veto/veto-export.json.',
|
|
541
|
-
},
|
|
542
|
-
},
|
|
543
|
-
required: [],
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
{
|
|
547
|
-
name: 'veto_record_outcome',
|
|
548
|
-
description: 'Records a task outcome (quality score) to feed the self-learning router. Call after completing any task. After 20+ outcomes, call veto_learning_apply to update tier thresholds.',
|
|
549
|
-
inputSchema: {
|
|
550
|
-
type: 'object',
|
|
551
|
-
properties: {
|
|
552
|
-
task_type: { type: 'string', description: 'Short consistent label for the task category (e.g. "write-unit-tests", "fix-auth-bug"). Use the same label for similar tasks to enable pattern detection.' },
|
|
553
|
-
complexity: { type: 'number', description: 'The complexity score from veto_route_task (0–100).' },
|
|
554
|
-
model_tier: { type: 'number', description: 'The tier that was actually used (1, 2, or 3).', enum: [1, 2, 3] },
|
|
555
|
-
output_quality: { type: 'number', description: 'Output quality score 0–100. 90–100=excellent, 70–89=good, 50–69=acceptable, 30–49=poor, 0–29=failed.' },
|
|
556
|
-
agent: { type: 'string', description: 'The worker agent type used (optional but useful for agent performance tracking).' },
|
|
557
|
-
tokens_used: { type: 'number', description: 'Approximate tokens used (optional).' },
|
|
558
|
-
file_ext: { type: 'string', description: 'File extension of the primary file worked on (e.g. ".ts", ".sql", ".tsx"). Enables predictive agent routing — next time you work on the same extension, veto_route_task will recommend the best agent.' },
|
|
559
|
-
},
|
|
560
|
-
required: ['task_type', 'complexity', 'model_tier', 'output_quality'],
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
name: 'veto_learning_stats',
|
|
565
|
-
description: 'Returns the self-learning router dashboard: tier distribution, per-agent quality stats, suggested threshold adjustments, and council insights. Use to understand how the router is performing and where to improve.',
|
|
566
|
-
inputSchema: {
|
|
567
|
-
type: 'object',
|
|
568
|
-
properties: {
|
|
569
|
-
include_agent_stats: { type: 'boolean', description: 'Include per-agent quality breakdown (default true).' },
|
|
570
|
-
include_task_types: { type: 'boolean', description: 'Include per-task-type breakdown (default false, verbose).' },
|
|
571
|
-
include_council_insights: { type: 'boolean', description: 'Include council decision → debugging correlation (default false).' },
|
|
572
|
-
},
|
|
573
|
-
required: [],
|
|
574
|
-
},
|
|
575
|
-
},
|
|
576
|
-
{
|
|
577
|
-
name: 'veto_learning_apply',
|
|
578
|
-
description: 'Applies learned tier thresholds to the router based on recorded task outcomes. Requires at least 20 recorded outcomes. The router immediately uses the new thresholds on the next veto_route_task call.',
|
|
579
|
-
inputSchema: {
|
|
580
|
-
type: 'object',
|
|
581
|
-
properties: {},
|
|
582
|
-
required: [],
|
|
583
|
-
},
|
|
584
|
-
},
|
|
585
|
-
{
|
|
586
|
-
name: 'veto_handoff',
|
|
587
|
-
description: 'Saves the current session and returns step-by-step instructions to continue on another AI platform (Gemini or Codex). Call this when Claude is approaching its rate limit. The receiving platform calls veto_continue to restore full context instantly.',
|
|
588
|
-
inputSchema: {
|
|
589
|
-
type: 'object',
|
|
590
|
-
properties: {
|
|
591
|
-
summary: { type: 'string', description: 'What was accomplished this session — one or two sentences.' },
|
|
592
|
-
context: { type: 'string', description: 'Key context the next platform needs: active decisions, file paths, constraints.' },
|
|
593
|
-
task_state: { type: 'string', description: 'Current task state — what is done, what is in progress, what is next.' },
|
|
594
|
-
from_platform: { type: 'string', enum: ['claude', 'gemini', 'codex'], description: 'Platform handing off (default: claude).' },
|
|
595
|
-
to_platform: { type: 'string', enum: ['gemini', 'codex', 'claude'], description: 'Target platform. If omitted, Veto picks the platform with the most headroom.' },
|
|
596
|
-
project_dir: { type: 'string', description: 'Absolute path to the current project directory.' },
|
|
597
|
-
token_count: { type: 'number', description: 'Approximate tokens used this session.' },
|
|
598
|
-
},
|
|
599
|
-
required: ['summary', 'context'],
|
|
600
|
-
},
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
name: 'veto_continue',
|
|
604
|
-
description: 'Restores the most recent session on any platform. Call this immediately after switching platforms — Veto returns the full context, summary, and next action. Nothing needs to be re-explained.',
|
|
605
|
-
inputSchema: {
|
|
606
|
-
type: 'object',
|
|
607
|
-
properties: {
|
|
608
|
-
session_id: { type: 'string', description: 'Optional. Session ID from veto_handoff. If omitted, the most recent saved session is restored.' },
|
|
609
|
-
resuming_as: { type: 'string', description: 'The AI client resuming this session (e.g. "gemini"). Recorded as active_client so you can track which tool is currently working on it.', enum: ['claude', 'gemini', 'codex'] },
|
|
610
|
-
},
|
|
611
|
-
required: [],
|
|
612
|
-
},
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
name: 'veto_platform_setup',
|
|
616
|
-
description: 'Returns the exact MCP config and setup steps to connect a specific AI platform to this Veto server.',
|
|
617
|
-
inputSchema: {
|
|
618
|
-
type: 'object',
|
|
619
|
-
properties: {
|
|
620
|
-
platform: { type: 'string', enum: ['claude', 'gemini', 'codex'], description: 'The platform to get setup instructions for.' },
|
|
621
|
-
veto_server_path: { type: 'string', description: 'Absolute path to the built veto server (dist/server.js).' },
|
|
622
|
-
},
|
|
623
|
-
required: ['platform', 'veto_server_path'],
|
|
624
|
-
},
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
name: 'veto_watch',
|
|
628
|
-
description: 'Starts a file watcher on a project directory. Returns a watch_id. Call veto_watch_poll to collect file-change events with recommended agents. Call veto_watch_stop when done.',
|
|
629
|
-
inputSchema: {
|
|
630
|
-
type: 'object',
|
|
631
|
-
properties: {
|
|
632
|
-
project_dir: { type: 'string', description: 'Absolute path to the project directory to watch.' },
|
|
633
|
-
},
|
|
634
|
-
required: ['project_dir'],
|
|
635
|
-
},
|
|
636
|
-
},
|
|
637
|
-
{
|
|
638
|
-
name: 'veto_watch_poll',
|
|
639
|
-
description: 'Polls for file-change events from an active watcher. Returns accumulated events since last poll (events are cleared on read). Each event includes the file, recommended agent, and suggested veto tool to call.',
|
|
640
|
-
inputSchema: {
|
|
641
|
-
type: 'object',
|
|
642
|
-
properties: {
|
|
643
|
-
watch_id: { type: 'string', description: 'The watch_id returned by veto_watch.' },
|
|
644
|
-
},
|
|
645
|
-
required: ['watch_id'],
|
|
646
|
-
},
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
name: 'veto_watch_stop',
|
|
650
|
-
description: 'Stops an active file watcher.',
|
|
651
|
-
inputSchema: {
|
|
652
|
-
type: 'object',
|
|
653
|
-
properties: {
|
|
654
|
-
watch_id: { type: 'string', description: 'The watch_id returned by veto_watch.' },
|
|
655
|
-
},
|
|
656
|
-
required: ['watch_id'],
|
|
657
|
-
},
|
|
658
|
-
},
|
|
659
|
-
{
|
|
660
|
-
name: 'veto_workflow',
|
|
661
|
-
description: 'Runs a sequential agent pipeline with optional pass/fail gates between steps. Each step runs a worker agent; if a gate score is set and the step confidence falls below it, the pipeline stops. Returns per-step results plus an overall verdict (passed/partial/failed).',
|
|
662
|
-
inputSchema: {
|
|
663
|
-
type: 'object',
|
|
664
|
-
properties: {
|
|
665
|
-
steps: {
|
|
666
|
-
type: 'array',
|
|
667
|
-
description: 'Ordered pipeline steps.',
|
|
668
|
-
items: {
|
|
669
|
-
type: 'object',
|
|
670
|
-
properties: {
|
|
671
|
-
id: { type: 'string', description: 'Step identifier.' },
|
|
672
|
-
agent: { type: 'string', description: 'Worker agent type.' },
|
|
673
|
-
task: { type: 'string', description: 'Task description for this step.' },
|
|
674
|
-
code: { type: 'string', description: 'Optional code to analyze.' },
|
|
675
|
-
context: { type: 'string', description: 'Optional context.' },
|
|
676
|
-
gate: { type: 'number', description: 'Optional minimum confidence % (0–100) required to proceed to the next step.' },
|
|
677
|
-
},
|
|
678
|
-
required: ['id', 'agent', 'task'],
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
project_dir: { type: 'string', description: 'Optional project directory — auto-injects codebase context into all steps.' },
|
|
682
|
-
},
|
|
683
|
-
required: ['steps'],
|
|
684
|
-
},
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
name: 'veto_explain',
|
|
688
|
-
description: 'Reads a file and returns an expert explanation from the most appropriate agent (auto-detected from file extension and name). Pass depth to control detail level.',
|
|
689
|
-
inputSchema: {
|
|
690
|
-
type: 'object',
|
|
691
|
-
properties: {
|
|
692
|
-
file_path: { type: 'string', description: 'Absolute path to the file to explain.' },
|
|
693
|
-
depth: { type: 'string', enum: ['overview', 'detailed', 'line-by-line'], description: 'Explanation depth. Default: overview.' },
|
|
694
|
-
context: { type: 'string', description: 'Optional additional context about what you want explained.' },
|
|
695
|
-
},
|
|
696
|
-
required: ['file_path'],
|
|
697
|
-
},
|
|
698
|
-
},
|
|
699
|
-
{
|
|
700
|
-
name: 'veto_plugins',
|
|
701
|
-
description: 'Lists all custom agents loaded from ~/.veto/agents/. Drop a .js file there that exports plan(task, context?) to register a new agent available in veto_agent_plan and veto_execute_parallel.',
|
|
702
|
-
inputSchema: {
|
|
703
|
-
type: 'object',
|
|
704
|
-
properties: {},
|
|
705
|
-
required: [],
|
|
706
|
-
},
|
|
707
|
-
},
|
|
708
|
-
// ── Phase 13: Developer Intelligence ──────────────────────────────────────
|
|
709
|
-
{
|
|
710
|
-
name: 'veto_docs_fetch',
|
|
711
|
-
description: 'Fetches current, version-accurate documentation for any npm, PyPI, or crates.io package and returns it for injection into agent context. Eliminates hallucinated APIs. Results are cached for 24 hours.',
|
|
712
|
-
inputSchema: {
|
|
713
|
-
type: 'object',
|
|
714
|
-
properties: {
|
|
715
|
-
package_name: { type: 'string', description: 'Package name (e.g. "react", "requests", "serde").' },
|
|
716
|
-
ecosystem: { type: 'string', enum: ['npm', 'pypi', 'crates'], description: 'Package ecosystem.' },
|
|
717
|
-
version: { type: 'string', description: 'Specific version. Defaults to latest.' },
|
|
718
|
-
max_chars: { type: 'number', description: 'Max characters to return (default 8000). Higher = more complete docs, more tokens.' },
|
|
719
|
-
},
|
|
720
|
-
required: ['package_name', 'ecosystem'],
|
|
721
|
-
},
|
|
722
|
-
},
|
|
723
|
-
{
|
|
724
|
-
name: 'veto_context_status',
|
|
725
|
-
description: 'Returns the context window usage for a saved session — tokens used, % of platform limit consumed, and whether to compress or hand off before the window fills.',
|
|
726
|
-
inputSchema: {
|
|
727
|
-
type: 'object',
|
|
728
|
-
properties: {
|
|
729
|
-
session_id: { type: 'string', description: 'Session ID to check.' },
|
|
730
|
-
},
|
|
731
|
-
required: ['session_id'],
|
|
732
|
-
},
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
name: 'veto_task_parse',
|
|
736
|
-
description: 'Parses a plain-English project description or PRD into a structured task DAG with dependencies, complexity scores, priorities, and suggested agent assignments. Feeds directly into veto_workflow.',
|
|
737
|
-
inputSchema: {
|
|
738
|
-
type: 'object',
|
|
739
|
-
properties: {
|
|
740
|
-
description: { type: 'string', description: 'Project description, PRD, or feature brief to parse into tasks.' },
|
|
741
|
-
project_dir: { type: 'string', description: 'Optional project directory for codebase context injection.' },
|
|
742
|
-
max_tasks: { type: 'number', description: 'Maximum number of tasks to generate (default 20).' },
|
|
743
|
-
},
|
|
744
|
-
required: ['description'],
|
|
745
|
-
},
|
|
746
|
-
},
|
|
747
|
-
// ── Phase 14: Observability & Safety ──────────────────────────────────────
|
|
748
|
-
{
|
|
749
|
-
name: 'veto_usage_status',
|
|
750
|
-
description: 'Live AI usage dashboard. Shows tokens consumed today, requests per platform, subscription vs API usage split, 7-day history, and warnings when approaching limits.',
|
|
751
|
-
inputSchema: {
|
|
752
|
-
type: 'object',
|
|
753
|
-
properties: {},
|
|
754
|
-
required: [],
|
|
755
|
-
},
|
|
756
|
-
},
|
|
757
|
-
{
|
|
758
|
-
name: 'veto_audit_log',
|
|
759
|
-
description: 'Queryable log of every council verdict, decision, and session event. Filter by session, agent, verdict, or date. Essential for tracing what happened and why.',
|
|
760
|
-
inputSchema: {
|
|
761
|
-
type: 'object',
|
|
762
|
-
properties: {
|
|
763
|
-
session_id: { type: 'string', description: 'Filter to a specific session.' },
|
|
764
|
-
verdict: { type: 'string', description: 'Filter by council verdict (GREEN, YELLOW, RED).' },
|
|
765
|
-
since: { type: 'string', description: 'ISO date — only return events after this time.' },
|
|
766
|
-
limit: { type: 'number', description: 'Max events to return (default 20, max 100).' },
|
|
767
|
-
},
|
|
768
|
-
required: [],
|
|
769
|
-
},
|
|
770
|
-
},
|
|
771
|
-
{
|
|
772
|
-
name: 'veto_health',
|
|
773
|
-
description: 'Returns a live health snapshot of the Veto server — DB size, session/memory/pattern counts, uptime, error count, and average council latency.',
|
|
774
|
-
inputSchema: {
|
|
775
|
-
type: 'object',
|
|
776
|
-
properties: {},
|
|
777
|
-
required: [],
|
|
778
|
-
},
|
|
779
|
-
},
|
|
780
|
-
// ── Phase 15: CI/CD & Distribution ────────────────────────────────────────
|
|
781
|
-
{
|
|
782
|
-
name: 'veto_ci_gate',
|
|
783
|
-
description: 'CI/CD pipeline gate. Runs code review + security scan + secrets scan on a git diff and returns a structured pass/warn/fail verdict with exit code. Ready for GitHub Actions and GitLab CI.',
|
|
784
|
-
inputSchema: {
|
|
785
|
-
type: 'object',
|
|
786
|
-
properties: {
|
|
787
|
-
project_dir: { type: 'string', description: 'Absolute project path. Veto reads git diff HEAD automatically.' },
|
|
788
|
-
diff: { type: 'string', description: 'Optional: pass a diff string directly instead of reading from project_dir.' },
|
|
789
|
-
context: { type: 'string', description: 'Optional: PR description or ticket number for context.' },
|
|
790
|
-
fail_on: { type: 'string', enum: ['warn', 'fail'], description: 'Whether WARN counts as a failure (exit code 1). Default: "fail" — only FAIL exits non-zero.' },
|
|
791
|
-
},
|
|
792
|
-
required: ['project_dir'],
|
|
793
|
-
},
|
|
794
|
-
},
|
|
795
|
-
{
|
|
796
|
-
name: 'veto_pr_review',
|
|
797
|
-
description: 'Fetches a GitHub PR diff and runs the full Veto triple-scan (code review + security + secrets). Returns a structured verdict and ready-to-post GitHub review comments. Set GITHUB_TOKEN env var for private repos.',
|
|
798
|
-
inputSchema: {
|
|
799
|
-
type: 'object',
|
|
800
|
-
properties: {
|
|
801
|
-
pr_url: { type: 'string', description: 'Full GitHub PR URL. e.g. https://github.com/owner/repo/pull/123' },
|
|
802
|
-
context: { type: 'string', description: 'Optional: PR description or ticket number for extra context.' },
|
|
803
|
-
fail_on: { type: 'string', enum: ['warn', 'fail'], description: 'Whether WARN counts as a failure. Default: "fail".' },
|
|
804
|
-
},
|
|
805
|
-
required: ['pr_url'],
|
|
806
|
-
},
|
|
807
|
-
},
|
|
808
|
-
// ── Phase 16: Workspace Discovery & Summarization ─────────────────────────
|
|
809
|
-
{
|
|
810
|
-
name: 'veto_discover',
|
|
811
|
-
description: 'Scans a project directory and builds a rich context map: git state, tech stack, file structure, dependencies, and key config files. Stores the result in Veto memory so agents always have accurate project context. Call this once per project or after major structural changes.',
|
|
812
|
-
inputSchema: {
|
|
813
|
-
type: 'object',
|
|
814
|
-
properties: {
|
|
815
|
-
project_dir: { type: 'string', description: 'Absolute path to the project directory to scan.' },
|
|
816
|
-
depth: { type: 'string', enum: ['quick', 'standard', 'full'], description: 'Scan depth. quick: git + package metadata only. standard: + file tree up to 3 levels (default). full: + contents of key config files.' },
|
|
817
|
-
store: { type: 'boolean', description: 'Whether to store the discovery in Veto memory as a project map. Default: true.' },
|
|
818
|
-
},
|
|
819
|
-
required: ['project_dir'],
|
|
820
|
-
},
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
name: 'veto_summarize',
|
|
824
|
-
description: 'Generates a concise expert briefing of a project, directory, or file. Use at the start of a session to orient yourself on unfamiliar code. Returns bullet-point summary, key components, tech stack, and entry points. Faster and higher-level than veto_explain.',
|
|
825
|
-
inputSchema: {
|
|
826
|
-
type: 'object',
|
|
827
|
-
properties: {
|
|
828
|
-
project_dir: { type: 'string', description: 'Absolute path to a project directory to summarize.' },
|
|
829
|
-
file_path: { type: 'string', description: 'Absolute path to a single file to summarize. If both project_dir and file_path are given, file_path takes precedence.' },
|
|
830
|
-
focus: { type: 'string', description: 'Optional focus area: e.g. "security", "APIs", "data flow", "architecture". Narrows the summary.' },
|
|
831
|
-
format: { type: 'string', enum: ['brief', 'detailed'], description: 'brief: 4–6 bullet points (default). detailed: paragraph-level prose.' },
|
|
832
|
-
},
|
|
833
|
-
required: [],
|
|
834
|
-
},
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
name: 'veto_benchmark',
|
|
838
|
-
description: 'Compares two competing approaches by running a full council debate on each in parallel, then returns a structured winner analysis with verdict, confidence delta, warning counts, and council reasoning. Use when you have two valid options and want an unbiased council judgment before committing.',
|
|
839
|
-
inputSchema: {
|
|
840
|
-
type: 'object',
|
|
841
|
-
properties: {
|
|
842
|
-
task: { type: 'string', description: 'The decision context — what problem are both approaches solving?' },
|
|
843
|
-
approach_a: { type: 'string', description: 'First approach to evaluate. Be specific about tech choices, trade-offs, and constraints.' },
|
|
844
|
-
approach_b: { type: 'string', description: 'Second approach to evaluate. Same level of detail as approach_a.' },
|
|
845
|
-
context: { type: 'string', description: 'Optional: shared context for both debates (architecture notes, constraints, team size, etc.).' },
|
|
846
|
-
project_dir: { type: 'string', description: 'Optional: auto-inject package.json and git diff context.' },
|
|
847
|
-
},
|
|
848
|
-
required: ['task', 'approach_a', 'approach_b'],
|
|
849
|
-
},
|
|
850
|
-
},
|
|
851
|
-
];
|
|
124
|
+
const tools = TOOL_DEFINITIONS;
|
|
852
125
|
return { tools: tools.map(t => ({ ...t, annotations: TOOL_ANNOTATIONS[t.name] ?? {} })) };
|
|
853
126
|
});
|
|
854
127
|
// ─── Shared Scan Utility ──────────────────────────────────────────────────────
|
|
@@ -918,10 +191,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
918
191
|
case 'veto_status': {
|
|
919
192
|
const statusTokenCount = typeof args?.token_count === 'number' ? args.token_count : null;
|
|
920
193
|
const statusPlatform = args?.platform ? String(args.platform) : 'claude';
|
|
194
|
+
const statusModel = args?.model ? String(args.model) : undefined;
|
|
921
195
|
if (statusTokenCount !== null && statusTokenCount > 0) {
|
|
922
196
|
trackTokens(statusPlatform, statusTokenCount);
|
|
923
197
|
}
|
|
924
|
-
const autoSaveResult = statusTokenCount !== null ? maybeAutoSave(statusTokenCount, statusPlatform) : null;
|
|
198
|
+
const autoSaveResult = statusTokenCount !== null ? maybeAutoSave(statusTokenCount, statusPlatform, statusModel) : null;
|
|
925
199
|
return {
|
|
926
200
|
content: [
|
|
927
201
|
{
|
|
@@ -978,9 +252,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
978
252
|
if (sessionProjectDir)
|
|
979
253
|
activeProjectDir = sessionProjectDir;
|
|
980
254
|
const savePlatform = args?.platform ? String(args.platform) : 'claude';
|
|
981
|
-
|
|
982
|
-
const
|
|
983
|
-
const
|
|
255
|
+
// Enforce size limits — unbounded strings exhaust SQLite page cache and memory
|
|
256
|
+
const RAW_SUMMARY = String(args?.summary ?? '');
|
|
257
|
+
const RAW_CONTEXT = String(args?.context ?? '');
|
|
258
|
+
const RAW_TASK_STATE = args?.task_state ? String(args.task_state) : undefined;
|
|
259
|
+
const SUMMARY_LIMIT = 2_000;
|
|
260
|
+
const CONTEXT_LIMIT = 50_000;
|
|
261
|
+
const TASK_STATE_LIMIT = 20_000;
|
|
262
|
+
const saveSummary = RAW_SUMMARY.slice(0, SUMMARY_LIMIT);
|
|
263
|
+
const saveContext = RAW_CONTEXT.slice(0, CONTEXT_LIMIT);
|
|
264
|
+
const saveTaskState = RAW_TASK_STATE ? RAW_TASK_STATE.slice(0, TASK_STATE_LIMIT) : undefined;
|
|
265
|
+
const truncationWarnings = [];
|
|
266
|
+
if (RAW_SUMMARY.length > SUMMARY_LIMIT)
|
|
267
|
+
truncationWarnings.push(`summary truncated to ${SUMMARY_LIMIT} chars (was ${RAW_SUMMARY.length})`);
|
|
268
|
+
if (RAW_CONTEXT.length > CONTEXT_LIMIT)
|
|
269
|
+
truncationWarnings.push(`context truncated to ${CONTEXT_LIMIT} chars (was ${RAW_CONTEXT.length})`);
|
|
270
|
+
if (RAW_TASK_STATE && RAW_TASK_STATE.length > TASK_STATE_LIMIT)
|
|
271
|
+
truncationWarnings.push(`task_state truncated to ${TASK_STATE_LIMIT} chars (was ${RAW_TASK_STATE.length})`);
|
|
984
272
|
const existingId = args?.session_id ? String(args.session_id) : undefined;
|
|
985
273
|
const sessionInput = {
|
|
986
274
|
summary: saveSummary,
|
|
@@ -990,6 +278,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
990
278
|
connection_type: args?.connection_type ? String(args.connection_type) : 'subscription',
|
|
991
279
|
project_dir: sessionProjectDir,
|
|
992
280
|
token_count: typeof args?.token_count === 'number' ? args.token_count : 0,
|
|
281
|
+
tags: Array.isArray(args?.tags) ? args.tags.map(String) : undefined,
|
|
993
282
|
};
|
|
994
283
|
let result;
|
|
995
284
|
let wasUpdate = false;
|
|
@@ -1007,7 +296,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1007
296
|
result = saveSession(sessionInput);
|
|
1008
297
|
}
|
|
1009
298
|
// Cache for auto-save: future veto_status calls with high token_count will re-save this context
|
|
1010
|
-
|
|
299
|
+
const saveModel = args?.model ? String(args.model) : undefined;
|
|
300
|
+
const resolvedWindow = saveModel ? resolveContextWindow(savePlatform, saveModel) : undefined;
|
|
301
|
+
autoSave.cached = { summary: saveSummary, context: saveContext, task_state: saveTaskState, platform: savePlatform, project_dir: sessionProjectDir, context_window: resolvedWindow };
|
|
1011
302
|
autoSave.last_save_at = result.saved_at;
|
|
1012
303
|
autoSave.last_session_id = result.session_id;
|
|
1013
304
|
const responseObj = {
|
|
@@ -1021,6 +312,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1021
312
|
saved_at: result.saved_at,
|
|
1022
313
|
updated: wasUpdate,
|
|
1023
314
|
...(wasUpdate ? {} : { usage_pct: result.usage_pct, context_warning: result.context_warning }),
|
|
315
|
+
...(truncationWarnings.length > 0 ? { truncation_warnings: truncationWarnings } : {}),
|
|
1024
316
|
};
|
|
1025
317
|
if (result.continuation_prompt)
|
|
1026
318
|
responseObj.continuation_prompt = result.continuation_prompt;
|
|
@@ -1044,22 +336,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1044
336
|
const s = result.session;
|
|
1045
337
|
if (s.project_dir)
|
|
1046
338
|
activeProjectDir = s.project_dir;
|
|
339
|
+
const parsedTaskState = s.task_state ? (() => { try {
|
|
340
|
+
return JSON.parse(s.task_state);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return s.task_state;
|
|
344
|
+
} })() : null;
|
|
345
|
+
const nextAction = (typeof parsedTaskState === 'object' && parsedTaskState !== null)
|
|
346
|
+
? (parsedTaskState.nextAction ?? parsedTaskState.next_action ?? null)
|
|
347
|
+
: null;
|
|
348
|
+
const resumeInstructions = [
|
|
349
|
+
'Context restored from previous session. Trust the summary, context, and task_state above — they were written by the AI that last worked on this.',
|
|
350
|
+
'Do NOT re-read source files to orient yourself. That defeats the purpose of session restore and wastes tokens.',
|
|
351
|
+
'Only open a file if you are about to EDIT it — not to "verify" or "familiarize yourself" with it.',
|
|
352
|
+
nextAction ? `Start immediately with: ${nextAction}` : 'Read task_state.nextAction (or context) for where to start.',
|
|
353
|
+
'If context seems stale (e.g. you find a file has changed), read only that file, update the context, and continue.',
|
|
354
|
+
].join(' ');
|
|
1047
355
|
return {
|
|
1048
356
|
content: [
|
|
1049
357
|
{
|
|
1050
358
|
type: 'text',
|
|
1051
359
|
text: JSON.stringify({
|
|
1052
360
|
success: true,
|
|
361
|
+
resume_instructions: resumeInstructions,
|
|
1053
362
|
session_id: s.id,
|
|
1054
363
|
created_by: s.platform,
|
|
1055
|
-
|
|
1056
|
-
last_resumed_at: s.last_resumed_at,
|
|
1057
|
-
started_at: s.started_at,
|
|
1058
|
-
ended_at: s.ended_at,
|
|
364
|
+
saved_at: s.started_at,
|
|
1059
365
|
project_dir: s.project_dir,
|
|
1060
366
|
summary: s.summary,
|
|
1061
|
-
context: s.context ?
|
|
1062
|
-
|
|
367
|
+
context: s.context ? (() => { try {
|
|
368
|
+
return JSON.parse(s.context);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return s.context;
|
|
372
|
+
} })() : null,
|
|
373
|
+
task_state: parsedTaskState,
|
|
1063
374
|
token_count: s.token_count,
|
|
1064
375
|
}, null, 2),
|
|
1065
376
|
},
|
|
@@ -1068,7 +379,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1068
379
|
}
|
|
1069
380
|
case 'veto_sessions_list': {
|
|
1070
381
|
const limit = Math.min(typeof args?.limit === 'number' ? args.limit : 10, 50);
|
|
1071
|
-
const
|
|
382
|
+
const query = args?.query ? String(args.query).trim() : undefined;
|
|
383
|
+
const sessions = listSessions(limit, query);
|
|
1072
384
|
return {
|
|
1073
385
|
content: [
|
|
1074
386
|
{
|
|
@@ -1083,6 +395,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1083
395
|
project_dir: s.project_dir,
|
|
1084
396
|
summary: s.summary,
|
|
1085
397
|
token_count: s.token_count,
|
|
398
|
+
tags: s.tags ? JSON.parse(s.tags) : [],
|
|
1086
399
|
})),
|
|
1087
400
|
}, null, 2),
|
|
1088
401
|
},
|
|
@@ -1127,11 +440,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1127
440
|
isError: true,
|
|
1128
441
|
};
|
|
1129
442
|
}
|
|
443
|
+
const strictnessArg = (['fast', 'standard', 'strict'].includes(String(args?.strictness ?? '')))
|
|
444
|
+
? String(args.strictness)
|
|
445
|
+
: 'standard';
|
|
1130
446
|
const debateStart = Date.now();
|
|
1131
|
-
const result =
|
|
447
|
+
const result = await runLlmDebate(server, {
|
|
1132
448
|
task,
|
|
1133
449
|
context: args?.context ? String(args.context) : undefined,
|
|
1134
450
|
project_dir: args?.project_dir ? String(args.project_dir) : undefined,
|
|
451
|
+
strictness: strictnessArg,
|
|
1135
452
|
});
|
|
1136
453
|
const debateDuration = Date.now() - debateStart;
|
|
1137
454
|
const sessionId = args?.session_id ? String(args.session_id) : undefined;
|
|
@@ -1156,22 +473,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1156
473
|
recordOutcome(task.slice(0, 50), 50, tMap[result.final_verdict] ?? 2, 'council', qMap[result.final_verdict] ?? 50);
|
|
1157
474
|
}
|
|
1158
475
|
// Auto-store RED verdicts so they appear in the Memory panel immediately
|
|
1159
|
-
if (result.final_verdict === 'RED') {
|
|
476
|
+
if (result.final_verdict === 'RED' || (result.final_verdict === 'YELLOW' && (result.warnings.length >= 2 || result.block_reasons.length > 0))) {
|
|
477
|
+
const isRed = result.final_verdict === 'RED';
|
|
1160
478
|
const lines = [`Task: ${task}`];
|
|
1161
479
|
if (result.block_reasons.length > 0)
|
|
1162
480
|
lines.push(`\nBlocked by:\n${result.block_reasons.map(r => `- ${r}`).join('\n')}`);
|
|
1163
481
|
if (result.warnings.length > 0)
|
|
1164
482
|
lines.push(`\nWarnings:\n${result.warnings.map(w => `- ${w}`).join('\n')}`);
|
|
483
|
+
// Include per-agent reasoning so future debates inherit the full context
|
|
484
|
+
const agentSummary = Object.entries(result.votes)
|
|
485
|
+
.filter(([, v]) => v.verdict !== 'approve')
|
|
486
|
+
.map(([name, v]) => `- ${name} [${v.verdict}]: ${v.reason}`)
|
|
487
|
+
.join('\n');
|
|
488
|
+
if (agentSummary)
|
|
489
|
+
lines.push(`\nAgent reasoning:\n${agentSummary}`);
|
|
1165
490
|
if (result.recommended)
|
|
1166
491
|
lines.push(`\nRecommended: ${result.recommended}`);
|
|
1167
492
|
storeKnowledge({
|
|
1168
493
|
type: 'decision',
|
|
1169
|
-
title:
|
|
494
|
+
title: `${result.final_verdict}: ${task.slice(0, 80)}`,
|
|
1170
495
|
content: lines.join(''),
|
|
1171
|
-
tags: ['red-verdict', 'blocked', 'council'],
|
|
496
|
+
tags: [isRed ? 'red-verdict' : 'yellow-verdict', isRed ? 'blocked' : 'caution', 'council'],
|
|
1172
497
|
project_dir: args?.project_dir ? String(args.project_dir) : undefined,
|
|
1173
498
|
session_id: sessionId,
|
|
1174
|
-
relevance: 1.0,
|
|
499
|
+
relevance: isRed ? 1.0 : 0.8,
|
|
1175
500
|
});
|
|
1176
501
|
}
|
|
1177
502
|
const responsePayload = {
|
|
@@ -1182,13 +507,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1182
507
|
recommended: result.recommended,
|
|
1183
508
|
debated_at: result.debated_at,
|
|
1184
509
|
votes: {
|
|
1185
|
-
lead_dev: result.votes.lead_dev
|
|
1186
|
-
pm: result.votes.pm
|
|
1187
|
-
architect: result.votes.architect
|
|
1188
|
-
ux: result.votes.ux
|
|
1189
|
-
devil: result.votes.devil
|
|
1190
|
-
legal: result.votes.legal
|
|
1191
|
-
security: result.votes.security
|
|
510
|
+
lead_dev: result.votes.lead_dev,
|
|
511
|
+
pm: result.votes.pm,
|
|
512
|
+
architect: result.votes.architect,
|
|
513
|
+
ux: result.votes.ux,
|
|
514
|
+
devil: result.votes.devil,
|
|
515
|
+
legal: result.votes.legal,
|
|
516
|
+
security: result.votes.security,
|
|
1192
517
|
},
|
|
1193
518
|
};
|
|
1194
519
|
const fullText = result.formatted_output + '\n\n' + JSON.stringify(responsePayload, null, 2);
|
|
@@ -1719,42 +1044,53 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1719
1044
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1720
1045
|
}
|
|
1721
1046
|
case 'veto_explain': {
|
|
1722
|
-
const filePath =
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1047
|
+
const filePath = args?.file_path ? String(args.file_path).trim() : '';
|
|
1048
|
+
const rawText = args?.text ? String(args.text).trim() : '';
|
|
1049
|
+
const depth = String(args?.depth ?? 'overview');
|
|
1050
|
+
const userContext = args?.context ? String(args.context) : undefined;
|
|
1051
|
+
if (!filePath && !rawText) {
|
|
1052
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Provide file_path or text.' }) }], isError: true };
|
|
1728
1053
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
1054
|
+
let fileContent;
|
|
1055
|
+
let agent;
|
|
1056
|
+
let taskLabel;
|
|
1057
|
+
if (rawText) {
|
|
1058
|
+
fileContent = rawText;
|
|
1059
|
+
// Auto-detect agent from content: error messages → debugger, stack traces → debugger, code → coder
|
|
1060
|
+
const looksLikeError = /error|exception|traceback|stack trace|at \w+\.|TypeError|SyntaxError|ENOENT/i.test(rawText);
|
|
1061
|
+
agent = looksLikeError ? 'debugger' : 'coder';
|
|
1062
|
+
taskLabel = looksLikeError ? 'raw error/stack trace' : 'raw text input';
|
|
1731
1063
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
else if (/\.(test|spec)\.(ts|js|tsx|jsx)$/.test(name_))
|
|
1742
|
-
agent = 'tester';
|
|
1743
|
-
else if (['.yaml', '.yml', '.toml', '.dockerfile'].includes(ext) || name_ === 'dockerfile')
|
|
1744
|
-
agent = 'devops';
|
|
1745
|
-
else if (name_.includes('auth') || name_.includes('login') || name_.includes('jwt') || name_.includes('token'))
|
|
1746
|
-
agent = 'auth';
|
|
1747
|
-
else if (name_.includes('security') || name_.includes('crypt'))
|
|
1748
|
-
agent = 'security-scanner';
|
|
1749
|
-
else if (['.ts', '.js', '.mjs'].includes(ext))
|
|
1064
|
+
else {
|
|
1065
|
+
try {
|
|
1066
|
+
fileContent = readFileSync(filePath, 'utf8');
|
|
1067
|
+
}
|
|
1068
|
+
catch {
|
|
1069
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Cannot read file: ${filePath}` }) }], isError: true };
|
|
1070
|
+
}
|
|
1071
|
+
const ext = extname(filePath).toLowerCase();
|
|
1072
|
+
const name_ = basename(filePath).toLowerCase();
|
|
1750
1073
|
agent = 'coder';
|
|
1751
|
-
|
|
1752
|
-
|
|
1074
|
+
if (['.tsx', '.jsx', '.vue', '.svelte'].includes(ext))
|
|
1075
|
+
agent = 'frontend';
|
|
1076
|
+
else if (['.sql', '.prisma'].includes(ext) || name_.includes('schema'))
|
|
1077
|
+
agent = 'database';
|
|
1078
|
+
else if (/\.(test|spec)\.(ts|js|tsx|jsx)$/.test(name_))
|
|
1079
|
+
agent = 'tester';
|
|
1080
|
+
else if (['.yaml', '.yml', '.toml', '.dockerfile'].includes(ext) || name_ === 'dockerfile')
|
|
1081
|
+
agent = 'devops';
|
|
1082
|
+
else if (name_.includes('auth') || name_.includes('login') || name_.includes('jwt') || name_.includes('token'))
|
|
1083
|
+
agent = 'auth';
|
|
1084
|
+
else if (name_.includes('security') || name_.includes('crypt'))
|
|
1085
|
+
agent = 'security-scanner';
|
|
1086
|
+
taskLabel = `${ext} file: ${basename(filePath)}`;
|
|
1087
|
+
}
|
|
1088
|
+
const task = `Explain this ${taskLabel} at ${depth} depth.${userContext ? ` Focus: ${userContext}` : ''}`;
|
|
1753
1089
|
const result = await executeOne({ id: 'explain-1', agent, task, code: fileContent, project_dir: undefined });
|
|
1754
|
-
autoRecord(`explain ${
|
|
1090
|
+
autoRecord(`explain ${taskLabel}`, agent, Math.round(result.output.confidence * 100));
|
|
1755
1091
|
return {
|
|
1756
1092
|
content: [{ type: 'text', text: JSON.stringify({
|
|
1757
|
-
|
|
1093
|
+
source: filePath || 'raw text', agent_used: agent, depth,
|
|
1758
1094
|
explanation: result.plan ?? result.analysis,
|
|
1759
1095
|
output: result.output,
|
|
1760
1096
|
}, null, 2) }],
|
|
@@ -1796,6 +1132,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1796
1132
|
if (!description) {
|
|
1797
1133
|
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'description is required.' }) }], isError: true };
|
|
1798
1134
|
}
|
|
1135
|
+
const hash = createHash('sha256').update(description).digest('hex').slice(0, 16);
|
|
1136
|
+
const cached = getTaskPlan(hash);
|
|
1137
|
+
if (cached) {
|
|
1138
|
+
const plan = JSON.parse(cached.plan_json);
|
|
1139
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, plan_id: cached.id, cached: true, ...plan }, null, 2) }] };
|
|
1140
|
+
}
|
|
1799
1141
|
const ctx = project_dir ? buildContextString(project_dir) : '';
|
|
1800
1142
|
const planResult = await executeOne({ id: 'task-parse-1', agent: 'task-planner', task: `Parse this project description into a structured task breakdown with dependencies and complexity scores (max ${max_tasks} tasks):\n\n${description}`, context: ctx || undefined, project_dir });
|
|
1801
1143
|
// Build structured task DAG from planner output
|
|
@@ -1858,7 +1200,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1858
1200
|
duration_estimate: planResult.plan?.duration_estimate ?? 'unknown',
|
|
1859
1201
|
};
|
|
1860
1202
|
autoRecord(description, 'task-planner', Math.round(planResult.output.confidence * 100));
|
|
1861
|
-
const hash = createHash('sha256').update(description).digest('hex').slice(0, 16);
|
|
1862
1203
|
const plan_id = saveTaskPlan(JSON.stringify(plan), hash, project_dir);
|
|
1863
1204
|
return { content: [{ type: 'text', text: JSON.stringify({ success: true, plan_id, ...plan }, null, 2) }] };
|
|
1864
1205
|
}
|
|
@@ -2196,9 +1537,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2196
1537
|
throw new Error('veto_benchmark requires task, approach_a, and approach_b');
|
|
2197
1538
|
}
|
|
2198
1539
|
const bmStart = Date.now();
|
|
2199
|
-
// Run both debates
|
|
2200
|
-
const debateA =
|
|
2201
|
-
|
|
1540
|
+
// Run both debates in parallel via LLM council
|
|
1541
|
+
const [debateA, debateB] = await Promise.all([
|
|
1542
|
+
runLlmDebate(server, { task: `${task}\n\nApproach A: ${approachA}`, context: ctx, project_dir: projectDir }),
|
|
1543
|
+
runLlmDebate(server, { task: `${task}\n\nApproach B: ${approachB}`, context: ctx, project_dir: projectDir }),
|
|
1544
|
+
]);
|
|
2202
1545
|
// Score: GREEN=3, YELLOW=2, RED=1, DEADLOCK=0
|
|
2203
1546
|
const verdictScore = { GREEN: 3, YELLOW: 2, RED: 1, DEADLOCK: 0 };
|
|
2204
1547
|
const scoreA = verdictScore[debateA.final_verdict] ?? 0;
|
|
@@ -2266,6 +1609,102 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2266
1609
|
duration_ms: Date.now() - bmStart,
|
|
2267
1610
|
}, null, 2) }] };
|
|
2268
1611
|
}
|
|
1612
|
+
// ── Part 4: New Features ───────────────────────────────────────────────────
|
|
1613
|
+
case 'veto_metrics': {
|
|
1614
|
+
const metrics = getMetrics();
|
|
1615
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...metrics }, null, 2) }] };
|
|
1616
|
+
}
|
|
1617
|
+
case 'veto_git_blame': {
|
|
1618
|
+
const blameDir = args?.project_dir ? String(args.project_dir).trim() : '';
|
|
1619
|
+
const blameFile = args?.file_path ? String(args.file_path).trim() : '';
|
|
1620
|
+
const blameTarget = blameFile || blameDir;
|
|
1621
|
+
if (!blameTarget) {
|
|
1622
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Provide project_dir or file_path.' }) }], isError: true };
|
|
1623
|
+
}
|
|
1624
|
+
const resolvedTarget = resolve(blameTarget);
|
|
1625
|
+
try {
|
|
1626
|
+
statSync(resolvedTarget);
|
|
1627
|
+
}
|
|
1628
|
+
catch {
|
|
1629
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Path not found: ${resolvedTarget}` }) }], isError: true };
|
|
1630
|
+
}
|
|
1631
|
+
function gitExec(cmd, cwd) {
|
|
1632
|
+
try {
|
|
1633
|
+
return execSyncTop(cmd, { cwd, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
|
|
1634
|
+
}
|
|
1635
|
+
catch {
|
|
1636
|
+
return '';
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
const cwd = statSync(resolvedTarget).isDirectory() ? resolvedTarget : dirname(resolvedTarget);
|
|
1640
|
+
const shortlog = gitExec(`git shortlog -sn -- "${resolvedTarget}"`, cwd);
|
|
1641
|
+
const contributors = shortlog.split('\n').filter(Boolean).map(line => {
|
|
1642
|
+
const m = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
1643
|
+
return m ? { commits: parseInt(m[1], 10), author: m[2].trim() } : null;
|
|
1644
|
+
}).filter(Boolean);
|
|
1645
|
+
const lastModified = gitExec(`git log -1 --format="%ai|%aN|%s" -- "${resolvedTarget}"`, cwd);
|
|
1646
|
+
const [last_modified_at, last_author, last_commit_message] = lastModified.split('|');
|
|
1647
|
+
const totalCommits = gitExec(`git rev-list --count HEAD -- "${resolvedTarget}"`, cwd);
|
|
1648
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1649
|
+
success: true,
|
|
1650
|
+
path: resolvedTarget,
|
|
1651
|
+
total_commits: parseInt(totalCommits || '0', 10),
|
|
1652
|
+
contributors,
|
|
1653
|
+
last_modified_at: last_modified_at?.trim(),
|
|
1654
|
+
last_author: last_author?.trim(),
|
|
1655
|
+
last_commit_message: last_commit_message?.trim(),
|
|
1656
|
+
}, null, 2) }] };
|
|
1657
|
+
}
|
|
1658
|
+
case 'veto_changelog': {
|
|
1659
|
+
const changelogDir = args?.project_dir ? String(args.project_dir).trim() : activeProjectDir ?? process.cwd();
|
|
1660
|
+
const maxEntries = typeof args?.max_entries === 'number' ? Math.min(args.max_entries, 200) : 50;
|
|
1661
|
+
const resolvedDir = resolve(changelogDir);
|
|
1662
|
+
try {
|
|
1663
|
+
statSync(resolvedDir);
|
|
1664
|
+
}
|
|
1665
|
+
catch {
|
|
1666
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Directory not found: ${resolvedDir}` }) }], isError: true };
|
|
1667
|
+
}
|
|
1668
|
+
function gitRun(cmd) {
|
|
1669
|
+
try {
|
|
1670
|
+
return execSyncTop(cmd, { cwd: resolvedDir, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
|
|
1671
|
+
}
|
|
1672
|
+
catch {
|
|
1673
|
+
return '';
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const lastTag = gitRun('git describe --tags --abbrev=0 2>/dev/null') || '';
|
|
1677
|
+
const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
|
|
1678
|
+
const rawLog = gitRun(`git log ${range} --format="%s|||%H|||%aN|||%ai" --no-merges -n ${maxEntries}`);
|
|
1679
|
+
if (!rawLog) {
|
|
1680
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, since_tag: lastTag || 'beginning', entries: [], message: 'No commits found in range.' }) }] };
|
|
1681
|
+
}
|
|
1682
|
+
const typeLabels = {
|
|
1683
|
+
feat: 'Features', fix: 'Bug Fixes', refactor: 'Refactoring', perf: 'Performance',
|
|
1684
|
+
docs: 'Documentation', test: 'Tests', chore: 'Chores', ci: 'CI/CD',
|
|
1685
|
+
style: 'Style', build: 'Build', revert: 'Reverts',
|
|
1686
|
+
};
|
|
1687
|
+
const grouped = {};
|
|
1688
|
+
for (const line of rawLog.split('\n').filter(Boolean)) {
|
|
1689
|
+
const [subject, hash, author, date] = line.split('|||');
|
|
1690
|
+
if (!subject)
|
|
1691
|
+
continue;
|
|
1692
|
+
const typeMatch = subject.match(/^(\w+)(\([\w-]+\))?:\s*(.*)/);
|
|
1693
|
+
const type = typeMatch ? typeMatch[1].toLowerCase() : 'other';
|
|
1694
|
+
const msg = typeMatch ? typeMatch[3] : subject;
|
|
1695
|
+
const label = typeLabels[type] ?? 'Other';
|
|
1696
|
+
if (!grouped[label])
|
|
1697
|
+
grouped[label] = [];
|
|
1698
|
+
grouped[label].push({ message: msg.trim(), hash: hash?.trim().slice(0, 8) ?? '', author: author?.trim() ?? '', date: date?.trim().slice(0, 10) ?? '' });
|
|
1699
|
+
}
|
|
1700
|
+
const sections = Object.entries(grouped).map(([section, items]) => ({ section, items }));
|
|
1701
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1702
|
+
success: true,
|
|
1703
|
+
since_tag: lastTag || '(beginning of history)',
|
|
1704
|
+
total_commits: rawLog.split('\n').filter(Boolean).length,
|
|
1705
|
+
sections,
|
|
1706
|
+
}, null, 2) }] };
|
|
1707
|
+
}
|
|
2269
1708
|
default:
|
|
2270
1709
|
throw new Error(`Unknown tool: ${name}`);
|
|
2271
1710
|
}
|