@jigyasudham/veto 1.2.19 → 1.4.0
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 +234 -254
- 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.js +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/llm-council.d.ts +4 -0
- package/dist/council/llm-council.d.ts.map +1 -0
- package/dist/council/llm-council.js +137 -0
- package/dist/council/llm-council.js.map +1 -0
- package/dist/council/types.d.ts +2 -0
- package/dist/council/types.d.ts.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 +189 -784
- package/dist/server.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
|
{
|
|
@@ -990,6 +264,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
990
264
|
connection_type: args?.connection_type ? String(args.connection_type) : 'subscription',
|
|
991
265
|
project_dir: sessionProjectDir,
|
|
992
266
|
token_count: typeof args?.token_count === 'number' ? args.token_count : 0,
|
|
267
|
+
tags: Array.isArray(args?.tags) ? args.tags.map(String) : undefined,
|
|
993
268
|
};
|
|
994
269
|
let result;
|
|
995
270
|
let wasUpdate = false;
|
|
@@ -1007,7 +282,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1007
282
|
result = saveSession(sessionInput);
|
|
1008
283
|
}
|
|
1009
284
|
// Cache for auto-save: future veto_status calls with high token_count will re-save this context
|
|
1010
|
-
|
|
285
|
+
const saveModel = args?.model ? String(args.model) : undefined;
|
|
286
|
+
const resolvedWindow = saveModel ? resolveContextWindow(savePlatform, saveModel) : undefined;
|
|
287
|
+
autoSave.cached = { summary: saveSummary, context: saveContext, task_state: saveTaskState, platform: savePlatform, project_dir: sessionProjectDir, context_window: resolvedWindow };
|
|
1011
288
|
autoSave.last_save_at = result.saved_at;
|
|
1012
289
|
autoSave.last_session_id = result.session_id;
|
|
1013
290
|
const responseObj = {
|
|
@@ -1068,7 +345,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1068
345
|
}
|
|
1069
346
|
case 'veto_sessions_list': {
|
|
1070
347
|
const limit = Math.min(typeof args?.limit === 'number' ? args.limit : 10, 50);
|
|
1071
|
-
const
|
|
348
|
+
const query = args?.query ? String(args.query).trim() : undefined;
|
|
349
|
+
const sessions = listSessions(limit, query);
|
|
1072
350
|
return {
|
|
1073
351
|
content: [
|
|
1074
352
|
{
|
|
@@ -1083,6 +361,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1083
361
|
project_dir: s.project_dir,
|
|
1084
362
|
summary: s.summary,
|
|
1085
363
|
token_count: s.token_count,
|
|
364
|
+
tags: s.tags ? JSON.parse(s.tags) : [],
|
|
1086
365
|
})),
|
|
1087
366
|
}, null, 2),
|
|
1088
367
|
},
|
|
@@ -1127,11 +406,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1127
406
|
isError: true,
|
|
1128
407
|
};
|
|
1129
408
|
}
|
|
409
|
+
const strictnessArg = (['fast', 'standard', 'strict'].includes(String(args?.strictness ?? '')))
|
|
410
|
+
? String(args.strictness)
|
|
411
|
+
: 'standard';
|
|
1130
412
|
const debateStart = Date.now();
|
|
1131
|
-
const result =
|
|
413
|
+
const result = await runLlmDebate(server, {
|
|
1132
414
|
task,
|
|
1133
415
|
context: args?.context ? String(args.context) : undefined,
|
|
1134
416
|
project_dir: args?.project_dir ? String(args.project_dir) : undefined,
|
|
417
|
+
strictness: strictnessArg,
|
|
1135
418
|
});
|
|
1136
419
|
const debateDuration = Date.now() - debateStart;
|
|
1137
420
|
const sessionId = args?.session_id ? String(args.session_id) : undefined;
|
|
@@ -1156,22 +439,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1156
439
|
recordOutcome(task.slice(0, 50), 50, tMap[result.final_verdict] ?? 2, 'council', qMap[result.final_verdict] ?? 50);
|
|
1157
440
|
}
|
|
1158
441
|
// Auto-store RED verdicts so they appear in the Memory panel immediately
|
|
1159
|
-
if (result.final_verdict === 'RED') {
|
|
442
|
+
if (result.final_verdict === 'RED' || (result.final_verdict === 'YELLOW' && (result.warnings.length >= 2 || result.block_reasons.length > 0))) {
|
|
443
|
+
const isRed = result.final_verdict === 'RED';
|
|
1160
444
|
const lines = [`Task: ${task}`];
|
|
1161
445
|
if (result.block_reasons.length > 0)
|
|
1162
446
|
lines.push(`\nBlocked by:\n${result.block_reasons.map(r => `- ${r}`).join('\n')}`);
|
|
1163
447
|
if (result.warnings.length > 0)
|
|
1164
448
|
lines.push(`\nWarnings:\n${result.warnings.map(w => `- ${w}`).join('\n')}`);
|
|
449
|
+
// Include per-agent reasoning so future debates inherit the full context
|
|
450
|
+
const agentSummary = Object.entries(result.votes)
|
|
451
|
+
.filter(([, v]) => v.verdict !== 'approve')
|
|
452
|
+
.map(([name, v]) => `- ${name} [${v.verdict}]: ${v.reason}`)
|
|
453
|
+
.join('\n');
|
|
454
|
+
if (agentSummary)
|
|
455
|
+
lines.push(`\nAgent reasoning:\n${agentSummary}`);
|
|
1165
456
|
if (result.recommended)
|
|
1166
457
|
lines.push(`\nRecommended: ${result.recommended}`);
|
|
1167
458
|
storeKnowledge({
|
|
1168
459
|
type: 'decision',
|
|
1169
|
-
title:
|
|
460
|
+
title: `${result.final_verdict}: ${task.slice(0, 80)}`,
|
|
1170
461
|
content: lines.join(''),
|
|
1171
|
-
tags: ['red-verdict', 'blocked', 'council'],
|
|
462
|
+
tags: [isRed ? 'red-verdict' : 'yellow-verdict', isRed ? 'blocked' : 'caution', 'council'],
|
|
1172
463
|
project_dir: args?.project_dir ? String(args.project_dir) : undefined,
|
|
1173
464
|
session_id: sessionId,
|
|
1174
|
-
relevance: 1.0,
|
|
465
|
+
relevance: isRed ? 1.0 : 0.8,
|
|
1175
466
|
});
|
|
1176
467
|
}
|
|
1177
468
|
const responsePayload = {
|
|
@@ -1182,13 +473,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1182
473
|
recommended: result.recommended,
|
|
1183
474
|
debated_at: result.debated_at,
|
|
1184
475
|
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
|
|
476
|
+
lead_dev: result.votes.lead_dev,
|
|
477
|
+
pm: result.votes.pm,
|
|
478
|
+
architect: result.votes.architect,
|
|
479
|
+
ux: result.votes.ux,
|
|
480
|
+
devil: result.votes.devil,
|
|
481
|
+
legal: result.votes.legal,
|
|
482
|
+
security: result.votes.security,
|
|
1192
483
|
},
|
|
1193
484
|
};
|
|
1194
485
|
const fullText = result.formatted_output + '\n\n' + JSON.stringify(responsePayload, null, 2);
|
|
@@ -1719,42 +1010,53 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1719
1010
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1720
1011
|
}
|
|
1721
1012
|
case 'veto_explain': {
|
|
1722
|
-
const filePath =
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1013
|
+
const filePath = args?.file_path ? String(args.file_path).trim() : '';
|
|
1014
|
+
const rawText = args?.text ? String(args.text).trim() : '';
|
|
1015
|
+
const depth = String(args?.depth ?? 'overview');
|
|
1016
|
+
const userContext = args?.context ? String(args.context) : undefined;
|
|
1017
|
+
if (!filePath && !rawText) {
|
|
1018
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Provide file_path or text.' }) }], isError: true };
|
|
1728
1019
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
1020
|
+
let fileContent;
|
|
1021
|
+
let agent;
|
|
1022
|
+
let taskLabel;
|
|
1023
|
+
if (rawText) {
|
|
1024
|
+
fileContent = rawText;
|
|
1025
|
+
// Auto-detect agent from content: error messages → debugger, stack traces → debugger, code → coder
|
|
1026
|
+
const looksLikeError = /error|exception|traceback|stack trace|at \w+\.|TypeError|SyntaxError|ENOENT/i.test(rawText);
|
|
1027
|
+
agent = looksLikeError ? 'debugger' : 'coder';
|
|
1028
|
+
taskLabel = looksLikeError ? 'raw error/stack trace' : 'raw text input';
|
|
1731
1029
|
}
|
|
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))
|
|
1030
|
+
else {
|
|
1031
|
+
try {
|
|
1032
|
+
fileContent = readFileSync(filePath, 'utf8');
|
|
1033
|
+
}
|
|
1034
|
+
catch {
|
|
1035
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Cannot read file: ${filePath}` }) }], isError: true };
|
|
1036
|
+
}
|
|
1037
|
+
const ext = extname(filePath).toLowerCase();
|
|
1038
|
+
const name_ = basename(filePath).toLowerCase();
|
|
1750
1039
|
agent = 'coder';
|
|
1751
|
-
|
|
1752
|
-
|
|
1040
|
+
if (['.tsx', '.jsx', '.vue', '.svelte'].includes(ext))
|
|
1041
|
+
agent = 'frontend';
|
|
1042
|
+
else if (['.sql', '.prisma'].includes(ext) || name_.includes('schema'))
|
|
1043
|
+
agent = 'database';
|
|
1044
|
+
else if (/\.(test|spec)\.(ts|js|tsx|jsx)$/.test(name_))
|
|
1045
|
+
agent = 'tester';
|
|
1046
|
+
else if (['.yaml', '.yml', '.toml', '.dockerfile'].includes(ext) || name_ === 'dockerfile')
|
|
1047
|
+
agent = 'devops';
|
|
1048
|
+
else if (name_.includes('auth') || name_.includes('login') || name_.includes('jwt') || name_.includes('token'))
|
|
1049
|
+
agent = 'auth';
|
|
1050
|
+
else if (name_.includes('security') || name_.includes('crypt'))
|
|
1051
|
+
agent = 'security-scanner';
|
|
1052
|
+
taskLabel = `${ext} file: ${basename(filePath)}`;
|
|
1053
|
+
}
|
|
1054
|
+
const task = `Explain this ${taskLabel} at ${depth} depth.${userContext ? ` Focus: ${userContext}` : ''}`;
|
|
1753
1055
|
const result = await executeOne({ id: 'explain-1', agent, task, code: fileContent, project_dir: undefined });
|
|
1754
|
-
autoRecord(`explain ${
|
|
1056
|
+
autoRecord(`explain ${taskLabel}`, agent, Math.round(result.output.confidence * 100));
|
|
1755
1057
|
return {
|
|
1756
1058
|
content: [{ type: 'text', text: JSON.stringify({
|
|
1757
|
-
|
|
1059
|
+
source: filePath || 'raw text', agent_used: agent, depth,
|
|
1758
1060
|
explanation: result.plan ?? result.analysis,
|
|
1759
1061
|
output: result.output,
|
|
1760
1062
|
}, null, 2) }],
|
|
@@ -1796,6 +1098,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1796
1098
|
if (!description) {
|
|
1797
1099
|
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'description is required.' }) }], isError: true };
|
|
1798
1100
|
}
|
|
1101
|
+
const hash = createHash('sha256').update(description).digest('hex').slice(0, 16);
|
|
1102
|
+
const cached = getTaskPlan(hash);
|
|
1103
|
+
if (cached) {
|
|
1104
|
+
const plan = JSON.parse(cached.plan_json);
|
|
1105
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, plan_id: cached.id, cached: true, ...plan }, null, 2) }] };
|
|
1106
|
+
}
|
|
1799
1107
|
const ctx = project_dir ? buildContextString(project_dir) : '';
|
|
1800
1108
|
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
1109
|
// Build structured task DAG from planner output
|
|
@@ -1858,7 +1166,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1858
1166
|
duration_estimate: planResult.plan?.duration_estimate ?? 'unknown',
|
|
1859
1167
|
};
|
|
1860
1168
|
autoRecord(description, 'task-planner', Math.round(planResult.output.confidence * 100));
|
|
1861
|
-
const hash = createHash('sha256').update(description).digest('hex').slice(0, 16);
|
|
1862
1169
|
const plan_id = saveTaskPlan(JSON.stringify(plan), hash, project_dir);
|
|
1863
1170
|
return { content: [{ type: 'text', text: JSON.stringify({ success: true, plan_id, ...plan }, null, 2) }] };
|
|
1864
1171
|
}
|
|
@@ -2196,9 +1503,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2196
1503
|
throw new Error('veto_benchmark requires task, approach_a, and approach_b');
|
|
2197
1504
|
}
|
|
2198
1505
|
const bmStart = Date.now();
|
|
2199
|
-
// Run both debates
|
|
2200
|
-
const debateA =
|
|
2201
|
-
|
|
1506
|
+
// Run both debates in parallel via LLM council
|
|
1507
|
+
const [debateA, debateB] = await Promise.all([
|
|
1508
|
+
runLlmDebate(server, { task: `${task}\n\nApproach A: ${approachA}`, context: ctx, project_dir: projectDir }),
|
|
1509
|
+
runLlmDebate(server, { task: `${task}\n\nApproach B: ${approachB}`, context: ctx, project_dir: projectDir }),
|
|
1510
|
+
]);
|
|
2202
1511
|
// Score: GREEN=3, YELLOW=2, RED=1, DEADLOCK=0
|
|
2203
1512
|
const verdictScore = { GREEN: 3, YELLOW: 2, RED: 1, DEADLOCK: 0 };
|
|
2204
1513
|
const scoreA = verdictScore[debateA.final_verdict] ?? 0;
|
|
@@ -2266,6 +1575,102 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2266
1575
|
duration_ms: Date.now() - bmStart,
|
|
2267
1576
|
}, null, 2) }] };
|
|
2268
1577
|
}
|
|
1578
|
+
// ── Part 4: New Features ───────────────────────────────────────────────────
|
|
1579
|
+
case 'veto_metrics': {
|
|
1580
|
+
const metrics = getMetrics();
|
|
1581
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...metrics }, null, 2) }] };
|
|
1582
|
+
}
|
|
1583
|
+
case 'veto_git_blame': {
|
|
1584
|
+
const blameDir = args?.project_dir ? String(args.project_dir).trim() : '';
|
|
1585
|
+
const blameFile = args?.file_path ? String(args.file_path).trim() : '';
|
|
1586
|
+
const blameTarget = blameFile || blameDir;
|
|
1587
|
+
if (!blameTarget) {
|
|
1588
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'Provide project_dir or file_path.' }) }], isError: true };
|
|
1589
|
+
}
|
|
1590
|
+
const resolvedTarget = resolve(blameTarget);
|
|
1591
|
+
try {
|
|
1592
|
+
statSync(resolvedTarget);
|
|
1593
|
+
}
|
|
1594
|
+
catch {
|
|
1595
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Path not found: ${resolvedTarget}` }) }], isError: true };
|
|
1596
|
+
}
|
|
1597
|
+
function gitExec(cmd, cwd) {
|
|
1598
|
+
try {
|
|
1599
|
+
return execSyncTop(cmd, { cwd, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
|
|
1600
|
+
}
|
|
1601
|
+
catch {
|
|
1602
|
+
return '';
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const cwd = statSync(resolvedTarget).isDirectory() ? resolvedTarget : dirname(resolvedTarget);
|
|
1606
|
+
const shortlog = gitExec(`git shortlog -sn -- "${resolvedTarget}"`, cwd);
|
|
1607
|
+
const contributors = shortlog.split('\n').filter(Boolean).map(line => {
|
|
1608
|
+
const m = line.match(/^\s*(\d+)\s+(.+)$/);
|
|
1609
|
+
return m ? { commits: parseInt(m[1], 10), author: m[2].trim() } : null;
|
|
1610
|
+
}).filter(Boolean);
|
|
1611
|
+
const lastModified = gitExec(`git log -1 --format="%ai|%aN|%s" -- "${resolvedTarget}"`, cwd);
|
|
1612
|
+
const [last_modified_at, last_author, last_commit_message] = lastModified.split('|');
|
|
1613
|
+
const totalCommits = gitExec(`git rev-list --count HEAD -- "${resolvedTarget}"`, cwd);
|
|
1614
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1615
|
+
success: true,
|
|
1616
|
+
path: resolvedTarget,
|
|
1617
|
+
total_commits: parseInt(totalCommits || '0', 10),
|
|
1618
|
+
contributors,
|
|
1619
|
+
last_modified_at: last_modified_at?.trim(),
|
|
1620
|
+
last_author: last_author?.trim(),
|
|
1621
|
+
last_commit_message: last_commit_message?.trim(),
|
|
1622
|
+
}, null, 2) }] };
|
|
1623
|
+
}
|
|
1624
|
+
case 'veto_changelog': {
|
|
1625
|
+
const changelogDir = args?.project_dir ? String(args.project_dir).trim() : activeProjectDir ?? process.cwd();
|
|
1626
|
+
const maxEntries = typeof args?.max_entries === 'number' ? Math.min(args.max_entries, 200) : 50;
|
|
1627
|
+
const resolvedDir = resolve(changelogDir);
|
|
1628
|
+
try {
|
|
1629
|
+
statSync(resolvedDir);
|
|
1630
|
+
}
|
|
1631
|
+
catch {
|
|
1632
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: `Directory not found: ${resolvedDir}` }) }], isError: true };
|
|
1633
|
+
}
|
|
1634
|
+
function gitRun(cmd) {
|
|
1635
|
+
try {
|
|
1636
|
+
return execSyncTop(cmd, { cwd: resolvedDir, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
|
|
1637
|
+
}
|
|
1638
|
+
catch {
|
|
1639
|
+
return '';
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
const lastTag = gitRun('git describe --tags --abbrev=0 2>/dev/null') || '';
|
|
1643
|
+
const range = lastTag ? `${lastTag}..HEAD` : 'HEAD';
|
|
1644
|
+
const rawLog = gitRun(`git log ${range} --format="%s|||%H|||%aN|||%ai" --no-merges -n ${maxEntries}`);
|
|
1645
|
+
if (!rawLog) {
|
|
1646
|
+
return { content: [{ type: 'text', text: JSON.stringify({ success: true, since_tag: lastTag || 'beginning', entries: [], message: 'No commits found in range.' }) }] };
|
|
1647
|
+
}
|
|
1648
|
+
const typeLabels = {
|
|
1649
|
+
feat: 'Features', fix: 'Bug Fixes', refactor: 'Refactoring', perf: 'Performance',
|
|
1650
|
+
docs: 'Documentation', test: 'Tests', chore: 'Chores', ci: 'CI/CD',
|
|
1651
|
+
style: 'Style', build: 'Build', revert: 'Reverts',
|
|
1652
|
+
};
|
|
1653
|
+
const grouped = {};
|
|
1654
|
+
for (const line of rawLog.split('\n').filter(Boolean)) {
|
|
1655
|
+
const [subject, hash, author, date] = line.split('|||');
|
|
1656
|
+
if (!subject)
|
|
1657
|
+
continue;
|
|
1658
|
+
const typeMatch = subject.match(/^(\w+)(\([\w-]+\))?:\s*(.*)/);
|
|
1659
|
+
const type = typeMatch ? typeMatch[1].toLowerCase() : 'other';
|
|
1660
|
+
const msg = typeMatch ? typeMatch[3] : subject;
|
|
1661
|
+
const label = typeLabels[type] ?? 'Other';
|
|
1662
|
+
if (!grouped[label])
|
|
1663
|
+
grouped[label] = [];
|
|
1664
|
+
grouped[label].push({ message: msg.trim(), hash: hash?.trim().slice(0, 8) ?? '', author: author?.trim() ?? '', date: date?.trim().slice(0, 10) ?? '' });
|
|
1665
|
+
}
|
|
1666
|
+
const sections = Object.entries(grouped).map(([section, items]) => ({ section, items }));
|
|
1667
|
+
return { content: [{ type: 'text', text: JSON.stringify({
|
|
1668
|
+
success: true,
|
|
1669
|
+
since_tag: lastTag || '(beginning of history)',
|
|
1670
|
+
total_commits: rawLog.split('\n').filter(Boolean).length,
|
|
1671
|
+
sections,
|
|
1672
|
+
}, null, 2) }] };
|
|
1673
|
+
}
|
|
2269
1674
|
default:
|
|
2270
1675
|
throw new Error(`Unknown tool: ${name}`);
|
|
2271
1676
|
}
|