@viberaven/mcp 1.0.6 → 1.0.7

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 (3) hide show
  1. package/README.md +20 -8
  2. package/bin/server.js +381 -366
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -32,14 +32,26 @@ npx -y viberaven mcp
32
32
  - `viberaven_strict_gate` -> `npx -y viberaven --agent-mode --strict --json`
33
33
  - `viberaven_gate_result` -> `npx -y viberaven --agent-mode --json`
34
34
  - `viberaven_context_map` -> `npx -y viberaven --condense`
35
- - `viberaven_heal_plan` -> `npx -y viberaven --heal --plan`
36
- - `viberaven_heal_prompt` -> `npx -y viberaven --heal --prompt`
37
- - `viberaven_heal_apply` -> `npx -y viberaven --heal --apply`
38
- - `viberaven_validate_npm_package` -> `npx -y viberaven validate-npm-package --json`
39
-
40
- `viberaven_check_readiness` is the canonical entrypoint. It runs VibeRaven as the Agent Context + Production Gate and writes `.viberaven/agent-tasklist.md`, `.viberaven/gate-result.json`, and `.viberaven/context-map.json`.
41
-
42
- After one fix, call `viberaven_verify`. Before deploy or CI pass, call `viberaven_strict_gate` or run:
35
+ - `viberaven_heal_plan` -> `npx -y viberaven --heal --plan`
36
+ - `viberaven_heal_prompt` -> `npx -y viberaven --heal --prompt`
37
+ - `viberaven_heal_apply` -> `npx -y viberaven --heal --apply`
38
+
39
+ `viberaven_check_readiness` is the canonical entrypoint. It runs VibeRaven as the Agent Context + Production Gate and writes `.viberaven/agent-tasklist.md`, `.viberaven/gate-result.json`, `.viberaven/context-map.json`, and Launch Autopilot artifacts.
40
+
41
+ Launch Autopilot keeps the canonical agent command:
42
+
43
+ ```bash
44
+ npx -y viberaven --agent-mode
45
+ ```
46
+
47
+ MCP clients may pass `launchMode` to `viberaven_check_readiness`:
48
+
49
+ - `manual`: show links and commands only
50
+ - `ask`: default, ask before provider actions
51
+ - `safe`: read-only provider checks and safe repo fixes
52
+ - `full`: approved session mode; production deploy still needs final confirmation
53
+
54
+ After one fix, call `viberaven_verify`. Before deploy or CI pass, call `viberaven_strict_gate` or run:
43
55
 
44
56
  ```bash
45
57
  npx -y viberaven --strict
package/bin/server.js CHANGED
@@ -1,383 +1,398 @@
1
- #!/usr/bin/env node
2
- const { spawn } = require('node:child_process');
3
- let spawnChild = spawn;
4
-
5
- const SERVER_INFO = { name: 'viberaven-mcp', version: '1.0.6' };
6
- const CLI_PACKAGE = '@viberaven/cli';
7
-
1
+ #!/usr/bin/env node
2
+ const { spawn } = require('node:child_process');
3
+ let spawnChild = spawn;
4
+
5
+ const SERVER_INFO = { name: 'viberaven-mcp', version: '1.0.7' };
6
+ const CLI_PACKAGE = '@viberaven/cli';
7
+
8
8
  const cwdSchema = {
9
9
  type: 'object',
10
10
  properties: {
11
- cwd: { type: 'string', description: 'Project root. Defaults to the MCP server working directory.' }
12
- },
13
- additionalProperties: false
14
- };
15
-
16
- const healSchema = {
17
- type: 'object',
18
- properties: {
19
- cwd: { type: 'string' },
20
- target: { type: 'string' },
21
- gap: { type: 'string' },
22
- yes: { type: 'boolean' }
11
+ cwd: { type: 'string', description: 'Project root. Defaults to the MCP server working directory.' },
12
+ launchMode: {
13
+ type: 'string',
14
+ enum: ['manual', 'ask', 'safe', 'full'],
15
+ description: 'Launch Autopilot permission mode. Defaults to ask.'
16
+ }
23
17
  },
24
18
  additionalProperties: false
25
19
  };
26
-
27
- const TOOLS = [
20
+
21
+ const healSchema = {
22
+ type: 'object',
23
+ properties: {
24
+ cwd: { type: 'string' },
25
+ target: { type: 'string' },
26
+ gap: { type: 'string' },
27
+ yes: { type: 'boolean' }
28
+ },
29
+ additionalProperties: false
30
+ };
31
+
32
+ const TOOLS = [
28
33
  {
29
34
  name: 'viberaven_check_readiness',
30
- description: 'Run the VibeRaven agent-mode production-readiness check and write .viberaven artifacts.',
31
- inputSchema: cwdSchema
32
- },
33
- {
34
- name: 'viberaven_verify',
35
- description: 'Rescan and refresh VibeRaven production-readiness artifacts after a fix.',
36
- inputSchema: cwdSchema
37
- },
38
- {
39
- name: 'viberaven_audit',
40
- description: 'Run local Vercel/Supabase production checks for RLS, service-role boundaries, and Vercel pooler usage.',
41
- inputSchema: {
42
- type: 'object',
43
- properties: {
44
- cwd: { type: 'string', description: 'Project root. Defaults to current working directory.' },
45
- json: { type: 'boolean', description: 'Return JSON output.' }
46
- },
47
- additionalProperties: false
48
- }
49
- },
50
- {
51
- name: 'viberaven_init_rules',
52
- description: 'Install bounded VibeRaven rules into native AI instruction files.',
53
- inputSchema: {
54
- type: 'object',
55
- properties: {
56
- cwd: { type: 'string', description: 'Project root. Defaults to current working directory.' },
57
- agents: { type: 'string', description: 'Comma-separated agent targets, or all.' },
58
- dryRun: { type: 'boolean', description: 'Preview changes without writing files.' }
59
- },
60
- additionalProperties: false
61
- }
62
- },
63
- {
64
- name: 'viberaven_clean_plan',
65
- description: 'Write a non-destructive context cleanup plan for generated artifacts and logs.',
66
- inputSchema: cwdSchema
67
- },
68
- {
69
- name: 'viberaven_strict_gate',
70
- description: 'Run VibeRaven agent-mode strict gate and return the machine verdict.',
71
- inputSchema: cwdSchema
72
- },
73
- {
74
- name: 'viberaven_gate_result',
75
- description: 'Run VibeRaven agent-mode JSON output and return gate-result.json content.',
76
- inputSchema: cwdSchema
77
- },
78
- {
79
- name: 'viberaven_context_map',
80
- description: 'Refresh .viberaven/context-map.json from the last scan.',
35
+ description:
36
+ 'Run VibeRaven agent-mode production-readiness and Launch Autopilot; writes .viberaven artifacts and one next launch action.',
81
37
  inputSchema: cwdSchema
82
38
  },
83
- {
84
- name: 'viberaven_heal_plan',
85
- description: 'Write a non-destructive VibeRaven heal plan for a target file or gap.',
86
- inputSchema: healSchema
87
- },
88
- {
89
- name: 'viberaven_heal_prompt',
90
- description: 'Write an agent-ready VibeRaven heal prompt for a target file or gap.',
91
- inputSchema: healSchema
92
- },
93
- {
94
- name: 'viberaven_heal_apply',
95
- description: 'Apply a guarded VibeRaven repo-code heal recipe when supported.',
96
- inputSchema: healSchema
97
- },
98
- {
99
- name: 'viberaven_validate_npm_package',
100
- description:
101
- 'Check whether an npm package name exists and looks safe before install. Always run viberaven --agent-mode after adding dependencies.',
102
- inputSchema: {
103
- type: 'object',
104
- properties: {
105
- packageName: { type: 'string', description: 'Single npm package name to validate.' },
106
- packageNames: {
107
- type: 'array',
108
- items: { type: 'string' },
109
- description: 'Multiple npm package names to validate in one call.'
110
- }
111
- },
112
- additionalProperties: false
113
- }
114
- }
115
- ];
116
-
117
- let buffer = Buffer.alloc(0);
118
-
119
- function main() {
120
- if (process.argv.includes('--help') || process.argv.includes('-h')) {
121
- console.log(`${SERVER_INFO.name} ${SERVER_INFO.version}`);
122
- console.log(`MCP stdio server wrapping npx -y ${CLI_PACKAGE}.`);
123
- process.exit(0);
124
- }
125
-
126
- process.stdin.on('data', (chunk) => {
127
- buffer = Buffer.concat([buffer, chunk]);
128
- drainMessages();
129
- });
130
- }
131
-
132
- function drainMessages() {
133
- while (true) {
134
- const headerEnd = buffer.indexOf('\r\n\r\n');
135
- if (headerEnd === -1) return;
136
-
137
- const header = buffer.slice(0, headerEnd).toString('utf8');
138
- const match = header.match(/content-length:\s*(\d+)/i);
139
- if (!match) {
140
- buffer = buffer.slice(headerEnd + 4);
141
- continue;
142
- }
143
-
144
- const length = Number(match[1]);
145
- const messageStart = headerEnd + 4;
146
- const messageEnd = messageStart + length;
147
- if (buffer.length < messageEnd) return;
148
-
149
- const raw = buffer.slice(messageStart, messageEnd).toString('utf8');
150
- buffer = buffer.slice(messageEnd);
151
- void handleRawMessage(raw);
152
- }
153
- }
154
-
155
- async function handleRawMessage(raw) {
156
- let message;
157
- try {
158
- message = JSON.parse(raw);
159
- } catch {
160
- return;
161
- }
162
-
163
- if (!message || typeof message.method !== 'string') return;
164
- if (message.id === undefined || message.id === null) return;
165
-
166
- try {
167
- const result = await handleRequest(message.method, message.params || {});
168
- send({ jsonrpc: '2.0', id: message.id, result });
169
- } catch (error) {
170
- send({
171
- jsonrpc: '2.0',
172
- id: message.id,
173
- error: {
174
- code: -32000,
175
- message: error instanceof Error ? error.message : String(error)
176
- }
177
- });
178
- }
179
- }
180
-
181
- async function handleRequest(method, params) {
182
- if (method === 'initialize') {
183
- return {
184
- protocolVersion: params.protocolVersion || '2024-11-05',
185
- capabilities: { tools: {} },
186
- serverInfo: SERVER_INFO
187
- };
188
- }
189
-
190
- if (method === 'tools/list') {
191
- return { tools: TOOLS };
192
- }
193
-
194
- if (method === 'tools/call') {
195
- const name = params.name;
196
- const args = params.arguments || {};
197
- const text = await callTool(name, args);
198
- const parsed = parseJsonObjectFromToolText(text);
199
- return {
200
- content: [{ type: 'text', text }],
201
- ...(parsed ? { structuredContent: parsed } : {})
202
- };
203
- }
204
-
205
- throw new Error(`Unsupported MCP method: ${method}`);
206
- }
207
-
208
- async function callTool(name, args) {
209
- return runVibeRaven(['-y', CLI_PACKAGE, ...buildToolArgs(name, args || {})], args && args.cwd);
210
- }
211
-
39
+ {
40
+ name: 'viberaven_verify',
41
+ description: 'Rescan and refresh VibeRaven production-readiness artifacts after a fix.',
42
+ inputSchema: cwdSchema
43
+ },
44
+ {
45
+ name: 'viberaven_audit',
46
+ description: 'Run local Vercel/Supabase production checks for RLS, service-role boundaries, and Vercel pooler usage.',
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ cwd: { type: 'string', description: 'Project root. Defaults to current working directory.' },
51
+ json: { type: 'boolean', description: 'Return JSON output.' }
52
+ },
53
+ additionalProperties: false
54
+ }
55
+ },
56
+ {
57
+ name: 'viberaven_init_rules',
58
+ description: 'Install bounded VibeRaven rules into native AI instruction files.',
59
+ inputSchema: {
60
+ type: 'object',
61
+ properties: {
62
+ cwd: { type: 'string', description: 'Project root. Defaults to current working directory.' },
63
+ agents: { type: 'string', description: 'Comma-separated agent targets, or all.' },
64
+ dryRun: { type: 'boolean', description: 'Preview changes without writing files.' }
65
+ },
66
+ additionalProperties: false
67
+ }
68
+ },
69
+ {
70
+ name: 'viberaven_clean_plan',
71
+ description: 'Write a non-destructive context cleanup plan for generated artifacts and logs.',
72
+ inputSchema: cwdSchema
73
+ },
74
+ {
75
+ name: 'viberaven_strict_gate',
76
+ description: 'Run VibeRaven agent-mode strict gate and return the machine verdict.',
77
+ inputSchema: cwdSchema
78
+ },
79
+ {
80
+ name: 'viberaven_gate_result',
81
+ description: 'Run VibeRaven agent-mode JSON output and return gate-result.json content.',
82
+ inputSchema: cwdSchema
83
+ },
84
+ {
85
+ name: 'viberaven_context_map',
86
+ description: 'Refresh .viberaven/context-map.json from the last scan.',
87
+ inputSchema: cwdSchema
88
+ },
89
+ {
90
+ name: 'viberaven_heal_plan',
91
+ description: 'Write a non-destructive VibeRaven heal plan for a target file or gap.',
92
+ inputSchema: healSchema
93
+ },
94
+ {
95
+ name: 'viberaven_heal_prompt',
96
+ description: 'Write an agent-ready VibeRaven heal prompt for a target file or gap.',
97
+ inputSchema: healSchema
98
+ },
99
+ {
100
+ name: 'viberaven_heal_apply',
101
+ description: 'Apply a guarded VibeRaven repo-code heal recipe when supported.',
102
+ inputSchema: healSchema
103
+ },
104
+ {
105
+ name: 'viberaven_validate_npm_package',
106
+ description:
107
+ 'Check whether an npm package name exists and looks safe before install. Always run viberaven --agent-mode after adding dependencies.',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ packageName: { type: 'string', description: 'Single npm package name to validate.' },
112
+ packageNames: {
113
+ type: 'array',
114
+ items: { type: 'string' },
115
+ description: 'Multiple npm package names to validate in one call.'
116
+ }
117
+ },
118
+ additionalProperties: false
119
+ }
120
+ }
121
+ ];
122
+
123
+ let buffer = Buffer.alloc(0);
124
+
125
+ function main() {
126
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
127
+ console.log(`${SERVER_INFO.name} ${SERVER_INFO.version}`);
128
+ console.log(`MCP stdio server wrapping npx -y ${CLI_PACKAGE}.`);
129
+ process.exit(0);
130
+ }
131
+
132
+ process.stdin.on('data', (chunk) => {
133
+ buffer = Buffer.concat([buffer, chunk]);
134
+ drainMessages();
135
+ });
136
+ }
137
+
138
+ function drainMessages() {
139
+ while (true) {
140
+ const headerEnd = buffer.indexOf('\r\n\r\n');
141
+ if (headerEnd === -1) return;
142
+
143
+ const header = buffer.slice(0, headerEnd).toString('utf8');
144
+ const match = header.match(/content-length:\s*(\d+)/i);
145
+ if (!match) {
146
+ buffer = buffer.slice(headerEnd + 4);
147
+ continue;
148
+ }
149
+
150
+ const length = Number(match[1]);
151
+ const messageStart = headerEnd + 4;
152
+ const messageEnd = messageStart + length;
153
+ if (buffer.length < messageEnd) return;
154
+
155
+ const raw = buffer.slice(messageStart, messageEnd).toString('utf8');
156
+ buffer = buffer.slice(messageEnd);
157
+ void handleRawMessage(raw);
158
+ }
159
+ }
160
+
161
+ async function handleRawMessage(raw) {
162
+ let message;
163
+ try {
164
+ message = JSON.parse(raw);
165
+ } catch {
166
+ return;
167
+ }
168
+
169
+ if (!message || typeof message.method !== 'string') return;
170
+ if (message.id === undefined || message.id === null) return;
171
+
172
+ try {
173
+ const result = await handleRequest(message.method, message.params || {});
174
+ send({ jsonrpc: '2.0', id: message.id, result });
175
+ } catch (error) {
176
+ send({
177
+ jsonrpc: '2.0',
178
+ id: message.id,
179
+ error: {
180
+ code: -32000,
181
+ message: error instanceof Error ? error.message : String(error)
182
+ }
183
+ });
184
+ }
185
+ }
186
+
187
+ async function handleRequest(method, params) {
188
+ if (method === 'initialize') {
189
+ return {
190
+ protocolVersion: params.protocolVersion || '2024-11-05',
191
+ capabilities: { tools: {} },
192
+ serverInfo: SERVER_INFO
193
+ };
194
+ }
195
+
196
+ if (method === 'tools/list') {
197
+ return { tools: TOOLS };
198
+ }
199
+
200
+ if (method === 'tools/call') {
201
+ const name = params.name;
202
+ const args = params.arguments || {};
203
+ const text = await callTool(name, args);
204
+ const parsed = parseJsonObjectFromToolText(text);
205
+ return {
206
+ content: [{ type: 'text', text }],
207
+ ...(parsed ? { structuredContent: parsed } : {})
208
+ };
209
+ }
210
+
211
+ throw new Error(`Unsupported MCP method: ${method}`);
212
+ }
213
+
214
+ async function callTool(name, args) {
215
+ return runVibeRaven(['-y', CLI_PACKAGE, ...buildToolArgs(name, args || {})], args && args.cwd);
216
+ }
217
+
212
218
  function buildToolArgs(name, args) {
213
219
  if (name === 'viberaven_check_readiness') {
214
- return ['--agent-mode'];
215
- }
216
- if (name === 'viberaven_verify') {
217
- return ['--verify'];
218
- }
219
- if (name === 'viberaven_audit') {
220
- return ['audit', '--vercel-supabase', ...(args.json ? ['--json'] : [])];
221
- }
222
- if (name === 'viberaven_init_rules') {
223
- const cliArgs = ['init'];
224
- if (args.agents) cliArgs.push('--agents', safeStringArg(args.agents, 'agents'));
225
- if (args.dryRun) cliArgs.push('--dry-run');
226
- return cliArgs;
227
- }
228
- if (name === 'viberaven_clean_plan') {
229
- return ['clean', '--plan'];
230
- }
231
- if (name === 'viberaven_strict_gate') {
232
- return ['--agent-mode', '--strict', '--json'];
233
- }
234
- if (name === 'viberaven_gate_result') {
235
- return ['--agent-mode', '--json'];
236
- }
237
- if (name === 'viberaven_context_map') {
238
- return ['--condense'];
239
- }
240
- if (name === 'viberaven_heal_plan') {
241
- return buildHealArgs(['--heal', '--plan'], args);
242
- }
243
- if (name === 'viberaven_heal_prompt') {
244
- return buildHealArgs(['--heal', '--prompt'], args);
245
- }
246
- if (name === 'viberaven_heal_apply') {
247
- const cliArgs = buildHealArgs(['--heal', '--apply'], args);
248
- if (args.yes === true) cliArgs.push('--yes');
249
- return cliArgs;
250
- }
251
- if (name === 'viberaven_validate_npm_package') {
252
- const names = [];
253
- if (args.packageName) {
254
- names.push(safeStringArg(args.packageName, 'packageName'));
255
- }
256
- if (Array.isArray(args.packageNames)) {
257
- for (const entry of args.packageNames) {
258
- names.push(safeStringArg(entry, 'packageName'));
259
- }
260
- }
261
- if (names.length === 0) {
262
- throw new Error('packageName or packageNames is required');
263
- }
264
- return ['validate-npm-package', '--json', ...names];
265
- }
266
- throw new Error(`Unknown VibeRaven tool: ${name}`);
267
- }
268
-
269
- function buildHealArgs(prefix, args) {
270
- const cliArgs = [...prefix];
271
- if (args.target) cliArgs.push('--target', safeStringArg(args.target, 'target'));
272
- if (args.gap) cliArgs.push('--gap', safeStringArg(args.gap, 'gap'));
273
- return cliArgs;
274
- }
275
-
276
- function safeStringArg(value, label) {
277
- if (typeof value !== 'string') {
278
- throw new Error(`Invalid ${label}: expected string`);
279
- }
280
-
281
- if (!value.trim()) {
282
- throw new Error(`Invalid ${label}: expected non-empty string`);
220
+ return ['--agent-mode', ...launchModeArgs(args)];
283
221
  }
284
-
285
- if (/[&|<>^%!"\r\n]/.test(value)) {
286
- throw new Error(`Unsafe characters in ${label}`);
287
- }
288
-
289
- return value;
222
+ if (name === 'viberaven_verify') {
223
+ return ['--verify'];
224
+ }
225
+ if (name === 'viberaven_audit') {
226
+ return ['audit', '--vercel-supabase', ...(args.json ? ['--json'] : [])];
227
+ }
228
+ if (name === 'viberaven_init_rules') {
229
+ const cliArgs = ['init'];
230
+ if (args.agents) cliArgs.push('--agents', safeStringArg(args.agents, 'agents'));
231
+ if (args.dryRun) cliArgs.push('--dry-run');
232
+ return cliArgs;
233
+ }
234
+ if (name === 'viberaven_clean_plan') {
235
+ return ['clean', '--plan'];
236
+ }
237
+ if (name === 'viberaven_strict_gate') {
238
+ return ['--agent-mode', '--strict', '--json'];
239
+ }
240
+ if (name === 'viberaven_gate_result') {
241
+ return ['--agent-mode', '--json'];
242
+ }
243
+ if (name === 'viberaven_context_map') {
244
+ return ['--condense'];
245
+ }
246
+ if (name === 'viberaven_heal_plan') {
247
+ return buildHealArgs(['--heal', '--plan'], args);
248
+ }
249
+ if (name === 'viberaven_heal_prompt') {
250
+ return buildHealArgs(['--heal', '--prompt'], args);
251
+ }
252
+ if (name === 'viberaven_heal_apply') {
253
+ const cliArgs = buildHealArgs(['--heal', '--apply'], args);
254
+ if (args.yes === true) cliArgs.push('--yes');
255
+ return cliArgs;
256
+ }
257
+ if (name === 'viberaven_validate_npm_package') {
258
+ const names = [];
259
+ if (args.packageName) {
260
+ names.push(safeStringArg(args.packageName, 'packageName'));
261
+ }
262
+ if (Array.isArray(args.packageNames)) {
263
+ for (const entry of args.packageNames) {
264
+ names.push(safeStringArg(entry, 'packageName'));
265
+ }
266
+ }
267
+ if (names.length === 0) {
268
+ throw new Error('packageName or packageNames is required');
269
+ }
270
+ return ['validate-npm-package', '--json', ...names];
271
+ }
272
+ throw new Error(`Unknown VibeRaven tool: ${name}`);
290
273
  }
291
274
 
292
- function resolveCwd(cwd) {
293
- if (cwd === undefined || cwd === null) {
294
- return process.cwd();
275
+ function launchModeArgs(args) {
276
+ if (!args.launchMode) return [];
277
+ const allowed = new Set(['manual', 'ask', 'safe', 'full']);
278
+ if (!allowed.has(args.launchMode)) {
279
+ throw new Error('Invalid launchMode');
295
280
  }
296
-
297
- if (typeof cwd !== 'string') {
298
- throw new Error('Invalid cwd: expected string');
299
- }
300
-
301
- if (!cwd.trim()) {
302
- throw new Error('Invalid cwd: expected non-empty string');
303
- }
304
-
305
- return cwd;
281
+ return ['--launch-mode', args.launchMode];
306
282
  }
307
-
308
- function buildNpxCommand(args) {
309
- if (process.platform === 'win32') {
310
- return {
311
- command: 'cmd.exe',
312
- args: ['/d', '/s', '/c', 'npx', ...args],
313
- options: { shell: false }
314
- };
315
- }
316
-
317
- return {
318
- command: 'npx',
319
- args,
320
- options: { shell: false }
321
- };
322
- }
323
-
324
- function runVibeRaven(args, cwd) {
325
- return new Promise((resolve) => {
326
- const built = buildNpxCommand(args);
327
- const child = spawnChild(built.command, built.args, {
328
- cwd: resolveCwd(cwd),
329
- env: process.env,
330
- shell: built.options.shell,
331
- stdio: ['ignore', 'pipe', 'pipe']
332
- });
333
- let stdout = '';
334
- let stderr = '';
335
- child.stdout.on('data', (chunk) => {
336
- stdout += chunk.toString();
337
- });
338
- child.stderr.on('data', (chunk) => {
339
- stderr += chunk.toString();
340
- });
341
- child.on('error', (error) => {
342
- resolve(`ERROR: ${error.message}`);
343
- });
344
- child.on('close', (code) => {
345
- const combined = `${stdout}${stderr}`.trim();
346
- resolve(`exit ${code ?? 1}${combined ? `\n${combined}` : ''}`);
347
- });
348
- });
349
- }
350
-
351
- function parseJsonObjectFromToolText(text) {
352
- const jsonStart = text.indexOf('{');
353
- if (jsonStart === -1) return undefined;
354
- try {
355
- return JSON.parse(text.slice(jsonStart));
356
- } catch {
357
- return undefined;
358
- }
359
- }
360
-
361
- function send(message) {
362
- const body = JSON.stringify(message);
363
- process.stdout.write(`Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`);
364
- }
365
-
366
- if (require.main === module) {
367
- main();
368
- }
369
-
370
- module.exports = {
371
- TOOLS,
372
- buildNpxCommand,
373
- callTool,
374
- handleRequest,
375
- parseJsonObjectFromToolText,
376
- safeStringArg,
377
- __setSpawnForTest(spawnForTest) {
378
- spawnChild = spawnForTest;
379
- },
380
- __resetSpawnForTest() {
381
- spawnChild = spawn;
382
- }
383
- };
283
+
284
+ function buildHealArgs(prefix, args) {
285
+ const cliArgs = [...prefix];
286
+ if (args.target) cliArgs.push('--target', safeStringArg(args.target, 'target'));
287
+ if (args.gap) cliArgs.push('--gap', safeStringArg(args.gap, 'gap'));
288
+ return cliArgs;
289
+ }
290
+
291
+ function safeStringArg(value, label) {
292
+ if (typeof value !== 'string') {
293
+ throw new Error(`Invalid ${label}: expected string`);
294
+ }
295
+
296
+ if (!value.trim()) {
297
+ throw new Error(`Invalid ${label}: expected non-empty string`);
298
+ }
299
+
300
+ if (/[&|<>^%!"\r\n]/.test(value)) {
301
+ throw new Error(`Unsafe characters in ${label}`);
302
+ }
303
+
304
+ return value;
305
+ }
306
+
307
+ function resolveCwd(cwd) {
308
+ if (cwd === undefined || cwd === null) {
309
+ return process.cwd();
310
+ }
311
+
312
+ if (typeof cwd !== 'string') {
313
+ throw new Error('Invalid cwd: expected string');
314
+ }
315
+
316
+ if (!cwd.trim()) {
317
+ throw new Error('Invalid cwd: expected non-empty string');
318
+ }
319
+
320
+ return cwd;
321
+ }
322
+
323
+ function buildNpxCommand(args) {
324
+ if (process.platform === 'win32') {
325
+ return {
326
+ command: 'cmd.exe',
327
+ args: ['/d', '/s', '/c', 'npx', ...args],
328
+ options: { shell: false }
329
+ };
330
+ }
331
+
332
+ return {
333
+ command: 'npx',
334
+ args,
335
+ options: { shell: false }
336
+ };
337
+ }
338
+
339
+ function runVibeRaven(args, cwd) {
340
+ return new Promise((resolve) => {
341
+ const built = buildNpxCommand(args);
342
+ const child = spawnChild(built.command, built.args, {
343
+ cwd: resolveCwd(cwd),
344
+ env: process.env,
345
+ shell: built.options.shell,
346
+ stdio: ['ignore', 'pipe', 'pipe']
347
+ });
348
+ let stdout = '';
349
+ let stderr = '';
350
+ child.stdout.on('data', (chunk) => {
351
+ stdout += chunk.toString();
352
+ });
353
+ child.stderr.on('data', (chunk) => {
354
+ stderr += chunk.toString();
355
+ });
356
+ child.on('error', (error) => {
357
+ resolve(`ERROR: ${error.message}`);
358
+ });
359
+ child.on('close', (code) => {
360
+ const combined = `${stdout}${stderr}`.trim();
361
+ resolve(`exit ${code ?? 1}${combined ? `\n${combined}` : ''}`);
362
+ });
363
+ });
364
+ }
365
+
366
+ function parseJsonObjectFromToolText(text) {
367
+ const jsonStart = text.indexOf('{');
368
+ if (jsonStart === -1) return undefined;
369
+ try {
370
+ return JSON.parse(text.slice(jsonStart));
371
+ } catch {
372
+ return undefined;
373
+ }
374
+ }
375
+
376
+ function send(message) {
377
+ const body = JSON.stringify(message);
378
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`);
379
+ }
380
+
381
+ if (require.main === module) {
382
+ main();
383
+ }
384
+
385
+ module.exports = {
386
+ TOOLS,
387
+ buildNpxCommand,
388
+ callTool,
389
+ handleRequest,
390
+ parseJsonObjectFromToolText,
391
+ safeStringArg,
392
+ __setSpawnForTest(spawnForTest) {
393
+ spawnChild = spawnForTest;
394
+ },
395
+ __resetSpawnForTest() {
396
+ spawnChild = spawn;
397
+ }
398
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viberaven/mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server for VibeRaven production-readiness: check_readiness, verify, heal, audit, and strict gate tools for Cursor, Claude Code, and Codex.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "VibeRaven",