@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.
- package/README.md +209 -52
- package/dist/agents/executor.js +36 -3
- package/dist/cli.js +350 -51
- package/dist/context/reader.js +113 -0
- package/dist/council/index.js +3 -1
- package/dist/plugins/loader.js +49 -0
- package/dist/router/index.js +2 -2
- package/dist/router/learning-updater.js +45 -1
- package/dist/server.js +478 -14
- package/dist/watcher/index.js +77 -0
- package/dist/workflow/pipeline.js +64 -0
- package/package.json +12 -3
- package/.claude/settings.local.json +0 -9
- package/src/adapters/claude.ts +0 -70
- package/src/adapters/codex.ts +0 -71
- package/src/adapters/gemini.ts +0 -71
- package/src/adapters/index.ts +0 -217
- package/src/agents/development/api.ts +0 -120
- package/src/agents/development/backend.ts +0 -85
- package/src/agents/development/coder.ts +0 -213
- package/src/agents/development/database.ts +0 -83
- package/src/agents/development/debugger.ts +0 -238
- package/src/agents/development/devops.ts +0 -86
- package/src/agents/development/frontend.ts +0 -85
- package/src/agents/development/migration.ts +0 -144
- package/src/agents/development/performance.ts +0 -144
- package/src/agents/development/refactor.ts +0 -86
- package/src/agents/development/reviewer.ts +0 -268
- package/src/agents/development/tester.ts +0 -151
- package/src/agents/executor.ts +0 -158
- package/src/agents/memory/context-manager.ts +0 -171
- package/src/agents/memory/decision-logger.ts +0 -160
- package/src/agents/memory/knowledge-base.ts +0 -124
- package/src/agents/memory/pattern-learner.ts +0 -143
- package/src/agents/memory/project-mapper.ts +0 -118
- package/src/agents/quality/accessibility.ts +0 -99
- package/src/agents/quality/code-quality.ts +0 -115
- package/src/agents/quality/compatibility.ts +0 -58
- package/src/agents/quality/documentation.ts +0 -105
- package/src/agents/quality/error-handling.ts +0 -96
- package/src/agents/research/competitor-analyzer.ts +0 -45
- package/src/agents/research/cost-analyzer.ts +0 -54
- package/src/agents/research/estimator.ts +0 -60
- package/src/agents/research/ethics-bias.ts +0 -113
- package/src/agents/research/researcher.ts +0 -114
- package/src/agents/research/risk-assessor.ts +0 -63
- package/src/agents/research/tech-advisor.ts +0 -55
- package/src/agents/security/auth.ts +0 -287
- package/src/agents/security/dependency-audit.ts +0 -337
- package/src/agents/security/penetration.ts +0 -262
- package/src/agents/security/privacy.ts +0 -285
- package/src/agents/security/scanner.ts +0 -322
- package/src/agents/security/secrets.ts +0 -249
- package/src/agents/types.ts +0 -66
- package/src/agents/workflow/automation.ts +0 -59
- package/src/agents/workflow/file-manager.ts +0 -52
- package/src/agents/workflow/git-agent.ts +0 -55
- package/src/agents/workflow/reporter.ts +0 -51
- package/src/agents/workflow/search-agent.ts +0 -40
- package/src/agents/workflow/task-coordinator.ts +0 -41
- package/src/agents/workflow/task-planner.ts +0 -47
- package/src/cli.ts +0 -135
- package/src/council/decision-engine.ts +0 -171
- package/src/council/devil-advocate.ts +0 -116
- package/src/council/index.ts +0 -44
- package/src/council/lead-developer.ts +0 -118
- package/src/council/legal-compliance.ts +0 -152
- package/src/council/product-manager.ts +0 -102
- package/src/council/security.ts +0 -172
- package/src/council/system-architect.ts +0 -132
- package/src/council/types.ts +0 -33
- package/src/council/ux-designer.ts +0 -121
- package/src/memory/local.ts +0 -305
- package/src/memory/schema.ts +0 -174
- package/src/memory/sync.ts +0 -274
- package/src/router/complexity-scorer.ts +0 -96
- package/src/router/context-compressor.ts +0 -74
- package/src/router/index.ts +0 -60
- package/src/router/learning-updater.ts +0 -271
- package/src/router/model-selector.ts +0 -83
- package/src/router/rate-monitor.ts +0 -103
- package/src/server.ts +0 -1038
- package/src/skills/development/skill-api-design.ts +0 -329
- package/src/skills/development/skill-auth.ts +0 -271
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +0 -209
- package/src/skills/development/skill-db-schema.ts +0 -0
- package/src/skills/development/skill-docker.ts +0 -0
- package/src/skills/development/skill-env-setup.ts +0 -0
- package/src/skills/development/skill-scaffold.ts +0 -323
- package/src/skills/intelligence/skill-complexity-score.ts +0 -69
- package/src/skills/intelligence/skill-cost-track.ts +0 -39
- package/src/skills/intelligence/skill-learning-loop.ts +0 -69
- package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
- package/src/skills/intelligence/skill-rate-watch.ts +0 -61
- package/src/skills/memory/skill-context-compress.ts +0 -98
- package/src/skills/memory/skill-cross-sync.ts +0 -104
- package/src/skills/memory/skill-decision-log.ts +0 -119
- package/src/skills/memory/skill-session-restore.ts +0 -59
- package/src/skills/memory/skill-session-save.ts +0 -94
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +0 -84
- package/src/skills/quality/skill-docs-gen.ts +0 -0
- package/src/skills/quality/skill-perf-audit.ts +0 -0
- package/src/skills/quality/skill-security-scan.ts +0 -91
- package/src/skills/quality/skill-test-suite.ts +0 -290
- package/src/skills/workflow/skill-deploy.ts +0 -0
- package/src/skills/workflow/skill-git-workflow.ts +0 -0
- package/src/skills/workflow/skill-rollback.ts +0 -0
- package/src/skills/workflow/skill-task-breakdown.ts +0 -0
- 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
|
+
const VERSION = '1.0.0';
|
|
9
9
|
const VETO_DIR = join(homedir(), '.veto');
|
|
10
|
-
|
|
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
|
+
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
|
|
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.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
console.log(
|
|
83
|
-
console.log(
|
|
84
|
-
console.log(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
console.log(
|
|
108
|
-
|
|
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
|
-
|
|
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
|
package/dist/council/index.js
CHANGED
|
@@ -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
|
|
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
|
package/dist/router/index.js
CHANGED
|
@@ -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);
|