vibe-splain 3.0.0 → 3.2.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/dist/commands/bundle.d.ts +4 -0
- package/dist/commands/bundle.js +68 -0
- package/dist/commands/gc.d.ts +3 -0
- package/dist/commands/gc.js +59 -0
- package/dist/commands/importBundle.d.ts +4 -0
- package/dist/commands/importBundle.js +80 -0
- package/dist/export/ArtifactBundleWriter.js +24 -6
- package/dist/export/ExportOrchestrator.d.ts +19 -1
- package/dist/export/ExportOrchestrator.js +90 -3
- package/dist/export/Watcher.d.ts +1 -1
- package/dist/export/Watcher.js +9 -1
- package/dist/export/renderers/AgentMarkdownRenderer.d.ts +2 -1
- package/dist/export/renderers/AgentMarkdownRenderer.js +17 -1
- package/dist/export/renderers/HtmlRenderer.js +29 -6
- package/dist/index.js +1671 -129
- package/dist/mcp/BudgetGuard.d.ts +13 -0
- package/dist/mcp/BudgetGuard.js +55 -0
- package/dist/mcp/SessionScope.d.ts +26 -0
- package/dist/mcp/SessionScope.js +56 -0
- package/dist/mcp/server.js +38 -0
- package/dist/mcp/tools/apply_patch.d.ts +37 -0
- package/dist/mcp/tools/apply_patch.js +103 -0
- package/dist/mcp/tools/get_file_skeleton.d.ts +23 -0
- package/dist/mcp/tools/get_file_skeleton.js +124 -0
- package/dist/mcp/tools/hydration/get_evidence_slice.d.ts +31 -0
- package/dist/mcp/tools/hydration/get_evidence_slice.js +59 -0
- package/dist/mcp/tools/hydration/get_project_summary.d.ts +23 -0
- package/dist/mcp/tools/hydration/get_project_summary.js +58 -0
- package/dist/mcp/tools/hydration/get_start_here.d.ts +23 -0
- package/dist/mcp/tools/hydration/get_start_here.js +52 -0
- package/dist/mcp/tools/read_file.d.ts +31 -0
- package/dist/mcp/tools/read_file.js +90 -0
- package/dist/mcp/tools/scan_project.js +6 -3
- package/dist/mcp/tools/set_session_scope.d.ts +19 -0
- package/dist/mcp/tools/set_session_scope.js +40 -0
- package/dist/mcp/tools/submit_receipt.d.ts +68 -0
- package/dist/mcp/tools/submit_receipt.js +94 -0
- package/dist/mcp/tools/work_orders.d.ts +79 -0
- package/dist/mcp/tools/work_orders.js +126 -0
- package/dist/mcp/tools/yield_for_scope_expansion.d.ts +29 -0
- package/dist/mcp/tools/yield_for_scope_expansion.js +59 -0
- package/dist/store/BlobStore.d.ts +22 -0
- package/dist/store/BlobStore.js +96 -0
- package/dist/store/PointerStore.d.ts +52 -0
- package/dist/store/PointerStore.js +138 -0
- package/package.json +8 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const getStartHereTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectRoot: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
manifestPointer: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
scanId: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
required: string[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export declare function handleGetStartHere(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { hydratePointer, applyBudgetGuard } from '../../BudgetGuard.js';
|
|
2
|
+
export const getStartHereTool = {
|
|
3
|
+
name: 'get_start_here',
|
|
4
|
+
description: 'Hydrates the start-here index for a scan manifest pointer. Returns the top 5 highest-gravity files. Pointer must be valid and unexpired.',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
9
|
+
manifestPointer: { type: 'string', description: 'Pointer ID for the scan manifest or analysis.index artifact' },
|
|
10
|
+
scanId: { type: 'string', description: 'Current scan ID for budget pointer registration' },
|
|
11
|
+
},
|
|
12
|
+
required: ['projectRoot', 'manifestPointer', 'scanId'],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function handleGetStartHere(args) {
|
|
16
|
+
const projectRoot = args.projectRoot;
|
|
17
|
+
const manifestPointer = args.manifestPointer;
|
|
18
|
+
const scanId = args.scanId;
|
|
19
|
+
if (!projectRoot || !manifestPointer || !scanId) {
|
|
20
|
+
throw new Error('projectRoot, manifestPointer, and scanId are required');
|
|
21
|
+
}
|
|
22
|
+
// 1. Verify pointer exists, unexpired, hash matches blob
|
|
23
|
+
const { content, row } = await hydratePointer(projectRoot, manifestPointer);
|
|
24
|
+
const payload = JSON.parse(content.toString('utf8'));
|
|
25
|
+
// 2. If it's a manifest pointer, find the analysis.index pointer
|
|
26
|
+
if (row.artifactName === 'artifact_manifest') {
|
|
27
|
+
const manifest = payload;
|
|
28
|
+
const analysisEntry = manifest.artifacts.find(a => a.name === 'analysis' || a.name === 'analysis.index');
|
|
29
|
+
if (!analysisEntry?.indexes?.startHere) {
|
|
30
|
+
throw new Error('Manifest has no analysis.index entry — rescan to regenerate');
|
|
31
|
+
}
|
|
32
|
+
const { content: indexContent } = await hydratePointer(projectRoot, analysisEntry.indexes.startHere);
|
|
33
|
+
const index = JSON.parse(indexContent.toString('utf8'));
|
|
34
|
+
const result = {
|
|
35
|
+
startHere: index.startHere.slice(0, 5),
|
|
36
|
+
schemaVersion: index.schemaVersion,
|
|
37
|
+
scanId: index.scanId,
|
|
38
|
+
};
|
|
39
|
+
return await applyBudgetGuard(projectRoot, scanId, 'get_start_here_result', result);
|
|
40
|
+
}
|
|
41
|
+
// 3. If it's an analysis.index pointer directly
|
|
42
|
+
if (row.artifactName === 'analysis.index') {
|
|
43
|
+
const result = {
|
|
44
|
+
startHere: payload.startHere.slice(0, 5),
|
|
45
|
+
schemaVersion: payload.schemaVersion,
|
|
46
|
+
scanId: payload.scanId,
|
|
47
|
+
};
|
|
48
|
+
return await applyBudgetGuard(projectRoot, scanId, 'get_start_here_result', result);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Unsupported artifact type for get_start_here: ${row.artifactName}`);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=get_start_here.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const readFileTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectRoot: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
filePath: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
scanId: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
startLine: {
|
|
20
|
+
type: string;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
endLine: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
required: string[];
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare function handleReadFile(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { SessionScope, ScopeViolation } from '../SessionScope.js';
|
|
4
|
+
import { hashFile } from '../../store/BlobStore.js';
|
|
5
|
+
import { BlobStore } from '../../store/BlobStore.js';
|
|
6
|
+
import { PointerStore } from '../../store/PointerStore.js';
|
|
7
|
+
import { applyBudgetGuard } from '../BudgetGuard.js';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
export const readFileTool = {
|
|
10
|
+
name: 'read_file',
|
|
11
|
+
description: 'Reads a file within the active workOrder scope. Enforces allowedFiles/allowedGlobs/deniedGlobs. Records content hash. Output is budgeted.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
16
|
+
filePath: { type: 'string', description: 'Path relative to projectRoot' },
|
|
17
|
+
scanId: { type: 'string', description: 'Current scan ID for pointer registration' },
|
|
18
|
+
startLine: { type: 'number', description: 'Optional: 1-based start line to return a slice' },
|
|
19
|
+
endLine: { type: 'number', description: 'Optional: 1-based end line (inclusive). Capped at startLine+500.' },
|
|
20
|
+
},
|
|
21
|
+
required: ['projectRoot', 'filePath', 'scanId'],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export async function handleReadFile(args) {
|
|
25
|
+
const projectRoot = args.projectRoot;
|
|
26
|
+
const filePath = args.filePath;
|
|
27
|
+
const scanId = args.scanId;
|
|
28
|
+
const startLine = args.startLine !== undefined ? Number(args.startLine) : undefined;
|
|
29
|
+
const endLine = args.endLine !== undefined
|
|
30
|
+
? Math.min(Number(args.endLine), (startLine ?? 1) + 500)
|
|
31
|
+
: undefined;
|
|
32
|
+
if (!projectRoot || !filePath || !scanId) {
|
|
33
|
+
throw new Error('projectRoot, filePath, and scanId are required');
|
|
34
|
+
}
|
|
35
|
+
// 1. Scope enforcement
|
|
36
|
+
try {
|
|
37
|
+
SessionScope.enforce(filePath);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (e instanceof ScopeViolation)
|
|
41
|
+
throw e;
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
44
|
+
const absolutePath = filePath.startsWith('/') ? filePath : join(projectRoot, filePath);
|
|
45
|
+
// 2. Read file
|
|
46
|
+
let content;
|
|
47
|
+
try {
|
|
48
|
+
content = await readFile(absolutePath, 'utf8');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error(`FileNotFound: cannot read ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
// 3. Content hash recording
|
|
54
|
+
const contentHash = await hashFile(absolutePath);
|
|
55
|
+
// 4. Apply line slice if requested
|
|
56
|
+
let output = content;
|
|
57
|
+
let sliceInfo;
|
|
58
|
+
if (startLine !== undefined) {
|
|
59
|
+
const lines = content.split('\n');
|
|
60
|
+
const end = endLine ?? lines.length;
|
|
61
|
+
output = lines.slice(startLine - 1, end).join('\n');
|
|
62
|
+
sliceInfo = { startLine, endLine: end, totalLines: lines.length };
|
|
63
|
+
}
|
|
64
|
+
// 5. Record content hash in pointer store
|
|
65
|
+
const blobStore = new BlobStore(projectRoot);
|
|
66
|
+
const pointerStore = PointerStore.open(projectRoot);
|
|
67
|
+
const { blobPath } = await blobStore.writeAtomic(content);
|
|
68
|
+
const pointerId = `ptr_file_${uuidv4().replace(/-/g, '').slice(0, 12)}`;
|
|
69
|
+
await pointerStore.insertPointer({
|
|
70
|
+
pointerId,
|
|
71
|
+
scanId,
|
|
72
|
+
artifactName: 'file_read',
|
|
73
|
+
contentHash,
|
|
74
|
+
blobPath,
|
|
75
|
+
schemaVersion: '1.0.0',
|
|
76
|
+
createdAt: Date.now(),
|
|
77
|
+
expiresAt: null,
|
|
78
|
+
});
|
|
79
|
+
const result = {
|
|
80
|
+
filePath,
|
|
81
|
+
contentHash,
|
|
82
|
+
pointerId,
|
|
83
|
+
content: output,
|
|
84
|
+
};
|
|
85
|
+
if (sliceInfo)
|
|
86
|
+
result.slice = sliceInfo;
|
|
87
|
+
// 6. Budget enforcement
|
|
88
|
+
return await applyBudgetGuard(projectRoot, scanId, 'file_read', result);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=read_file.js.map
|
|
@@ -47,14 +47,15 @@ export async function handleScanProject(args, options = {}) {
|
|
|
47
47
|
dossier.pillars.push({ name: def.name, cardCount: 0, decisions: [] });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
const scanId = `scan_${Date.now()}`;
|
|
50
51
|
const orchestrator = new ExportOrchestrator(projectRoot);
|
|
51
|
-
await orchestrator.writeBundle(dossier, {
|
|
52
|
+
const { manifestPointer } = await orchestrator.writeBundle(dossier, {
|
|
52
53
|
format: options.format,
|
|
53
54
|
budget: options.budget ? parseInt(options.budget, 10) : undefined,
|
|
54
55
|
scope: options.scope,
|
|
55
|
-
}, result.store, result.graph);
|
|
56
|
+
}, result.store, result.graph, scanId);
|
|
56
57
|
// Watch the real-source files for staleness.
|
|
57
|
-
startWatcher(projectRoot, result.files.map(f => f.path));
|
|
58
|
+
await startWatcher(projectRoot, result.files.map(f => f.path));
|
|
58
59
|
console.error(`[vibe-splain] Scan complete. ${result.totalFilesScanned} files, ${result.realSourceCount} real-source, ${result.wildCandidates.length} wild candidates.`);
|
|
59
60
|
const validation = result.validation ?? { passed: true, errors: 0, warnings: 0, reportPath: '.vibe-splainer/validation_report.json' };
|
|
60
61
|
let statusMsg = 'Scan complete.';
|
|
@@ -64,6 +65,8 @@ export async function handleScanProject(args, options = {}) {
|
|
|
64
65
|
return {
|
|
65
66
|
ok: true,
|
|
66
67
|
message: statusMsg,
|
|
68
|
+
scanId,
|
|
69
|
+
manifestPointer,
|
|
67
70
|
validation: {
|
|
68
71
|
passed: validation.passed,
|
|
69
72
|
errors: validation.errors,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const setSessionScopeTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectRoot: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
workOrderId: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
required: string[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare function handleSetSessionScope(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PointerStore } from '../../store/PointerStore.js';
|
|
2
|
+
import { SessionScope } from '../SessionScope.js';
|
|
3
|
+
export const setSessionScopeTool = {
|
|
4
|
+
name: 'set_session_scope',
|
|
5
|
+
description: 'Sets the active session scope from a Work Order. All subsequent file tools (read_file, get_file_skeleton, apply_patch) will enforce this scope until overwritten or the server restarts.',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
10
|
+
workOrderId: { type: 'string', description: 'Work Order ID to load scope from' },
|
|
11
|
+
},
|
|
12
|
+
required: ['projectRoot', 'workOrderId'],
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export async function handleSetSessionScope(args) {
|
|
16
|
+
const projectRoot = args.projectRoot;
|
|
17
|
+
const workOrderId = args.workOrderId;
|
|
18
|
+
if (!projectRoot || !workOrderId) {
|
|
19
|
+
throw new Error('projectRoot and workOrderId are required');
|
|
20
|
+
}
|
|
21
|
+
const pointerStore = PointerStore.open(projectRoot);
|
|
22
|
+
const row = pointerStore.getWorkOrder(workOrderId);
|
|
23
|
+
if (!row) {
|
|
24
|
+
throw new Error(`WorkOrderNotFound: ${workOrderId}`);
|
|
25
|
+
}
|
|
26
|
+
const policy = SessionScope.fromWorkOrderRow(row);
|
|
27
|
+
SessionScope.set(policy);
|
|
28
|
+
return {
|
|
29
|
+
ok: true,
|
|
30
|
+
workOrderId,
|
|
31
|
+
scope: {
|
|
32
|
+
allowedFiles: policy.allowedFiles,
|
|
33
|
+
allowedGlobs: policy.allowedGlobs,
|
|
34
|
+
deniedGlobs: policy.deniedGlobs,
|
|
35
|
+
requiredProofCount: policy.requiredProof.length,
|
|
36
|
+
},
|
|
37
|
+
message: `Session scope set from work order ${workOrderId}. All file tools will enforce this scope.`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=set_session_scope.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export declare const submitReceiptTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectRoot: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
receipt: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
properties: {
|
|
15
|
+
workOrderId: {
|
|
16
|
+
type: string;
|
|
17
|
+
};
|
|
18
|
+
status: {
|
|
19
|
+
type: string;
|
|
20
|
+
enum: string[];
|
|
21
|
+
};
|
|
22
|
+
proofPointers: {
|
|
23
|
+
type: string;
|
|
24
|
+
items: {
|
|
25
|
+
type: string;
|
|
26
|
+
properties: {
|
|
27
|
+
pointer: {
|
|
28
|
+
type: string;
|
|
29
|
+
};
|
|
30
|
+
schemaName: {
|
|
31
|
+
type: string;
|
|
32
|
+
};
|
|
33
|
+
contentHash: {
|
|
34
|
+
type: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
required: string[];
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
changedFiles: {
|
|
41
|
+
type: string;
|
|
42
|
+
items: {
|
|
43
|
+
type: string;
|
|
44
|
+
properties: {
|
|
45
|
+
path: {
|
|
46
|
+
type: string;
|
|
47
|
+
};
|
|
48
|
+
prePatchHash: {
|
|
49
|
+
type: string;
|
|
50
|
+
};
|
|
51
|
+
postPatchHash: {
|
|
52
|
+
type: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
required: string[];
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
summary: {
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
required: string[];
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
required: string[];
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export declare function handleSubmitReceipt(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { PointerStore } from '../../store/PointerStore.js';
|
|
3
|
+
import { ProofValidator } from '@vibe-splain/brain';
|
|
4
|
+
import { minimatch } from 'minimatch';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
export const submitReceiptTool = {
|
|
7
|
+
name: 'submit_receipt',
|
|
8
|
+
description: 'Worker submits a WorkerReceipt for a completed Work Order. The ProofValidator checks all 8 proof conditions. Returns accept/reject with detailed errors.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
13
|
+
receipt: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
description: 'WorkerReceipt object',
|
|
16
|
+
properties: {
|
|
17
|
+
workOrderId: { type: 'string' },
|
|
18
|
+
status: { type: 'string', enum: ['completed', 'failed', 'blocked'] },
|
|
19
|
+
proofPointers: {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
pointer: { type: 'string' },
|
|
25
|
+
schemaName: { type: 'string' },
|
|
26
|
+
contentHash: { type: 'string' },
|
|
27
|
+
},
|
|
28
|
+
required: ['pointer', 'schemaName', 'contentHash'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
changedFiles: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
path: { type: 'string' },
|
|
37
|
+
prePatchHash: { type: 'string' },
|
|
38
|
+
postPatchHash: { type: 'string' },
|
|
39
|
+
},
|
|
40
|
+
required: ['path', 'prePatchHash', 'postPatchHash'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
summary: { type: 'string' },
|
|
44
|
+
},
|
|
45
|
+
required: ['workOrderId', 'status', 'proofPointers', 'changedFiles', 'summary'],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['projectRoot', 'receipt'],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export async function handleSubmitReceipt(args) {
|
|
52
|
+
const projectRoot = args.projectRoot;
|
|
53
|
+
const receipt = args.receipt;
|
|
54
|
+
if (!projectRoot || !receipt) {
|
|
55
|
+
throw new Error('projectRoot and receipt are required');
|
|
56
|
+
}
|
|
57
|
+
const pointerStore = PointerStore.open(projectRoot);
|
|
58
|
+
const workOrder = pointerStore.getWorkOrder(receipt.workOrderId);
|
|
59
|
+
if (!workOrder) {
|
|
60
|
+
throw new Error(`WorkOrderNotFound: ${receipt.workOrderId}`);
|
|
61
|
+
}
|
|
62
|
+
if (workOrder.status !== 'active') {
|
|
63
|
+
throw new Error(`WorkOrderNotActive: ${receipt.workOrderId} is "${workOrder.status}", expected "active"`);
|
|
64
|
+
}
|
|
65
|
+
const allowedFiles = JSON.parse(workOrder.allowedFiles);
|
|
66
|
+
const allowedGlobs = JSON.parse(workOrder.allowedGlobs);
|
|
67
|
+
const requiredProof = JSON.parse(workOrder.requiredProof);
|
|
68
|
+
const blobDir = join(projectRoot, '.vibe-splainer', 'blobs');
|
|
69
|
+
const isAllowedFile = (filePath) => {
|
|
70
|
+
const inExplicit = allowedFiles.some(f => filePath === f || filePath.endsWith('/' + f) || filePath.endsWith(f));
|
|
71
|
+
const inGlobs = allowedGlobs.some(g => minimatch(filePath, g, { matchBase: true }));
|
|
72
|
+
return inExplicit || inGlobs;
|
|
73
|
+
};
|
|
74
|
+
const validation = await ProofValidator.validate(receipt, requiredProof, isAllowedFile, blobDir);
|
|
75
|
+
const receiptId = `rcpt_${uuidv4().replace(/-/g, '').slice(0, 16)}`;
|
|
76
|
+
const finalStatus = validation.valid ? receipt.status : 'failed';
|
|
77
|
+
await pointerStore.insertReceipt({
|
|
78
|
+
receiptId,
|
|
79
|
+
workOrderId: receipt.workOrderId,
|
|
80
|
+
status: finalStatus,
|
|
81
|
+
proofPointers: receipt.proofPointers,
|
|
82
|
+
changedFiles: receipt.changedFiles,
|
|
83
|
+
summary: receipt.summary,
|
|
84
|
+
});
|
|
85
|
+
await pointerStore.updateWorkOrderStatus(receipt.workOrderId, validation.valid ? (receipt.status === 'completed' ? 'completed' : 'failed') : 'failed');
|
|
86
|
+
return {
|
|
87
|
+
receiptId,
|
|
88
|
+
accepted: validation.valid,
|
|
89
|
+
workOrderId: receipt.workOrderId,
|
|
90
|
+
validation,
|
|
91
|
+
finalStatus,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=submit_receipt.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export declare const createWorkOrderTool: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object";
|
|
6
|
+
properties: {
|
|
7
|
+
projectRoot: {
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
intent: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
allowedFiles: {
|
|
16
|
+
type: string;
|
|
17
|
+
items: {
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
description: string;
|
|
21
|
+
};
|
|
22
|
+
allowedGlobs: {
|
|
23
|
+
type: string;
|
|
24
|
+
items: {
|
|
25
|
+
type: string;
|
|
26
|
+
};
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
deniedGlobs: {
|
|
30
|
+
type: string;
|
|
31
|
+
items: {
|
|
32
|
+
type: string;
|
|
33
|
+
};
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
requiredProof: {
|
|
37
|
+
type: string;
|
|
38
|
+
items: {
|
|
39
|
+
type: string;
|
|
40
|
+
properties: {
|
|
41
|
+
proofId: {
|
|
42
|
+
type: string;
|
|
43
|
+
};
|
|
44
|
+
schemaName: {
|
|
45
|
+
type: string;
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
description: {
|
|
49
|
+
type: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
required: string[];
|
|
53
|
+
};
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
required: string[];
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export declare function handleCreateWorkOrder(args: Record<string, unknown>): Promise<unknown>;
|
|
61
|
+
export declare const spawnWorkerTool: {
|
|
62
|
+
name: string;
|
|
63
|
+
description: string;
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object";
|
|
66
|
+
properties: {
|
|
67
|
+
projectRoot: {
|
|
68
|
+
type: string;
|
|
69
|
+
description: string;
|
|
70
|
+
};
|
|
71
|
+
workOrderId: {
|
|
72
|
+
type: string;
|
|
73
|
+
description: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
required: string[];
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
export declare function handleSpawnWorker(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { PointerStore } from '../../store/PointerStore.js';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
export const createWorkOrderTool = {
|
|
4
|
+
name: 'create_work_order',
|
|
5
|
+
description: 'Creates a new Work Order defining intent, allowed file scope, and required verifiable proof. Returns the workOrderId and a manifestPointer for use with spawn_worker.',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
10
|
+
intent: { type: 'string', description: 'Plain-language description of what the worker should do' },
|
|
11
|
+
allowedFiles: {
|
|
12
|
+
type: 'array',
|
|
13
|
+
items: { type: 'string' },
|
|
14
|
+
description: 'Explicit file paths (relative to projectRoot) the worker may read/write',
|
|
15
|
+
},
|
|
16
|
+
allowedGlobs: {
|
|
17
|
+
type: 'array',
|
|
18
|
+
items: { type: 'string' },
|
|
19
|
+
description: 'Glob patterns for allowed files',
|
|
20
|
+
},
|
|
21
|
+
deniedGlobs: {
|
|
22
|
+
type: 'array',
|
|
23
|
+
items: { type: 'string' },
|
|
24
|
+
description: 'Glob patterns that override allowedFiles/allowedGlobs',
|
|
25
|
+
},
|
|
26
|
+
requiredProof: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
proofId: { type: 'string' },
|
|
32
|
+
schemaName: { type: 'string', description: 'e.g. test_report.v1, patch_hash' },
|
|
33
|
+
description: { type: 'string' },
|
|
34
|
+
},
|
|
35
|
+
required: ['proofId', 'schemaName', 'description'],
|
|
36
|
+
},
|
|
37
|
+
description: 'Machine-verifiable evidence the worker must provide',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['projectRoot', 'intent', 'allowedFiles'],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export async function handleCreateWorkOrder(args) {
|
|
44
|
+
const projectRoot = args.projectRoot;
|
|
45
|
+
const intent = args.intent;
|
|
46
|
+
const allowedFiles = args.allowedFiles ?? [];
|
|
47
|
+
const allowedGlobs = args.allowedGlobs ?? [];
|
|
48
|
+
const deniedGlobs = args.deniedGlobs ?? [];
|
|
49
|
+
const requiredProof = args.requiredProof ?? [];
|
|
50
|
+
if (!projectRoot || !intent) {
|
|
51
|
+
throw new Error('projectRoot and intent are required');
|
|
52
|
+
}
|
|
53
|
+
const workOrderId = `wo_${uuidv4().replace(/-/g, '').slice(0, 16)}`;
|
|
54
|
+
const pointerStore = PointerStore.open(projectRoot);
|
|
55
|
+
await pointerStore.insertWorkOrder({
|
|
56
|
+
workOrderId,
|
|
57
|
+
intent,
|
|
58
|
+
allowedFiles: JSON.stringify(allowedFiles),
|
|
59
|
+
allowedGlobs: JSON.stringify(allowedGlobs),
|
|
60
|
+
deniedGlobs: JSON.stringify(deniedGlobs),
|
|
61
|
+
requiredProof: JSON.stringify(requiredProof),
|
|
62
|
+
status: 'pending',
|
|
63
|
+
createdAt: Date.now(),
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
workOrderId,
|
|
68
|
+
intent,
|
|
69
|
+
allowedFiles,
|
|
70
|
+
allowedGlobs,
|
|
71
|
+
deniedGlobs,
|
|
72
|
+
requiredProof,
|
|
73
|
+
nextStep: `Call spawn_worker with workOrderId "${workOrderId}" to generate a DelegationRequest`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export const spawnWorkerTool = {
|
|
77
|
+
name: 'spawn_worker',
|
|
78
|
+
description: 'Generates a DelegationRequest from a Work Order. The Client Orchestrator uses this object to spawn an isolated Worker session. The MCP server does NOT spawn any subprocess.',
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
83
|
+
workOrderId: { type: 'string', description: 'ID returned by create_work_order' },
|
|
84
|
+
},
|
|
85
|
+
required: ['projectRoot', 'workOrderId'],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
export async function handleSpawnWorker(args) {
|
|
89
|
+
const projectRoot = args.projectRoot;
|
|
90
|
+
const workOrderId = args.workOrderId;
|
|
91
|
+
if (!projectRoot || !workOrderId) {
|
|
92
|
+
throw new Error('projectRoot and workOrderId are required');
|
|
93
|
+
}
|
|
94
|
+
const pointerStore = PointerStore.open(projectRoot);
|
|
95
|
+
const row = pointerStore.getWorkOrder(workOrderId);
|
|
96
|
+
if (!row) {
|
|
97
|
+
throw new Error(`WorkOrderNotFound: ${workOrderId}`);
|
|
98
|
+
}
|
|
99
|
+
if (row.status === 'completed' || row.status === 'failed' || row.status === 'active') {
|
|
100
|
+
throw new Error(`WorkOrderClosed: ${workOrderId} is already ${row.status}`);
|
|
101
|
+
}
|
|
102
|
+
await pointerStore.updateWorkOrderStatus(workOrderId, 'active');
|
|
103
|
+
const delegationRequest = {
|
|
104
|
+
schemaVersion: '1.0.0',
|
|
105
|
+
workOrderId: row.workOrderId,
|
|
106
|
+
intent: row.intent,
|
|
107
|
+
sessionScope: {
|
|
108
|
+
allowedFiles: JSON.parse(row.allowedFiles),
|
|
109
|
+
allowedGlobs: JSON.parse(row.allowedGlobs),
|
|
110
|
+
deniedGlobs: JSON.parse(row.deniedGlobs),
|
|
111
|
+
},
|
|
112
|
+
requiredProof: JSON.parse(row.requiredProof),
|
|
113
|
+
instructions: [
|
|
114
|
+
`1. Call set_session_scope with workOrderId "${workOrderId}" before any file operations.`,
|
|
115
|
+
'2. Only read/write files within the sessionScope.',
|
|
116
|
+
'3. If you need a file outside scope, call yield_for_scope_expansion — do NOT proceed.',
|
|
117
|
+
'4. On completion, call submit_receipt with proof for every requiredProof entry.',
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
ok: true,
|
|
122
|
+
delegationRequest,
|
|
123
|
+
note: 'The Client Orchestrator must spawn the Worker session using this DelegationRequest. The MCP server does not spawn subprocesses.',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=work_orders.js.map
|