@jigyasudham/veto 0.8.1 → 0.8.3
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 +14 -3
- package/dist/cli.js +105 -52
- package/package.json +3 -2
- package/src/cli.ts +204 -143
package/README.md
CHANGED
|
@@ -49,15 +49,26 @@ The `init` command prints the exact config snippet for your platform. Paste it i
|
|
|
49
49
|
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` |
|
|
50
50
|
| **VS Code** | `.vscode/mcp.json` |
|
|
51
51
|
|
|
52
|
-
Claude Code, Gemini, Codex, Cursor, and Windsurf all use `"mcpServers"
|
|
52
|
+
Claude Code, Gemini, Codex, Cursor, and Windsurf all use `"mcpServers"`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"veto": {
|
|
58
|
+
"command": "veto-server"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
VS Code uses `"servers"` with `"type": "stdio"`:
|
|
53
65
|
|
|
54
66
|
```json
|
|
55
67
|
{
|
|
56
68
|
"servers": {
|
|
57
69
|
"veto": {
|
|
58
70
|
"type": "stdio",
|
|
59
|
-
"command": "
|
|
60
|
-
"args": ["/path/to/dist/server.js"]
|
|
71
|
+
"command": "veto-server"
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
74
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2,13 +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';
|
|
5
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
6
|
import { join, dirname } from 'node:path';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
|
-
|
|
9
|
-
const VERSION = '0.8.0';
|
|
8
|
+
const VERSION = '0.8.2';
|
|
10
9
|
const VETO_DIR = join(homedir(), '.veto');
|
|
11
|
-
|
|
10
|
+
const HOME = homedir();
|
|
12
11
|
const c = {
|
|
13
12
|
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
14
13
|
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
@@ -30,6 +29,70 @@ function printBanner() {
|
|
|
30
29
|
console.log(c.dim(` v${VERSION}`));
|
|
31
30
|
console.log('');
|
|
32
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
|
+
];
|
|
33
96
|
async function initCommand() {
|
|
34
97
|
printBanner();
|
|
35
98
|
// 1. Create ~/.veto directory
|
|
@@ -40,13 +103,12 @@ async function initCommand() {
|
|
|
40
103
|
else {
|
|
41
104
|
console.log(c.dim(' · ') + `Found existing ${VETO_DIR}`);
|
|
42
105
|
}
|
|
43
|
-
// 2. Initialize SQLite database
|
|
106
|
+
// 2. Initialize SQLite database
|
|
44
107
|
process.stdout.write(' · Initializing SQLite database...');
|
|
45
108
|
const { getDb, getDbPath, saveSession } = await import('./memory/local.js');
|
|
46
109
|
try {
|
|
47
110
|
const db = getDb();
|
|
48
111
|
const dbPath = getDbPath();
|
|
49
|
-
// Smoke test: save + retrieve a test session
|
|
50
112
|
const { session_id } = saveSession({
|
|
51
113
|
platform: 'claude',
|
|
52
114
|
summary: 'Veto initialized',
|
|
@@ -55,7 +117,6 @@ async function initCommand() {
|
|
|
55
117
|
const row = db.prepare('SELECT id FROM sessions WHERE id = ?').get(session_id);
|
|
56
118
|
if (!row)
|
|
57
119
|
throw new Error('DB smoke test failed');
|
|
58
|
-
// Clean up test row
|
|
59
120
|
db.prepare('DELETE FROM sessions WHERE id = ?').run(session_id);
|
|
60
121
|
console.log(c.green(' ✓'));
|
|
61
122
|
console.log(c.green(' ✓') + ` Database ready at ${dbPath}`);
|
|
@@ -66,54 +127,46 @@ async function initCommand() {
|
|
|
66
127
|
console.error(c.red(` Error initializing database: ${msg}`));
|
|
67
128
|
process.exit(1);
|
|
68
129
|
}
|
|
69
|
-
// 3.
|
|
70
|
-
// Point to server.js (the MCP server), not cli.js (the init wizard)
|
|
71
|
-
const cliFile = fileURLToPath(import.meta.url);
|
|
72
|
-
const distDir = dirname(cliFile);
|
|
73
|
-
const serverPath = join(distDir, 'server.js').replace(/\\/g, '/');
|
|
74
|
-
console.log('');
|
|
75
|
-
console.log(c.bold(' ┌─ Add Veto to your AI CLI or IDE ───────────────────────────────┐'));
|
|
76
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
77
|
-
console.log(c.bold(' │') + ' Config file locations: ' + c.bold('│'));
|
|
78
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
79
|
-
console.log(c.bold(' │') + c.dim(' Claude Code → ~/.claude/mcp_servers.json') + ' ' + c.bold('│'));
|
|
80
|
-
console.log(c.bold(' │') + c.dim(' Gemini CLI → ~/.gemini/settings.json') + ' ' + c.bold('│'));
|
|
81
|
-
console.log(c.bold(' │') + c.dim(' Codex CLI → ~/.codex/config.json') + ' ' + c.bold('│'));
|
|
82
|
-
console.log(c.bold(' │') + c.dim(' Cursor → ~/.cursor/mcp.json') + ' ' + c.bold('│'));
|
|
83
|
-
console.log(c.bold(' │') + c.dim(' Windsurf → ~/.codeium/windsurf/mcp_config.json') + ' ' + c.bold('│'));
|
|
84
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
85
|
-
console.log(c.bold(' │') + ' Claude / Gemini / Codex / Cursor / Windsurf: ' + c.bold('│'));
|
|
86
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
87
|
-
console.log(c.bold(' │') + c.cyan(' {') + ' ' + c.bold('│'));
|
|
88
|
-
console.log(c.bold(' │') + c.cyan(' "mcpServers": {') + ' ' + c.bold('│'));
|
|
89
|
-
console.log(c.bold(' │') + c.cyan(' "veto": {') + ' ' + c.bold('│'));
|
|
90
|
-
console.log(c.bold(' │') + c.cyan(' "command": "node",') + ' ' + c.bold('│'));
|
|
91
|
-
console.log(c.bold(' │') + c.cyan(` "args": ["${serverPath}"]`) + ' ' + c.bold('│'));
|
|
92
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
93
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
94
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
95
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
96
|
-
console.log(c.bold(' │') + ' VS Code → .vscode/mcp.json: ' + c.bold('│'));
|
|
97
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
98
|
-
console.log(c.bold(' │') + c.cyan(' {') + ' ' + c.bold('│'));
|
|
99
|
-
console.log(c.bold(' │') + c.cyan(' "servers": {') + ' ' + c.bold('│'));
|
|
100
|
-
console.log(c.bold(' │') + c.cyan(' "veto": {') + ' ' + c.bold('│'));
|
|
101
|
-
console.log(c.bold(' │') + c.cyan(' "type": "stdio",') + ' ' + c.bold('│'));
|
|
102
|
-
console.log(c.bold(' │') + c.cyan(' "command": "node",') + ' ' + c.bold('│'));
|
|
103
|
-
console.log(c.bold(' │') + c.cyan(` "args": ["${serverPath}"]`) + ' ' + c.bold('│'));
|
|
104
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
105
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
106
|
-
console.log(c.bold(' │') + c.cyan(' }') + ' ' + c.bold('│'));
|
|
107
|
-
console.log(c.bold(' │') + ' ' + c.bold('│'));
|
|
108
|
-
console.log(c.bold(' └────────────────────────────────────────────────────────────────┘'));
|
|
130
|
+
// 3. Auto-configure every AI CLI / IDE found on this machine
|
|
109
131
|
console.log('');
|
|
110
|
-
console.log(
|
|
132
|
+
console.log(' Configuring all AI tools found on this machine...');
|
|
111
133
|
console.log('');
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
let configured = 0;
|
|
135
|
+
let skipped = 0;
|
|
136
|
+
for (const platform of PLATFORMS) {
|
|
137
|
+
const detected = existsSync(platform.detectionDir);
|
|
138
|
+
if (!detected) {
|
|
139
|
+
console.log(c.dim(' · ') + c.dim(`${platform.name} — not installed, skipping`));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const result = writeVetoConfig(platform.path, platform.format);
|
|
143
|
+
if (result === 'skipped') {
|
|
144
|
+
console.log(c.yellow(' ⚠ ') + `${platform.name} — config unreadable, skipped`);
|
|
145
|
+
skipped++;
|
|
146
|
+
}
|
|
147
|
+
else if (result === 'created') {
|
|
148
|
+
console.log(c.green(' ✓ ') + `${platform.name} — configured`);
|
|
149
|
+
configured++;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log(c.green(' ✓ ') + `${platform.name} — updated`);
|
|
153
|
+
configured++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
116
156
|
console.log('');
|
|
157
|
+
if (configured === 0 && skipped === 0) {
|
|
158
|
+
console.log(c.yellow(' ⚠ No AI tools detected.'));
|
|
159
|
+
console.log(' Install Claude Code, Gemini CLI, or Codex CLI and run veto init again.');
|
|
160
|
+
console.log('');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
console.log(c.green(` ✓ Veto configured for ${configured} tool${configured !== 1 ? 's' : ''}!`));
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log(' Next steps:');
|
|
166
|
+
console.log(c.dim(' 1.') + ' Restart your AI CLI or IDE');
|
|
167
|
+
console.log(c.dim(' 2.') + ' Run: veto_status — should return { "status": "running", "version": "' + VERSION + '" }');
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
|
117
170
|
}
|
|
118
171
|
// ─── Router ────────────────────────────────────────────────────────────────────
|
|
119
172
|
const command = process.argv[2] ?? 'init';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jigyasudham/veto",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "50 agents. 28 skills. 3 AIs. Self-learning. Zero extra cost.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"node": ">=22.5.0"
|
|
18
18
|
},
|
|
19
19
|
"bin": {
|
|
20
|
-
"veto": "dist/cli.js"
|
|
20
|
+
"veto": "dist/cli.js",
|
|
21
|
+
"veto-server": "dist/server.js"
|
|
21
22
|
},
|
|
22
23
|
"main": "./dist/server.js",
|
|
23
24
|
"scripts": {
|
package/src/cli.ts
CHANGED
|
@@ -1,143 +1,204 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Veto CLI — entry point for `npx veto init`
|
|
3
|
-
|
|
4
|
-
// Suppress Node experimental warnings (node:sqlite) for clean UX
|
|
5
|
-
process.removeAllListeners('warning');
|
|
6
|
-
|
|
7
|
-
import { mkdirSync, existsSync } from 'node:fs';
|
|
8
|
-
import { join, dirname } from 'node:path';
|
|
9
|
-
import { homedir } from 'node:os';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.log('');
|
|
27
|
-
console.log(c.bold(c.cyan('
|
|
28
|
-
console.log(c.bold(c.cyan(' ██║
|
|
29
|
-
console.log(c.bold(c.cyan('
|
|
30
|
-
console.log(c.bold(c.cyan('
|
|
31
|
-
console.log(c.bold(c.cyan('
|
|
32
|
-
console.log(
|
|
33
|
-
console.log(
|
|
34
|
-
console.log(c.dim(`
|
|
35
|
-
console.log(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Veto CLI — entry point for `npx veto init`
|
|
3
|
+
|
|
4
|
+
// Suppress Node experimental warnings (node:sqlite) for clean UX
|
|
5
|
+
process.removeAllListeners('warning');
|
|
6
|
+
|
|
7
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { join, dirname } from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
|
|
11
|
+
const VERSION = '0.8.2';
|
|
12
|
+
const VETO_DIR = join(homedir(), '.veto');
|
|
13
|
+
const HOME = homedir();
|
|
14
|
+
|
|
15
|
+
const c = {
|
|
16
|
+
bold: (s: string) => `\x1b[1m${s}\x1b[0m`,
|
|
17
|
+
green: (s: string) => `\x1b[32m${s}\x1b[0m`,
|
|
18
|
+
yellow: (s: string) => `\x1b[33m${s}\x1b[0m`,
|
|
19
|
+
cyan: (s: string) => `\x1b[36m${s}\x1b[0m`,
|
|
20
|
+
dim: (s: string) => `\x1b[2m${s}\x1b[0m`,
|
|
21
|
+
red: (s: string) => `\x1b[31m${s}\x1b[0m`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function printBanner() {
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(c.bold(c.cyan(' ██╗ ██╗███████╗████████╗ ██████╗')));
|
|
27
|
+
console.log(c.bold(c.cyan(' ██║ ██║██╔════╝╚══██╔══╝██╔═══██╗')));
|
|
28
|
+
console.log(c.bold(c.cyan(' ██║ ██║█████╗ ██║ ██║ ██║')));
|
|
29
|
+
console.log(c.bold(c.cyan(' ╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║')));
|
|
30
|
+
console.log(c.bold(c.cyan(' ╚████╔╝ ███████╗ ██║ ╚██████╔╝')));
|
|
31
|
+
console.log(c.bold(c.cyan(' ╚═══╝ ╚══════╝ ╚═╝ ╚═════╝')));
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(c.dim(` 50 agents. 28 skills. 3 AIs. Self-learning. Zero extra cost.`));
|
|
34
|
+
console.log(c.dim(` v${VERSION}`));
|
|
35
|
+
console.log('');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Merge veto entry into an existing JSON config file, creating it if needed.
|
|
39
|
+
// Supports both "mcpServers" format (Claude/Gemini/Codex/Cursor/Windsurf)
|
|
40
|
+
// and "servers" format (VS Code).
|
|
41
|
+
function writeVetoConfig(
|
|
42
|
+
configPath: string,
|
|
43
|
+
format: 'mcpServers' | 'servers'
|
|
44
|
+
): 'created' | 'updated' | 'skipped' {
|
|
45
|
+
let existing: Record<string, unknown> = {};
|
|
46
|
+
|
|
47
|
+
if (existsSync(configPath)) {
|
|
48
|
+
try {
|
|
49
|
+
existing = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
50
|
+
} catch {
|
|
51
|
+
// Unreadable / invalid JSON — skip to avoid corrupting it
|
|
52
|
+
return 'skipped';
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const wasEmpty = Object.keys(existing).length === 0;
|
|
59
|
+
|
|
60
|
+
if (format === 'mcpServers') {
|
|
61
|
+
const servers = (existing.mcpServers as Record<string, unknown>) ?? {};
|
|
62
|
+
servers['veto'] = { command: 'veto-server' };
|
|
63
|
+
existing.mcpServers = servers;
|
|
64
|
+
} else {
|
|
65
|
+
const servers = (existing.servers as Record<string, unknown>) ?? {};
|
|
66
|
+
servers['veto'] = { type: 'stdio', command: 'veto-server' };
|
|
67
|
+
existing.servers = servers;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
writeFileSync(configPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
|
|
71
|
+
return wasEmpty ? 'created' : 'updated';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// All platforms Veto supports, with their config paths and formats.
|
|
75
|
+
const PLATFORMS = [
|
|
76
|
+
{
|
|
77
|
+
name: 'Claude Code',
|
|
78
|
+
path: join(HOME, '.claude', 'mcp_servers.json'),
|
|
79
|
+
format: 'mcpServers' as const,
|
|
80
|
+
detectionDir: join(HOME, '.claude'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Gemini CLI',
|
|
84
|
+
path: join(HOME, '.gemini', 'settings.json'),
|
|
85
|
+
format: 'mcpServers' as const,
|
|
86
|
+
detectionDir: join(HOME, '.gemini'),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Codex CLI',
|
|
90
|
+
path: join(HOME, '.codex', 'config.json'),
|
|
91
|
+
format: 'mcpServers' as const,
|
|
92
|
+
detectionDir: join(HOME, '.codex'),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'Cursor',
|
|
96
|
+
path: join(HOME, '.cursor', 'mcp.json'),
|
|
97
|
+
format: 'mcpServers' as const,
|
|
98
|
+
detectionDir: join(HOME, '.cursor'),
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'Windsurf',
|
|
102
|
+
path: join(HOME, '.codeium', 'windsurf', 'mcp_config.json'),
|
|
103
|
+
format: 'mcpServers' as const,
|
|
104
|
+
detectionDir: join(HOME, '.codeium', 'windsurf'),
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
async function initCommand() {
|
|
109
|
+
printBanner();
|
|
110
|
+
|
|
111
|
+
// 1. Create ~/.veto directory
|
|
112
|
+
if (!existsSync(VETO_DIR)) {
|
|
113
|
+
mkdirSync(VETO_DIR, { recursive: true });
|
|
114
|
+
console.log(c.green(' ✓') + ` Created ${VETO_DIR}`);
|
|
115
|
+
} else {
|
|
116
|
+
console.log(c.dim(' · ') + `Found existing ${VETO_DIR}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 2. Initialize SQLite database
|
|
120
|
+
process.stdout.write(' · Initializing SQLite database...');
|
|
121
|
+
const { getDb, getDbPath, saveSession } = await import('./memory/local.js');
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const db = getDb();
|
|
125
|
+
const dbPath = getDbPath();
|
|
126
|
+
const { session_id } = saveSession({
|
|
127
|
+
platform: 'claude',
|
|
128
|
+
summary: 'Veto initialized',
|
|
129
|
+
context: 'Initial setup via npx veto init',
|
|
130
|
+
});
|
|
131
|
+
const row = db.prepare('SELECT id FROM sessions WHERE id = ?').get(session_id);
|
|
132
|
+
if (!row) throw new Error('DB smoke test failed');
|
|
133
|
+
db.prepare('DELETE FROM sessions WHERE id = ?').run(session_id);
|
|
134
|
+
console.log(c.green(' ✓'));
|
|
135
|
+
console.log(c.green(' ✓') + ` Database ready at ${dbPath}`);
|
|
136
|
+
} catch (err: unknown) {
|
|
137
|
+
console.log(c.red(' ✗'));
|
|
138
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
139
|
+
console.error(c.red(` Error initializing database: ${msg}`));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 3. 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
|
+
|
|
148
|
+
let configured = 0;
|
|
149
|
+
let skipped = 0;
|
|
150
|
+
|
|
151
|
+
for (const platform of PLATFORMS) {
|
|
152
|
+
const detected = existsSync(platform.detectionDir);
|
|
153
|
+
if (!detected) {
|
|
154
|
+
console.log(c.dim(' · ') + c.dim(`${platform.name} — not installed, skipping`));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = writeVetoConfig(platform.path, platform.format);
|
|
159
|
+
|
|
160
|
+
if (result === 'skipped') {
|
|
161
|
+
console.log(c.yellow(' ⚠ ') + `${platform.name} — config unreadable, skipped`);
|
|
162
|
+
skipped++;
|
|
163
|
+
} else if (result === 'created') {
|
|
164
|
+
console.log(c.green(' ✓ ') + `${platform.name} — configured`);
|
|
165
|
+
configured++;
|
|
166
|
+
} else {
|
|
167
|
+
console.log(c.green(' ✓ ') + `${platform.name} — updated`);
|
|
168
|
+
configured++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log('');
|
|
173
|
+
|
|
174
|
+
if (configured === 0 && skipped === 0) {
|
|
175
|
+
console.log(c.yellow(' ⚠ No AI tools detected.'));
|
|
176
|
+
console.log(' Install Claude Code, Gemini CLI, or Codex CLI and run veto init again.');
|
|
177
|
+
console.log('');
|
|
178
|
+
} else {
|
|
179
|
+
console.log(c.green(` ✓ Veto configured for ${configured} tool${configured !== 1 ? 's' : ''}!`));
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log(' Next steps:');
|
|
182
|
+
console.log(c.dim(' 1.') + ' Restart your AI CLI or IDE');
|
|
183
|
+
console.log(c.dim(' 2.') + ' Run: veto_status — should return { "status": "running", "version": "' + VERSION + '" }');
|
|
184
|
+
console.log('');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─── Router ────────────────────────────────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
const command = process.argv[2] ?? 'init';
|
|
191
|
+
|
|
192
|
+
switch (command) {
|
|
193
|
+
case 'init':
|
|
194
|
+
initCommand().catch((err) => {
|
|
195
|
+
console.error(c.red(`Error: ${err.message}`));
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
default:
|
|
201
|
+
console.error(`Unknown command: ${command}`);
|
|
202
|
+
console.log('Usage: veto init');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|