vectorforce 0.3.2 → 0.3.4
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/CHANGELOG.md +38 -0
- package/README.md +185 -9
- package/bin/vf.js +142 -55
- package/package.json +1 -1
- package/src/mcp/harness-config.js +785 -40
- package/src/mcp/mcp-server.js +85 -13
- package/src/operations/project-service.js +18 -1
- package/src/policy/authorizer.js +15 -1
package/src/mcp/mcp-server.js
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import { ErrorCode, VectorForceError } from '../core/errors.js';
|
|
2
5
|
|
|
3
|
-
export const MCP_PROTOCOL_VERSION = '
|
|
6
|
+
export const MCP_PROTOCOL_VERSION = '2025-11-25';
|
|
7
|
+
|
|
8
|
+
export const MCP_SUPPORTED_PROTOCOL_VERSIONS = [
|
|
9
|
+
'2025-11-25',
|
|
10
|
+
'2025-06-18',
|
|
11
|
+
'2025-03-26',
|
|
12
|
+
'2024-11-05'
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
function readServerVersion() {
|
|
16
|
+
try {
|
|
17
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'package.json');
|
|
18
|
+
return JSON.parse(readFileSync(pkgPath, 'utf8')).version || '0.3.2';
|
|
19
|
+
} catch {
|
|
20
|
+
return '0.3.2';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const MCP_SERVER_VERSION = readServerVersion();
|
|
25
|
+
|
|
26
|
+
export function negotiateProtocolVersion(clientVersion) {
|
|
27
|
+
if (clientVersion && MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(clientVersion)) {
|
|
28
|
+
return clientVersion;
|
|
29
|
+
}
|
|
30
|
+
return MCP_PROTOCOL_VERSION;
|
|
31
|
+
}
|
|
4
32
|
|
|
5
33
|
export const MCP_TOOLS = [
|
|
6
34
|
{
|
|
@@ -44,7 +72,7 @@ export const MCP_TOOLS = [
|
|
|
44
72
|
task_id: { type: 'string' },
|
|
45
73
|
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID. Defaults to latest head.' },
|
|
46
74
|
max_tokens: { type: 'integer', default: 4000, maximum: 8000 },
|
|
47
|
-
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] }
|
|
75
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'cursor', 'windsurf', 'github-copilot', 'copilot', 'kiro', 'kiro-ide'] }
|
|
48
76
|
}
|
|
49
77
|
}
|
|
50
78
|
},
|
|
@@ -91,7 +119,7 @@ export const MCP_TOOLS = [
|
|
|
91
119
|
task_id: { type: 'string', description: 'Kebab-case task slug' },
|
|
92
120
|
recipient: { type: 'string', description: 'Target developer or audience identity' },
|
|
93
121
|
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID to export' },
|
|
94
|
-
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'any'], default: 'any' },
|
|
122
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'cursor', 'windsurf', 'github-copilot', 'copilot', 'kiro', 'kiro-ide', 'any'], default: 'any' },
|
|
95
123
|
preview: { type: 'boolean', default: false, description: 'Dry run preview without writing file' },
|
|
96
124
|
out_path: { type: 'string', description: 'Optional custom bundle file destination' }
|
|
97
125
|
}
|
|
@@ -117,7 +145,7 @@ export const MCP_TOOLS = [
|
|
|
117
145
|
properties: {
|
|
118
146
|
days: { type: 'integer', default: 7, description: 'Number of past days to include in the recap (defaults to 7)' },
|
|
119
147
|
task_id: { type: 'string', description: 'Optional specific task ID to filter by' },
|
|
120
|
-
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] }
|
|
148
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'cursor', 'windsurf', 'github-copilot', 'copilot', 'kiro', 'kiro-ide'] }
|
|
121
149
|
}
|
|
122
150
|
}
|
|
123
151
|
},
|
|
@@ -130,7 +158,7 @@ export const MCP_TOOLS = [
|
|
|
130
158
|
properties: {
|
|
131
159
|
task_id: { type: 'string', description: 'Kebab-case task slug' },
|
|
132
160
|
checkpoint_id: { type: 'string', description: 'Optional specific checkpoint ID. Defaults to latest head.' },
|
|
133
|
-
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli'] },
|
|
161
|
+
destination_harness: { type: 'string', enum: ['antigravity', 'command-code', 'muse', 'muse-code', 'pi', 'codex', 'cli', 'cursor', 'windsurf', 'github-copilot', 'copilot', 'kiro', 'kiro-ide'] },
|
|
134
162
|
model: { type: 'string', description: 'Optional model override (e.g. a Meta Llama or Muse model id)' },
|
|
135
163
|
focus: { type: 'string', description: 'Optional question or focus for the reasoning pass' },
|
|
136
164
|
include_code: { type: 'boolean', default: true, description: 'Ground reasoning in the live codebase index' }
|
|
@@ -214,14 +242,31 @@ export class McpServer {
|
|
|
214
242
|
return;
|
|
215
243
|
}
|
|
216
244
|
|
|
245
|
+
// JSON-RPC batch: dispatch each entry; entries without an id are notifications.
|
|
246
|
+
if (Array.isArray(req)) {
|
|
247
|
+
if (req.length === 0) {
|
|
248
|
+
this._sendError(null, -32600, 'Invalid Request: empty batch');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
for (const sub of req) {
|
|
252
|
+
this._dispatch(sub);
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this._dispatch(req);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
_dispatch(req) {
|
|
261
|
+
if (!req || typeof req !== 'object' || Array.isArray(req)) {
|
|
262
|
+
this._sendError(null, -32600, 'Invalid Request');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
217
266
|
const { id, method, params } = req;
|
|
218
267
|
|
|
219
|
-
// Notifications (no id)
|
|
268
|
+
// Notifications (no id) are acknowledged silently, never answered.
|
|
220
269
|
if (id === undefined || id === null) {
|
|
221
|
-
if (method === 'notifications/initialized') {
|
|
222
|
-
// Client handshake complete
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
270
|
return;
|
|
226
271
|
}
|
|
227
272
|
|
|
@@ -229,15 +274,17 @@ export class McpServer {
|
|
|
229
274
|
switch (method) {
|
|
230
275
|
case 'initialize': {
|
|
231
276
|
this._sendResult(id, {
|
|
232
|
-
protocolVersion:
|
|
277
|
+
protocolVersion: negotiateProtocolVersion(params?.protocolVersion),
|
|
233
278
|
serverInfo: {
|
|
234
279
|
name: 'vectorforce',
|
|
235
|
-
version:
|
|
280
|
+
version: MCP_SERVER_VERSION
|
|
236
281
|
},
|
|
237
282
|
capabilities: {
|
|
238
283
|
tools: {
|
|
239
284
|
listChanged: false
|
|
240
|
-
}
|
|
285
|
+
},
|
|
286
|
+
resources: {},
|
|
287
|
+
prompts: {}
|
|
241
288
|
}
|
|
242
289
|
});
|
|
243
290
|
break;
|
|
@@ -255,6 +302,31 @@ export class McpServer {
|
|
|
255
302
|
break;
|
|
256
303
|
}
|
|
257
304
|
|
|
305
|
+
case 'resources/list': {
|
|
306
|
+
this._sendResult(id, { resources: [] });
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
case 'resources/templates/list': {
|
|
311
|
+
this._sendResult(id, { resourceTemplates: [] });
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case 'resources/read': {
|
|
316
|
+
this._sendError(id, -32602, `Unknown resource: ${params?.uri || '(missing uri)'}`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
case 'prompts/list': {
|
|
321
|
+
this._sendResult(id, { prompts: [] });
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
case 'prompts/get': {
|
|
326
|
+
this._sendError(id, -32602, `Unknown prompt: ${params?.name || '(missing name)'}`);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
|
|
258
330
|
case 'tools/call': {
|
|
259
331
|
this._handleToolCall(id, params);
|
|
260
332
|
break;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Authorizer } from '../policy/authorizer.js';
|
|
2
2
|
import { sha256 } from '../core/hash.js';
|
|
3
3
|
import { basename, dirname } from 'node:path';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import { ErrorCode, VectorForceError } from '../core/errors.js';
|
|
5
6
|
|
|
6
7
|
export class ProjectService {
|
|
@@ -13,6 +14,8 @@ export class ProjectService {
|
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Initialize or retrieve a project for a workspace directory.
|
|
17
|
+
* Derives a deterministic project identity from Git remote URL if present,
|
|
18
|
+
* ensuring that multiple developers cloning the same repo share the identical project ID.
|
|
16
19
|
* @param {string} workspacePath
|
|
17
20
|
* @param {string} [customName]
|
|
18
21
|
* @returns {{ project_id: string, canonical_path: string, name: string, created_at: string }}
|
|
@@ -23,7 +26,21 @@ export class ProjectService {
|
|
|
23
26
|
if (existing) {
|
|
24
27
|
return existing;
|
|
25
28
|
}
|
|
26
|
-
|
|
29
|
+
|
|
30
|
+
let identitySeed = canonical;
|
|
31
|
+
try {
|
|
32
|
+
const gitRemote = execFileSync('git', ['config', '--get', 'remote.origin.url'], {
|
|
33
|
+
cwd: canonical,
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
36
|
+
timeout: 1000
|
|
37
|
+
}).trim();
|
|
38
|
+
if (gitRemote) {
|
|
39
|
+
identitySeed = gitRemote.toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
|
|
43
|
+
const idHash = sha256(identitySeed).slice(0, 16);
|
|
27
44
|
const projectId = `proj-${idHash}`;
|
|
28
45
|
const name = customName || basename(canonical);
|
|
29
46
|
return this.store.registerProject(projectId, canonical, name);
|
package/src/policy/authorizer.js
CHANGED
|
@@ -5,7 +5,21 @@ import { ErrorCode, VectorForceError } from '../core/errors.js';
|
|
|
5
5
|
export const MAX_FILE_BYTES = 8192; // 8 KiB per file
|
|
6
6
|
export const MAX_TOTAL_DOCUMENT_BYTES = 65536; // 64 KiB total
|
|
7
7
|
export const ALLOWED_EXTENSIONS = ['.md', '.txt'];
|
|
8
|
-
export const APPROVED_DESTINATIONS = [
|
|
8
|
+
export const APPROVED_DESTINATIONS = [
|
|
9
|
+
'antigravity',
|
|
10
|
+
'command-code',
|
|
11
|
+
'muse',
|
|
12
|
+
'muse-code',
|
|
13
|
+
'pi',
|
|
14
|
+
'codex',
|
|
15
|
+
'cli',
|
|
16
|
+
'cursor',
|
|
17
|
+
'windsurf',
|
|
18
|
+
'github-copilot',
|
|
19
|
+
'copilot',
|
|
20
|
+
'kiro',
|
|
21
|
+
'kiro-ide'
|
|
22
|
+
];
|
|
9
23
|
|
|
10
24
|
export class Authorizer {
|
|
11
25
|
/**
|