@jigyasudham/veto 0.8.2 → 1.0.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.
Files changed (111) hide show
  1. package/README.md +209 -52
  2. package/dist/agents/executor.js +36 -3
  3. package/dist/cli.js +350 -51
  4. package/dist/context/reader.js +113 -0
  5. package/dist/council/index.js +3 -1
  6. package/dist/plugins/loader.js +49 -0
  7. package/dist/router/index.js +2 -2
  8. package/dist/router/learning-updater.js +45 -1
  9. package/dist/server.js +478 -14
  10. package/dist/watcher/index.js +77 -0
  11. package/dist/workflow/pipeline.js +64 -0
  12. package/package.json +12 -3
  13. package/.claude/settings.local.json +0 -9
  14. package/src/adapters/claude.ts +0 -70
  15. package/src/adapters/codex.ts +0 -71
  16. package/src/adapters/gemini.ts +0 -71
  17. package/src/adapters/index.ts +0 -217
  18. package/src/agents/development/api.ts +0 -120
  19. package/src/agents/development/backend.ts +0 -85
  20. package/src/agents/development/coder.ts +0 -213
  21. package/src/agents/development/database.ts +0 -83
  22. package/src/agents/development/debugger.ts +0 -238
  23. package/src/agents/development/devops.ts +0 -86
  24. package/src/agents/development/frontend.ts +0 -85
  25. package/src/agents/development/migration.ts +0 -144
  26. package/src/agents/development/performance.ts +0 -144
  27. package/src/agents/development/refactor.ts +0 -86
  28. package/src/agents/development/reviewer.ts +0 -268
  29. package/src/agents/development/tester.ts +0 -151
  30. package/src/agents/executor.ts +0 -158
  31. package/src/agents/memory/context-manager.ts +0 -171
  32. package/src/agents/memory/decision-logger.ts +0 -160
  33. package/src/agents/memory/knowledge-base.ts +0 -124
  34. package/src/agents/memory/pattern-learner.ts +0 -143
  35. package/src/agents/memory/project-mapper.ts +0 -118
  36. package/src/agents/quality/accessibility.ts +0 -99
  37. package/src/agents/quality/code-quality.ts +0 -115
  38. package/src/agents/quality/compatibility.ts +0 -58
  39. package/src/agents/quality/documentation.ts +0 -105
  40. package/src/agents/quality/error-handling.ts +0 -96
  41. package/src/agents/research/competitor-analyzer.ts +0 -45
  42. package/src/agents/research/cost-analyzer.ts +0 -54
  43. package/src/agents/research/estimator.ts +0 -60
  44. package/src/agents/research/ethics-bias.ts +0 -113
  45. package/src/agents/research/researcher.ts +0 -114
  46. package/src/agents/research/risk-assessor.ts +0 -63
  47. package/src/agents/research/tech-advisor.ts +0 -55
  48. package/src/agents/security/auth.ts +0 -287
  49. package/src/agents/security/dependency-audit.ts +0 -337
  50. package/src/agents/security/penetration.ts +0 -262
  51. package/src/agents/security/privacy.ts +0 -285
  52. package/src/agents/security/scanner.ts +0 -322
  53. package/src/agents/security/secrets.ts +0 -249
  54. package/src/agents/types.ts +0 -66
  55. package/src/agents/workflow/automation.ts +0 -59
  56. package/src/agents/workflow/file-manager.ts +0 -52
  57. package/src/agents/workflow/git-agent.ts +0 -55
  58. package/src/agents/workflow/reporter.ts +0 -51
  59. package/src/agents/workflow/search-agent.ts +0 -40
  60. package/src/agents/workflow/task-coordinator.ts +0 -41
  61. package/src/agents/workflow/task-planner.ts +0 -47
  62. package/src/cli.ts +0 -135
  63. package/src/council/decision-engine.ts +0 -171
  64. package/src/council/devil-advocate.ts +0 -116
  65. package/src/council/index.ts +0 -44
  66. package/src/council/lead-developer.ts +0 -118
  67. package/src/council/legal-compliance.ts +0 -152
  68. package/src/council/product-manager.ts +0 -102
  69. package/src/council/security.ts +0 -172
  70. package/src/council/system-architect.ts +0 -132
  71. package/src/council/types.ts +0 -33
  72. package/src/council/ux-designer.ts +0 -121
  73. package/src/memory/local.ts +0 -305
  74. package/src/memory/schema.ts +0 -174
  75. package/src/memory/sync.ts +0 -274
  76. package/src/router/complexity-scorer.ts +0 -96
  77. package/src/router/context-compressor.ts +0 -74
  78. package/src/router/index.ts +0 -60
  79. package/src/router/learning-updater.ts +0 -271
  80. package/src/router/model-selector.ts +0 -83
  81. package/src/router/rate-monitor.ts +0 -103
  82. package/src/server.ts +0 -1038
  83. package/src/skills/development/skill-api-design.ts +0 -329
  84. package/src/skills/development/skill-auth.ts +0 -271
  85. package/src/skills/development/skill-ci-cd.ts +0 -0
  86. package/src/skills/development/skill-crud.ts +0 -209
  87. package/src/skills/development/skill-db-schema.ts +0 -0
  88. package/src/skills/development/skill-docker.ts +0 -0
  89. package/src/skills/development/skill-env-setup.ts +0 -0
  90. package/src/skills/development/skill-scaffold.ts +0 -323
  91. package/src/skills/intelligence/skill-complexity-score.ts +0 -69
  92. package/src/skills/intelligence/skill-cost-track.ts +0 -39
  93. package/src/skills/intelligence/skill-learning-loop.ts +0 -69
  94. package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
  95. package/src/skills/intelligence/skill-rate-watch.ts +0 -61
  96. package/src/skills/memory/skill-context-compress.ts +0 -98
  97. package/src/skills/memory/skill-cross-sync.ts +0 -104
  98. package/src/skills/memory/skill-decision-log.ts +0 -119
  99. package/src/skills/memory/skill-session-restore.ts +0 -59
  100. package/src/skills/memory/skill-session-save.ts +0 -94
  101. package/src/skills/quality/skill-accessibility.ts +0 -0
  102. package/src/skills/quality/skill-code-review.ts +0 -84
  103. package/src/skills/quality/skill-docs-gen.ts +0 -0
  104. package/src/skills/quality/skill-perf-audit.ts +0 -0
  105. package/src/skills/quality/skill-security-scan.ts +0 -91
  106. package/src/skills/quality/skill-test-suite.ts +0 -290
  107. package/src/skills/workflow/skill-deploy.ts +0 -0
  108. package/src/skills/workflow/skill-git-workflow.ts +0 -0
  109. package/src/skills/workflow/skill-rollback.ts +0 -0
  110. package/src/skills/workflow/skill-task-breakdown.ts +0 -0
  111. package/tsconfig.json +0 -20
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
  // Veto CLI — entry point for `npx veto init`
3
3
  // Suppress Node experimental warnings (node:sqlite) for clean UX
4
4
  process.removeAllListeners('warning');
5
- import { mkdirSync, existsSync } from 'node:fs';
6
- import { join } from 'node:path';
5
+ import { mkdirSync, existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
6
+ import { join, dirname, extname, resolve } from 'node:path';
7
7
  import { homedir } from 'node:os';
8
- const VERSION = '0.8.0';
8
+ const VERSION = '1.0.0';
9
9
  const VETO_DIR = join(homedir(), '.veto');
10
- // Simple inline colors (no chalk needed for the init wizard to avoid ESM issues)
10
+ const HOME = homedir();
11
11
  const c = {
12
12
  bold: (s) => `\x1b[1m${s}\x1b[0m`,
13
13
  green: (s) => `\x1b[32m${s}\x1b[0m`,
@@ -25,10 +25,74 @@ function printBanner() {
25
25
  console.log(c.bold(c.cyan(' ╚████╔╝ ███████╗ ██║ ╚██████╔╝')));
26
26
  console.log(c.bold(c.cyan(' ╚═══╝ ╚══════╝ ╚═╝ ╚═════╝')));
27
27
  console.log('');
28
- console.log(c.dim(` 50 agents. 28 skills. 3 AIs. Self-learning. Zero extra cost.`));
28
+ console.log(c.dim(` 50 agents. 33 tools. 3 AIs. Self-learning. Zero extra cost.`));
29
29
  console.log(c.dim(` v${VERSION}`));
30
30
  console.log('');
31
31
  }
32
+ // Merge veto entry into an existing JSON config file, creating it if needed.
33
+ // Supports both "mcpServers" format (Claude/Gemini/Codex/Cursor/Windsurf)
34
+ // and "servers" format (VS Code).
35
+ function writeVetoConfig(configPath, format) {
36
+ let existing = {};
37
+ if (existsSync(configPath)) {
38
+ try {
39
+ existing = JSON.parse(readFileSync(configPath, 'utf8'));
40
+ }
41
+ catch {
42
+ // Unreadable / invalid JSON — skip to avoid corrupting it
43
+ return 'skipped';
44
+ }
45
+ }
46
+ else {
47
+ mkdirSync(dirname(configPath), { recursive: true });
48
+ }
49
+ const wasEmpty = Object.keys(existing).length === 0;
50
+ if (format === 'mcpServers') {
51
+ const servers = existing.mcpServers ?? {};
52
+ servers['veto'] = { command: 'veto-server' };
53
+ existing.mcpServers = servers;
54
+ }
55
+ else {
56
+ const servers = existing.servers ?? {};
57
+ servers['veto'] = { type: 'stdio', command: 'veto-server' };
58
+ existing.servers = servers;
59
+ }
60
+ writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
61
+ return wasEmpty ? 'created' : 'updated';
62
+ }
63
+ // All platforms Veto supports, with their config paths and formats.
64
+ const PLATFORMS = [
65
+ {
66
+ name: 'Claude Code',
67
+ path: join(HOME, '.claude', 'mcp_servers.json'),
68
+ format: 'mcpServers',
69
+ detectionDir: join(HOME, '.claude'),
70
+ },
71
+ {
72
+ name: 'Gemini CLI',
73
+ path: join(HOME, '.gemini', 'settings.json'),
74
+ format: 'mcpServers',
75
+ detectionDir: join(HOME, '.gemini'),
76
+ },
77
+ {
78
+ name: 'Codex CLI',
79
+ path: join(HOME, '.codex', 'config.json'),
80
+ format: 'mcpServers',
81
+ detectionDir: join(HOME, '.codex'),
82
+ },
83
+ {
84
+ name: 'Cursor',
85
+ path: join(HOME, '.cursor', 'mcp.json'),
86
+ format: 'mcpServers',
87
+ detectionDir: join(HOME, '.cursor'),
88
+ },
89
+ {
90
+ name: 'Windsurf',
91
+ path: join(HOME, '.codeium', 'windsurf', 'mcp_config.json'),
92
+ format: 'mcpServers',
93
+ detectionDir: join(HOME, '.codeium', 'windsurf'),
94
+ },
95
+ ];
32
96
  async function initCommand() {
33
97
  printBanner();
34
98
  // 1. Create ~/.veto directory
@@ -39,13 +103,12 @@ async function initCommand() {
39
103
  else {
40
104
  console.log(c.dim(' · ') + `Found existing ${VETO_DIR}`);
41
105
  }
42
- // 2. Initialize SQLite database (imports local.ts which auto-creates tables)
106
+ // 2. Initialize SQLite database
43
107
  process.stdout.write(' · Initializing SQLite database...');
44
108
  const { getDb, getDbPath, saveSession } = await import('./memory/local.js');
45
109
  try {
46
110
  const db = getDb();
47
111
  const dbPath = getDbPath();
48
- // Smoke test: save + retrieve a test session
49
112
  const { session_id } = saveSession({
50
113
  platform: 'claude',
51
114
  summary: 'Veto initialized',
@@ -54,7 +117,6 @@ async function initCommand() {
54
117
  const row = db.prepare('SELECT id FROM sessions WHERE id = ?').get(session_id);
55
118
  if (!row)
56
119
  throw new Error('DB smoke test failed');
57
- // Clean up test row
58
120
  db.prepare('DELETE FROM sessions WHERE id = ?').run(session_id);
59
121
  console.log(c.green(' ✓'));
60
122
  console.log(c.green(' ✓') + ` Database ready at ${dbPath}`);
@@ -65,47 +127,255 @@ async function initCommand() {
65
127
  console.error(c.red(` Error initializing database: ${msg}`));
66
128
  process.exit(1);
67
129
  }
68
- // 3. Print config snippet using the stable "veto-server" bin command
69
- console.log('');
70
- console.log(c.bold(' ┌─ Add Veto to your AI CLI or IDE ───────────────────────────────┐'));
71
- console.log(c.bold(' │') + ' ' + c.bold('│'));
72
- console.log(c.bold(' │') + ' Config file locations: ' + c.bold('│'));
73
- console.log(c.bold(' │') + ' ' + c.bold('│'));
74
- console.log(c.bold(' │') + c.dim(' Claude Code → ~/.claude/mcp_servers.json') + ' ' + c.bold('│'));
75
- console.log(c.bold(' │') + c.dim(' Gemini CLI → ~/.gemini/settings.json') + ' ' + c.bold('│'));
76
- console.log(c.bold('') + c.dim(' Codex CLI → ~/.codex/config.json') + ' ' + c.bold('│'));
77
- console.log(c.bold(' │') + c.dim(' Cursor → ~/.cursor/mcp.json') + ' ' + c.bold('│'));
78
- console.log(c.bold(' │') + c.dim(' Windsurf → ~/.codeium/windsurf/mcp_config.json') + ' ' + c.bold('│'));
79
- console.log(c.bold(' │') + ' ' + c.bold('│'));
80
- console.log(c.bold(' │') + ' Claude / Gemini / Codex / Cursor / Windsurf: ' + c.bold('│'));
81
- console.log(c.bold(' │') + ' ' + c.bold('│'));
82
- console.log(c.bold('') + c.cyan(' {') + ' ' + c.bold('│'));
83
- console.log(c.bold(' │') + c.cyan(' "mcpServers": {') + ' ' + c.bold('│'));
84
- console.log(c.bold('') + c.cyan(' "veto": {') + ' ' + c.bold('│'));
85
- console.log(c.bold(' │') + c.cyan(' "command": "veto-server"') + ' ' + c.bold('│'));
86
- console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
87
- console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
88
- console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
89
- console.log(c.bold(' │') + ' ' + c.bold('│'));
90
- console.log(c.bold(' ') + ' VS Code .vscode/mcp.json: ' + c.bold('│'));
91
- console.log(c.bold(' │') + ' ' + c.bold('│'));
92
- console.log(c.bold(' │') + c.cyan(' {') + ' ' + c.bold('│'));
93
- console.log(c.bold(' │') + c.cyan(' "servers": {') + ' ' + c.bold('│'));
94
- console.log(c.bold(' │') + c.cyan(' "veto": {') + ' ' + c.bold('│'));
95
- console.log(c.bold(' ') + c.cyan(' "type": "stdio",') + ' ' + c.bold('│'));
96
- console.log(c.bold(' │') + c.cyan(' "command": "veto-server"') + ' ' + c.bold('│'));
97
- console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
98
- console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
99
- console.log(c.bold(' ') + c.cyan(' }') + ' ' + c.bold('│'));
100
- console.log(c.bold(' │') + ' ' + c.bold('│'));
101
- console.log(c.bold(' └────────────────────────────────────────────────────────────────┘'));
102
- console.log('');
103
- console.log(c.green(' ✓ Veto is ready!'));
104
- console.log('');
105
- console.log(' Next steps:');
106
- console.log(c.dim(' 1.') + ' Add the config above to your AI CLI or IDE config file');
107
- console.log(c.dim(' 2.') + ' Restart the CLI or IDE');
108
- console.log(c.dim(' 3.') + ' Run: veto_status — should return { "status": "running", "version": "' + VERSION + '" }');
130
+ // 3. Auto-scan current project and store project map
131
+ const cwd = resolve(process.cwd());
132
+ const { getDb: _getDb, updateProjectMap } = await import('./memory/local.js');
133
+ try {
134
+ process.stdout.write(' · Scanning project directory...');
135
+ const { structure, key_modules, tech_stack } = scanProjectDir(cwd);
136
+ updateProjectMap({ project_dir: cwd, structure: JSON.stringify(structure), key_modules, tech_stack });
137
+ const stackStr = tech_stack.length ? ` (${tech_stack.slice(0, 4).join(', ')})` : '';
138
+ console.log(c.green('') + ` Project map saved${stackStr}`);
139
+ }
140
+ catch {
141
+ console.log(c.dim(' skipped'));
142
+ }
143
+ // 4. Auto-configure every AI CLI / IDE found on this machine
144
+ console.log('');
145
+ console.log(' Configuring all AI tools found on this machine...');
146
+ console.log('');
147
+ let configured = 0;
148
+ let skipped = 0;
149
+ for (const platform of PLATFORMS) {
150
+ const detected = existsSync(platform.detectionDir);
151
+ if (!detected) {
152
+ console.log(c.dim(' · ') + c.dim(`${platform.name} not installed, skipping`));
153
+ continue;
154
+ }
155
+ const result = writeVetoConfig(platform.path, platform.format);
156
+ if (result === 'skipped') {
157
+ console.log(c.yellow(' ') + `${platform.name} config unreadable, skipped`);
158
+ skipped++;
159
+ }
160
+ else if (result === 'created') {
161
+ console.log(c.green(' ') + `${platform.name} configured`);
162
+ configured++;
163
+ }
164
+ else {
165
+ console.log(c.green(' ✓ ') + `${platform.name} — updated`);
166
+ configured++;
167
+ }
168
+ }
169
+ console.log('');
170
+ if (configured === 0 && skipped === 0) {
171
+ console.log(c.yellow(' ⚠ No AI tools detected.'));
172
+ console.log(' Install Claude Code, Gemini CLI, or Codex CLI and run veto init again.');
173
+ console.log('');
174
+ }
175
+ else {
176
+ console.log(c.green(` ✓ Veto configured for ${configured} tool${configured !== 1 ? 's' : ''}!`));
177
+ console.log('');
178
+ console.log(' Next steps:');
179
+ console.log(c.dim(' 1.') + ' Restart your AI CLI or IDE');
180
+ console.log(c.dim(' 2.') + ' Run: veto_status — should return { "status": "running", "version": "' + VERSION + '" }');
181
+ console.log('');
182
+ }
183
+ }
184
+ // ─── Project Map Scanner ────────────────────────────────────────────────────────
185
+ function scanProjectDir(dir) {
186
+ const structure = {};
187
+ const key_modules = [];
188
+ const tech_stack = [];
189
+ // Read package.json for stack detection
190
+ const pkgPath = join(dir, 'package.json');
191
+ if (existsSync(pkgPath)) {
192
+ try {
193
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
194
+ structure['name'] = pkg.name;
195
+ structure['version'] = pkg.version;
196
+ const allDeps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
197
+ structure['dep_count'] = allDeps.length;
198
+ const stackMap = [
199
+ [['next'], 'Next.js'], [['react'], 'React'], [['vue'], 'Vue'],
200
+ [['express'], 'Express'], [['fastify'], 'Fastify'], [['hono'], 'Hono'],
201
+ [['prisma'], 'Prisma'], [['drizzle-orm'], 'Drizzle'], [['typeorm'], 'TypeORM'],
202
+ [['@modelcontextprotocol/sdk'], 'MCP'], [['typescript'], 'TypeScript'],
203
+ [['jest'], 'Jest'], [['vitest'], 'Vitest'], [['tailwindcss'], 'Tailwind'],
204
+ [['zod'], 'Zod'], [['graphql'], 'GraphQL'],
205
+ ];
206
+ for (const [keywords, label] of stackMap) {
207
+ if (keywords.some(k => allDeps.some(d => d.toLowerCase().includes(k)))) {
208
+ tech_stack.push(label);
209
+ }
210
+ }
211
+ }
212
+ catch { /* malformed */ }
213
+ }
214
+ // Detect key config files
215
+ const CONFIG_FILES = ['tsconfig.json', 'vite.config.ts', 'vite.config.js', 'next.config.ts',
216
+ 'next.config.js', 'tailwind.config.ts', 'drizzle.config.ts', 'prisma/schema.prisma',
217
+ 'docker-compose.yml', '.env.example'];
218
+ const foundConfigs = CONFIG_FILES.filter(f => existsSync(join(dir, f)));
219
+ if (foundConfigs.length)
220
+ structure['config_files'] = foundConfigs;
221
+ // Scan src/ directory
222
+ const srcDir = join(dir, 'src');
223
+ if (existsSync(srcDir)) {
224
+ let tsCount = 0;
225
+ let testCount = 0;
226
+ const topDirs = [];
227
+ for (const entry of readdirSync(srcDir)) {
228
+ const full = join(srcDir, entry);
229
+ try {
230
+ if (statSync(full).isDirectory()) {
231
+ topDirs.push(entry);
232
+ key_modules.push(`src/${entry}`);
233
+ }
234
+ else {
235
+ const ext = extname(entry);
236
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext))
237
+ tsCount++;
238
+ if (entry.includes('.test.') || entry.includes('.spec.'))
239
+ testCount++;
240
+ }
241
+ }
242
+ catch { /* skip */ }
243
+ }
244
+ structure['src_dirs'] = topDirs;
245
+ structure['ts_files_in_src'] = tsCount;
246
+ if (testCount > 0)
247
+ structure['test_files'] = testCount;
248
+ }
249
+ // Entry points
250
+ const entryPoints = ['src/index.ts', 'src/main.ts', 'src/app.ts', 'src/server.ts', 'src/cli.ts', 'index.ts'];
251
+ const found = entryPoints.filter(f => existsSync(join(dir, f)));
252
+ if (found.length)
253
+ structure['entry_points'] = found;
254
+ return { structure, key_modules, tech_stack };
255
+ }
256
+ // ─── CLI Subcommands ────────────────────────────────────────────────────────────
257
+ async function statusCommand() {
258
+ const { getDbPath } = await import('./memory/local.js');
259
+ const { getDb } = await import('./memory/local.js');
260
+ const db = getDb();
261
+ const sessionCount = db.prepare('SELECT COUNT(*) as c FROM sessions').get().c;
262
+ const memoryCount = db.prepare('SELECT COUNT(*) as c FROM knowledge_base').get().c;
263
+ const patternCount = db.prepare('SELECT COUNT(*) as c FROM patterns').get().c;
264
+ const outcomeCount = db.prepare('SELECT COUNT(*) as c FROM learning_data').get().c;
265
+ console.log('');
266
+ console.log(c.bold(' Veto Status'));
267
+ console.log(c.dim(' ─────────────────────────────'));
268
+ console.log(` Version ${c.cyan(VERSION)}`);
269
+ console.log(` DB ${c.dim(getDbPath())}`);
270
+ console.log(` Sessions ${c.cyan(String(sessionCount))}`);
271
+ console.log(` Memory ${c.cyan(String(memoryCount))} knowledge entries`);
272
+ console.log(` Patterns ${c.cyan(String(patternCount))}`);
273
+ console.log(` Outcomes ${c.cyan(String(outcomeCount))} recorded`);
274
+ console.log('');
275
+ }
276
+ async function sessionsCommand() {
277
+ const { listSessions } = await import('./memory/local.js');
278
+ const sessions = listSessions(20);
279
+ console.log('');
280
+ console.log(c.bold(' Saved Sessions') + c.dim(` (${sessions.length})`));
281
+ console.log(c.dim(' ─────────────────────────────────────────────────────────────'));
282
+ if (sessions.length === 0) {
283
+ console.log(c.dim(' No sessions saved yet. Use veto_session_save inside an AI session.'));
284
+ }
285
+ else {
286
+ for (const s of sessions) {
287
+ const date = new Date(s.started_at).toLocaleString();
288
+ console.log(` ${c.cyan(s.id.slice(0, 8))} ${c.dim(date)} ${c.bold(s.platform ?? 'claude')} ${s.summary?.slice(0, 60) ?? ''}`);
289
+ }
290
+ }
291
+ console.log('');
292
+ }
293
+ async function memoryCommand() {
294
+ const args = process.argv.slice(3);
295
+ const query = args.join(' ') || undefined;
296
+ const { searchKnowledge } = await import('./memory/local.js');
297
+ const results = searchKnowledge({ query, limit: 20 });
298
+ console.log('');
299
+ console.log(c.bold(' Knowledge Base') + (query ? c.dim(` — "${query}"`) : '') + c.dim(` (${results.length} results)`));
300
+ console.log(c.dim(' ─────────────────────────────────────────────────────────────'));
301
+ if (results.length === 0) {
302
+ console.log(c.dim(' No entries found.'));
303
+ }
304
+ else {
305
+ for (const r of results) {
306
+ const tags = r.tags ? JSON.parse(r.tags).join(', ') : '';
307
+ console.log(` ${c.cyan(`[${r.type}]`)} ${c.bold(r.title)}`);
308
+ if (tags)
309
+ console.log(` ${c.dim('tags: ' + tags)}`);
310
+ console.log(` ${c.dim(r.content.slice(0, 100).replace(/\n/g, ' ') + (r.content.length > 100 ? '...' : ''))}`);
311
+ console.log('');
312
+ }
313
+ }
314
+ }
315
+ async function patternsCommand() {
316
+ const { getPatterns } = await import('./memory/local.js');
317
+ const prefix = process.argv[3];
318
+ const patterns = getPatterns(prefix, 30);
319
+ console.log('');
320
+ console.log(c.bold(' Learned Patterns') + c.dim(` (${patterns.length})`));
321
+ console.log(c.dim(' ─────────────────────────────────────────────────────────────'));
322
+ if (patterns.length === 0) {
323
+ console.log(c.dim(' No patterns yet. Record outcomes with veto_record_outcome to build up patterns.'));
324
+ }
325
+ else {
326
+ for (const p of patterns) {
327
+ const conf = Math.round(p.confidence * 100);
328
+ const confColor = conf >= 80 ? c.green : conf >= 60 ? c.yellow : c.dim;
329
+ console.log(` ${confColor(`${conf}%`)} ${c.cyan(p.pattern_key)} ${c.dim('→')} ${p.pattern_val} ${c.dim(`(seen ${p.seen_count}x)`)}`);
330
+ }
331
+ }
332
+ console.log('');
333
+ }
334
+ function helpCommand() {
335
+ console.log('');
336
+ console.log(c.bold(c.cyan(' veto')) + c.dim(` v${VERSION}`) + c.dim(' — 50 agents. 34 tools. 3 AIs. Self-learning. Zero extra cost.'));
337
+ console.log('');
338
+ console.log(c.bold(' CLI Commands'));
339
+ console.log(c.dim(' ─────────────────────────────────────────────────────'));
340
+ console.log(` ${c.cyan('veto init')} Configure all AI tools + scan project`);
341
+ console.log(` ${c.cyan('veto status')} Version, DB path, memory/session counts`);
342
+ console.log(` ${c.cyan('veto sessions')} List last 20 saved sessions`);
343
+ console.log(` ${c.cyan('veto memory')} ${c.dim('[query]')} Search knowledge base`);
344
+ console.log(` ${c.cyan('veto patterns')} ${c.dim('[prefix]')} List learned agent/routing patterns`);
345
+ console.log(` ${c.cyan('veto help')} Show this help`);
346
+ console.log('');
347
+ console.log(c.bold(' MCP Tools (34)'));
348
+ console.log(c.dim(' ─────────────────────────────────────────────────────'));
349
+ console.log(` ${c.dim('Session')} veto_status · veto_session_save · veto_session_restore · veto_sessions_list`);
350
+ console.log(` ${c.dim('Router')} veto_route_task · veto_rate_status`);
351
+ console.log(` ${c.dim('Council')} veto_council_debate`);
352
+ console.log(` ${c.dim('Agents')} veto_agent_plan · veto_execute_parallel · veto_explain`);
353
+ console.log(` ${c.dim('Review')} veto_code_review · veto_security_scan · veto_secrets_scan · veto_diff_review`);
354
+ console.log(` ${c.dim('Pipeline')} veto_workflow`);
355
+ console.log(` ${c.dim('Watch')} veto_watch · veto_watch_poll · veto_watch_stop`);
356
+ console.log(` ${c.dim('Memory')} veto_memory_store · veto_memory_search · veto_memory_delete`);
357
+ console.log(` veto_project_map_update · veto_project_map_get`);
358
+ console.log(` veto_pattern_store · veto_patterns_list`);
359
+ console.log(` veto_memory_export · veto_memory_import`);
360
+ console.log(` ${c.dim('Learning')} veto_record_outcome · veto_learning_stats · veto_learning_apply`);
361
+ console.log(` ${c.dim('Handoff')} veto_handoff · veto_continue · veto_platform_setup`);
362
+ console.log(` ${c.dim('Plugins')} veto_plugins`);
363
+ console.log('');
364
+ console.log(c.bold(' MCP Resources'));
365
+ console.log(c.dim(' ─────────────────────────────────────────────────────'));
366
+ console.log(` ${c.cyan('veto://sessions')} All saved sessions`);
367
+ console.log(` ${c.cyan('veto://project-map?dir=<path>')} Project structure map`);
368
+ console.log(` ${c.cyan('veto://memory?q=<query>')} Knowledge base search`);
369
+ console.log(` ${c.cyan('veto://patterns')} Learned patterns`);
370
+ console.log('');
371
+ console.log(c.bold(' MCP Prompts'));
372
+ console.log(c.dim(' ─────────────────────────────────────────────────────'));
373
+ console.log(` ${c.cyan('code-review')} · ${c.cyan('security-audit')} · ${c.cyan('deploy-checklist')} · ${c.cyan('explain-file')}`);
374
+ console.log('');
375
+ console.log(c.bold(' Docs'));
376
+ console.log(c.dim(' ─────────────────────────────────────────────────────'));
377
+ console.log(` ${c.dim('GitHub:')} https://github.com/jigyasudham/veto`);
378
+ console.log(` ${c.dim('npm:')} https://www.npmjs.com/package/@jigyasudham/veto`);
109
379
  console.log('');
110
380
  }
111
381
  // ─── Router ────────────────────────────────────────────────────────────────────
@@ -117,9 +387,38 @@ switch (command) {
117
387
  process.exit(1);
118
388
  });
119
389
  break;
390
+ case 'status':
391
+ statusCommand().catch((err) => {
392
+ console.error(c.red(`Error: ${err.message}`));
393
+ process.exit(1);
394
+ });
395
+ break;
396
+ case 'sessions':
397
+ sessionsCommand().catch((err) => {
398
+ console.error(c.red(`Error: ${err.message}`));
399
+ process.exit(1);
400
+ });
401
+ break;
402
+ case 'memory':
403
+ memoryCommand().catch((err) => {
404
+ console.error(c.red(`Error: ${err.message}`));
405
+ process.exit(1);
406
+ });
407
+ break;
408
+ case 'patterns':
409
+ patternsCommand().catch((err) => {
410
+ console.error(c.red(`Error: ${err.message}`));
411
+ process.exit(1);
412
+ });
413
+ break;
414
+ case 'help':
415
+ case '--help':
416
+ case '-h':
417
+ helpCommand();
418
+ break;
120
419
  default:
121
- console.error(`Unknown command: ${command}`);
122
- console.log('Usage: veto init');
420
+ console.error(c.red(` Unknown command: ${command}`));
421
+ helpCommand();
123
422
  process.exit(1);
124
423
  }
125
424
  //# sourceMappingURL=cli.js.map
@@ -0,0 +1,113 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ const STACK_INDICATORS = [
5
+ [['next', 'next.js'], 'Next.js'],
6
+ [['react', 'react-dom'], 'React'],
7
+ [['vue'], 'Vue'],
8
+ [['svelte'], 'Svelte'],
9
+ [['express'], 'Express'],
10
+ [['fastify'], 'Fastify'],
11
+ [['hono'], 'Hono'],
12
+ [['prisma'], 'Prisma'],
13
+ [['drizzle-orm'], 'Drizzle'],
14
+ [['typeorm'], 'TypeORM'],
15
+ [['mongoose'], 'Mongoose'],
16
+ [['@modelcontextprotocol/sdk'], 'MCP'],
17
+ [['typescript', 'ts-node', 'tsx'], 'TypeScript'],
18
+ [['jest', 'vitest'], 'Testing'],
19
+ [['tailwindcss'], 'Tailwind'],
20
+ [['zod'], 'Zod'],
21
+ [['trpc', '@trpc/server'], 'tRPC'],
22
+ [['graphql'], 'GraphQL'],
23
+ ];
24
+ function detectStack(deps) {
25
+ const names = Object.keys(deps).map(k => k.toLowerCase());
26
+ const stack = [];
27
+ for (const [keywords, label] of STACK_INDICATORS) {
28
+ if (keywords.some(k => names.some(n => n.includes(k)))) {
29
+ stack.push(label);
30
+ }
31
+ }
32
+ return stack;
33
+ }
34
+ function safeRead(filePath) {
35
+ try {
36
+ return readFileSync(filePath, 'utf8');
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ function safeExec(cmd, cwd) {
43
+ try {
44
+ return execSync(cmd, { cwd, timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).toString().trim();
45
+ }
46
+ catch {
47
+ return '';
48
+ }
49
+ }
50
+ export function readProjectContext(projectDir) {
51
+ const dir = resolve(projectDir);
52
+ if (!existsSync(dir)) {
53
+ return { summary: '', tech_stack: [], git_diff: '', key_files: [], error: `Directory not found: ${dir}` };
54
+ }
55
+ // package.json
56
+ let packageName;
57
+ let packageVersion;
58
+ let techStack = [];
59
+ const pkgRaw = safeRead(join(dir, 'package.json'));
60
+ if (pkgRaw) {
61
+ try {
62
+ const pkg = JSON.parse(pkgRaw);
63
+ packageName = pkg.name;
64
+ packageVersion = pkg.version;
65
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
66
+ techStack = detectStack(allDeps);
67
+ }
68
+ catch { /* malformed package.json */ }
69
+ }
70
+ // git diff — last 60 lines of unstaged + staged changes
71
+ const gitDiff = safeExec('git diff HEAD --stat --no-color', dir);
72
+ // git recent commits (last 5 one-liners for context)
73
+ const gitLog = safeExec('git log --oneline -5 --no-color', dir);
74
+ // key config files present
75
+ const CONFIG_FILES = [
76
+ 'tsconfig.json', 'vite.config.ts', 'vite.config.js',
77
+ 'next.config.ts', 'next.config.js', 'tailwind.config.ts',
78
+ 'drizzle.config.ts', 'prisma/schema.prisma', '.env.example',
79
+ ];
80
+ const keyFiles = CONFIG_FILES.filter(f => existsSync(join(dir, f)));
81
+ // build compact summary string
82
+ const lines = ['[CODEBASE CONTEXT]'];
83
+ if (packageName)
84
+ lines.push(`Project: ${packageName}${packageVersion ? ` v${packageVersion}` : ''}`);
85
+ if (techStack.length)
86
+ lines.push(`Stack: ${techStack.join(', ')}`);
87
+ if (keyFiles.length)
88
+ lines.push(`Config files: ${keyFiles.join(', ')}`);
89
+ if (gitDiff)
90
+ lines.push(`Recent changes:\n${gitDiff}`);
91
+ if (gitLog)
92
+ lines.push(`Recent commits:\n${gitLog}`);
93
+ return {
94
+ summary: lines.join('\n'),
95
+ package_name: packageName,
96
+ package_version: packageVersion,
97
+ tech_stack: techStack,
98
+ git_diff: gitDiff,
99
+ key_files: keyFiles,
100
+ };
101
+ }
102
+ export function buildContextString(projectDir, existingContext) {
103
+ if (!projectDir)
104
+ return existingContext ?? '';
105
+ const ctx = readProjectContext(projectDir);
106
+ if (ctx.error)
107
+ return existingContext ?? '';
108
+ const parts = [ctx.summary];
109
+ if (existingContext)
110
+ parts.push(existingContext);
111
+ return parts.join('\n\n');
112
+ }
113
+ //# sourceMappingURL=reader.js.map
@@ -7,8 +7,10 @@ import { analyze as devilAnalyze } from './devil-advocate.js';
7
7
  import { analyze as legalAnalyze } from './legal-compliance.js';
8
8
  import { analyze as securityAnalyze } from './security.js';
9
9
  import { decide, formatDebate } from './decision-engine.js';
10
+ import { buildContextString } from '../context/reader.js';
10
11
  export async function runDebate(input) {
11
- const fullText = input.context ? `${input.task}\n${input.context}` : input.task;
12
+ const enrichedContext = buildContextString(input.project_dir, input.context);
13
+ const fullText = enrichedContext ? `${input.task}\n\n${enrichedContext}` : input.task;
12
14
  // All 7 agents run in parallel — none depend on each other
13
15
  const [lead_dev, pm, architect, ux, devil, legal, security] = await Promise.all([
14
16
  Promise.resolve(leadDevAnalyze(fullText)),
@@ -0,0 +1,49 @@
1
+ import { readdirSync, existsSync } from 'node:fs';
2
+ import { join, basename, extname } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ const PLUGIN_DIR = join(homedir(), '.veto', 'agents');
5
+ const registry = new Map();
6
+ let loaded = false;
7
+ function pluginName(file) {
8
+ return basename(file, extname(file));
9
+ }
10
+ export async function loadPlugins() {
11
+ if (loaded)
12
+ return Array.from(registry.keys());
13
+ loaded = true;
14
+ if (!existsSync(PLUGIN_DIR))
15
+ return [];
16
+ const files = readdirSync(PLUGIN_DIR).filter(f => /\.(js|mjs)$/.test(f));
17
+ const names = [];
18
+ for (const file of files) {
19
+ const name = pluginName(file);
20
+ try {
21
+ const mod = await import(join(PLUGIN_DIR, file));
22
+ if (mod && typeof mod === 'object' &&
23
+ 'plan' in mod && typeof mod.plan === 'function') {
24
+ registry.set(name, mod);
25
+ names.push(name);
26
+ }
27
+ else {
28
+ process.stderr.write(`[veto] Plugin "${file}" skipped — must export plan(task, context?)\n`);
29
+ }
30
+ }
31
+ catch (err) {
32
+ process.stderr.write(`[veto] Plugin "${file}" failed to load: ${err}\n`);
33
+ }
34
+ }
35
+ return names;
36
+ }
37
+ export function getPlugin(name) {
38
+ return registry.get(name);
39
+ }
40
+ export function isPlugin(name) {
41
+ return registry.has(name);
42
+ }
43
+ export function listPlugins() {
44
+ return Array.from(registry.entries()).map(([name, mod]) => ({
45
+ name,
46
+ has_analyze: typeof mod.analyze === 'function',
47
+ }));
48
+ }
49
+ //# sourceMappingURL=loader.js.map
@@ -3,8 +3,8 @@ import { scoreComplexity } from './complexity-scorer.js';
3
3
  import { selectModel } from './model-selector.js';
4
4
  import { getRateStatus, trackRequest, getRoutingAdvice } from './rate-monitor.js';
5
5
  import { compressContext, estimateTokens } from './context-compressor.js';
6
- import { recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights } from './learning-updater.js';
7
- export { estimateTokens, getRateStatus, trackRequest, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights };
6
+ import { recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent } from './learning-updater.js';
7
+ export { estimateTokens, getRateStatus, trackRequest, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent };
8
8
  export function routeTask(task, options = {}) {
9
9
  const learned = getLearnedThresholds();
10
10
  const complexity = scoreComplexity(task, options.filesAffected, options.forceCouncil, learned.source === 'learned' ? learned : undefined);