vibe-splain 3.4.1 → 3.5.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 +26 -20
- package/dist/commands/hook.d.ts +9 -0
- package/dist/commands/hook.js +84 -0
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +58 -9
- package/dist/export/ExportOrchestrator.js +8 -6
- package/dist/export/renderers/AgentMarkdownRenderer.d.ts +1 -2
- package/dist/export/renderers/AgentMarkdownRenderer.js +1 -17
- package/dist/hook/preToolUse.d.ts +30 -0
- package/dist/hook/preToolUse.js +81 -0
- package/dist/hook.d.ts +1 -0
- package/dist/hook.js +337 -0
- package/dist/index.js +2336 -2825
- package/dist/mcp/server.js +1 -25
- package/dist/mcp/tools/hydration/get_evidence_slice.js +1 -3
- package/dist/mcp/tools/scan_project.d.ts +15 -0
- package/dist/mcp/tools/scan_project.js +16 -13
- package/dist/ui/index.html +3 -3
- package/package.json +2 -2
- package/dist/commands/bundle.d.ts +0 -4
- package/dist/commands/bundle.js +0 -68
- package/dist/commands/export.d.ts +0 -1
- package/dist/commands/export.js +0 -19
- package/dist/commands/gc.d.ts +0 -3
- package/dist/commands/gc.js +0 -59
- package/dist/commands/importBundle.d.ts +0 -4
- package/dist/commands/importBundle.js +0 -80
- package/dist/export/renderers/DeltaRenderer.d.ts +0 -6
- package/dist/export/renderers/DeltaRenderer.js +0 -22
- package/dist/mcp/tools/apply_patch.d.ts +0 -37
- package/dist/mcp/tools/apply_patch.js +0 -103
- package/dist/mcp/tools/get_call_chain.d.ts +0 -42
- package/dist/mcp/tools/get_call_chain.js +0 -50
- package/dist/mcp/tools/set_session_scope.d.ts +0 -19
- package/dist/mcp/tools/set_session_scope.js +0 -40
- package/dist/mcp/tools/submit_receipt.d.ts +0 -68
- package/dist/mcp/tools/submit_receipt.js +0 -94
- package/dist/mcp/tools/work_orders.d.ts +0 -79
- package/dist/mcp/tools/work_orders.js +0 -126
- package/dist/mcp/tools/yield_for_scope_expansion.d.ts +0 -29
- package/dist/mcp/tools/yield_for_scope_expansion.js +0 -59
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-splain",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"description": "Static-analysis MRI for codebases: a Tree-Sitter dossier engine and deterministic PreToolUse safety gate for coding agents. Language-agnostic core, optimized for TypeScript/JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "abp2204",
|
package/dist/commands/bundle.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { writeFile, mkdir, copyFile, rm } from 'fs/promises';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
4
|
-
import * as tar from 'tar';
|
|
5
|
-
import { PointerStore } from '../store/PointerStore.js';
|
|
6
|
-
import { BlobStore } from '../store/BlobStore.js';
|
|
7
|
-
export async function bundleCommand(scanId, opts = {}) {
|
|
8
|
-
const root = opts.projectRoot ?? process.cwd();
|
|
9
|
-
const outputPath = opts.output ?? join(root, `vibe-bundle-${scanId}.tar.gz`);
|
|
10
|
-
console.error(`[vibe-splain bundle] Bundling scan ${scanId} from ${root}`);
|
|
11
|
-
const pointerStore = PointerStore.open(root);
|
|
12
|
-
const blobStore = new BlobStore(root);
|
|
13
|
-
const pointers = pointerStore.listPointersByScan(scanId);
|
|
14
|
-
if (pointers.length === 0) {
|
|
15
|
-
throw new Error(`No pointers found for scanId "${scanId}"`);
|
|
16
|
-
}
|
|
17
|
-
// Stage bundle into a temp directory with predictable layout
|
|
18
|
-
const stagingDir = join(root, '.vibe-splainer', 'tmp', `bundle-stage-${scanId}`);
|
|
19
|
-
const blobsStageDir = join(stagingDir, 'blobs');
|
|
20
|
-
await mkdir(blobsStageDir, { recursive: true });
|
|
21
|
-
try {
|
|
22
|
-
// Build manifest for the bundle
|
|
23
|
-
const bundleManifest = {
|
|
24
|
-
schemaVersion: '1.0.0',
|
|
25
|
-
scanId,
|
|
26
|
-
exportedAt: new Date().toISOString(),
|
|
27
|
-
projectRoot: root,
|
|
28
|
-
pointers: pointers.map(p => ({
|
|
29
|
-
pointerId: p.pointerId,
|
|
30
|
-
scanId: p.scanId,
|
|
31
|
-
artifactName: p.artifactName,
|
|
32
|
-
contentHash: p.contentHash,
|
|
33
|
-
blobFile: `blobs/${p.contentHash.replace('sha256:', 'sha256_')}`,
|
|
34
|
-
schemaVersion: p.schemaVersion,
|
|
35
|
-
createdAt: p.createdAt,
|
|
36
|
-
expiresAt: p.expiresAt,
|
|
37
|
-
})),
|
|
38
|
-
};
|
|
39
|
-
await writeFile(join(stagingDir, 'bundle-manifest.json'), JSON.stringify(bundleManifest, null, 2), 'utf8');
|
|
40
|
-
// Copy blobs (deduplicated by contentHash)
|
|
41
|
-
const seen = new Set();
|
|
42
|
-
for (const p of pointers) {
|
|
43
|
-
const hex = p.contentHash.replace('sha256:', '');
|
|
44
|
-
if (seen.has(hex))
|
|
45
|
-
continue;
|
|
46
|
-
seen.add(hex);
|
|
47
|
-
const srcPath = p.blobPath;
|
|
48
|
-
if (!existsSync(srcPath)) {
|
|
49
|
-
console.error(`[vibe-splain bundle] Warning: blob missing for ${p.pointerId}: ${srcPath}`);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
await copyFile(srcPath, join(blobsStageDir, `sha256_${hex}`));
|
|
53
|
-
}
|
|
54
|
-
// Create tarball from staging directory
|
|
55
|
-
await tar.create({
|
|
56
|
-
gzip: true,
|
|
57
|
-
file: outputPath,
|
|
58
|
-
cwd: stagingDir,
|
|
59
|
-
portable: true,
|
|
60
|
-
}, ['.']);
|
|
61
|
-
console.error(`[vibe-splain bundle] Bundle written: ${outputPath}`);
|
|
62
|
-
console.error(`[vibe-splain bundle] ${pointers.length} pointers, ${seen.size} blobs`);
|
|
63
|
-
}
|
|
64
|
-
finally {
|
|
65
|
-
await rm(stagingDir, { recursive: true, force: true });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
//# sourceMappingURL=bundle.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function exportCommand(projectRoot: string, options: any): Promise<void>;
|
package/dist/commands/export.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { readDossier } from '@vibe-splain/brain';
|
|
2
|
-
import { ExportOrchestrator } from '../export/ExportOrchestrator.js';
|
|
3
|
-
export async function exportCommand(projectRoot, options) {
|
|
4
|
-
const root = projectRoot || process.cwd();
|
|
5
|
-
console.error(`[vibe-splain] Exporting dossier for ${root}`);
|
|
6
|
-
const dossier = await readDossier(root);
|
|
7
|
-
if (!dossier) {
|
|
8
|
-
console.error('[vibe-splain] Dossier not found. Run scan first.');
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
11
|
-
const orchestrator = new ExportOrchestrator(root);
|
|
12
|
-
await orchestrator.writeBundle(dossier, {
|
|
13
|
-
format: options.format,
|
|
14
|
-
budget: options.budget ? parseInt(options.budget, 10) : undefined,
|
|
15
|
-
scope: options.scope,
|
|
16
|
-
});
|
|
17
|
-
console.error('[vibe-splain] Export complete.');
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=export.js.map
|
package/dist/commands/gc.d.ts
DELETED
package/dist/commands/gc.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { rm, readdir } from 'fs/promises';
|
|
3
|
-
import { PointerStore } from '../store/PointerStore.js';
|
|
4
|
-
import { BlobStore } from '../store/BlobStore.js';
|
|
5
|
-
const DEFAULT_KEEP_SCANS = 3;
|
|
6
|
-
export async function gcCommand(projectRoot, opts = {}) {
|
|
7
|
-
const root = projectRoot ?? process.cwd();
|
|
8
|
-
const keepScans = opts.keepScans ?? DEFAULT_KEEP_SCANS;
|
|
9
|
-
console.error(`[vibe-splain gc] Running GC on ${root} (keeping last ${keepScans} scans)`);
|
|
10
|
-
const pointerStore = PointerStore.open(root);
|
|
11
|
-
const blobStore = new BlobStore(root);
|
|
12
|
-
// 1. Get all scan IDs ordered by createdAt desc
|
|
13
|
-
const allScanIds = pointerStore.listAllScanIds();
|
|
14
|
-
console.error(`[vibe-splain gc] Found ${allScanIds.length} scans`);
|
|
15
|
-
// Keep the N most recent by taking last N from sorted list
|
|
16
|
-
// Scan IDs contain timestamps, sort lexicographically descending
|
|
17
|
-
const sorted = [...allScanIds].sort().reverse();
|
|
18
|
-
const keepIds = sorted.slice(0, keepScans);
|
|
19
|
-
const deleteIds = sorted.slice(keepScans);
|
|
20
|
-
if (deleteIds.length === 0) {
|
|
21
|
-
console.error('[vibe-splain gc] Nothing to collect');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// 2. Collect all blob paths still referenced by kept pointers before deletion
|
|
25
|
-
const keptPointers = keepIds.flatMap(id => pointerStore.listPointersByScan(id));
|
|
26
|
-
const referencedBlobs = new Set(keptPointers.map(p => p.blobPath));
|
|
27
|
-
// 3. Delete old scan pointers
|
|
28
|
-
const deleted = await pointerStore.gcScanPointers(keepIds);
|
|
29
|
-
console.error(`[vibe-splain gc] Deleted ${deleted} pointer rows`);
|
|
30
|
-
// 4. Delete unreferenced blobs (reference count = 0)
|
|
31
|
-
const allBlobs = await blobStore.listBlobPaths();
|
|
32
|
-
let blobsDeleted = 0;
|
|
33
|
-
for (const blobPath of allBlobs) {
|
|
34
|
-
if (!referencedBlobs.has(blobPath)) {
|
|
35
|
-
try {
|
|
36
|
-
await rm(blobPath);
|
|
37
|
-
blobsDeleted++;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// ignore — may have already been deleted
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
console.error(`[vibe-splain gc] Deleted ${blobsDeleted} unreferenced blobs`);
|
|
45
|
-
// 5. Clean up tmp dir
|
|
46
|
-
const tmpDir = join(root, '.vibe-splainer', 'tmp');
|
|
47
|
-
try {
|
|
48
|
-
const tmpFiles = await readdir(tmpDir);
|
|
49
|
-
for (const f of tmpFiles) {
|
|
50
|
-
await rm(join(tmpDir, f), { force: true });
|
|
51
|
-
}
|
|
52
|
-
console.error(`[vibe-splain gc] Cleaned ${tmpFiles.length} tmp files`);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// tmp dir may not exist
|
|
56
|
-
}
|
|
57
|
-
console.error('[vibe-splain gc] Done');
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=gc.js.map
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { readFile, mkdir, rm } from 'fs/promises';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
4
|
-
import * as tar from 'tar';
|
|
5
|
-
import { createHash } from 'crypto';
|
|
6
|
-
import { BlobStore } from '../store/BlobStore.js';
|
|
7
|
-
import { PointerStore } from '../store/PointerStore.js';
|
|
8
|
-
export async function importBundleCommand(tarballPath, opts = {}) {
|
|
9
|
-
const root = opts.projectRoot ?? process.cwd();
|
|
10
|
-
const namespace = opts.namespace ?? `imported_${Date.now()}`;
|
|
11
|
-
console.error(`[vibe-splain import] Importing ${tarballPath} into ${root} (namespace: ${namespace})`);
|
|
12
|
-
if (!existsSync(tarballPath)) {
|
|
13
|
-
throw new Error(`Tarball not found: ${tarballPath}`);
|
|
14
|
-
}
|
|
15
|
-
// Extract to a temp directory
|
|
16
|
-
const extractDir = join(root, '.vibe-splainer', 'tmp', `import-${namespace}`);
|
|
17
|
-
await mkdir(extractDir, { recursive: true });
|
|
18
|
-
try {
|
|
19
|
-
await tar.extract({
|
|
20
|
-
file: tarballPath,
|
|
21
|
-
cwd: extractDir,
|
|
22
|
-
});
|
|
23
|
-
// Read bundle manifest
|
|
24
|
-
const manifestPath = join(extractDir, 'bundle-manifest.json');
|
|
25
|
-
if (!existsSync(manifestPath)) {
|
|
26
|
-
throw new Error('Invalid bundle: missing bundle-manifest.json');
|
|
27
|
-
}
|
|
28
|
-
const manifestRaw = await readFile(manifestPath, 'utf8');
|
|
29
|
-
const manifest = JSON.parse(manifestRaw);
|
|
30
|
-
if (manifest.schemaVersion !== '1.0.0') {
|
|
31
|
-
throw new Error(`Unsupported bundle schema version: ${manifest.schemaVersion}`);
|
|
32
|
-
}
|
|
33
|
-
const blobStore = new BlobStore(root);
|
|
34
|
-
const pointerStore = PointerStore.open(root);
|
|
35
|
-
await blobStore.ensureDirs();
|
|
36
|
-
let imported = 0;
|
|
37
|
-
let hashErrors = 0;
|
|
38
|
-
for (const entry of manifest.pointers) {
|
|
39
|
-
const blobSrcPath = join(extractDir, entry.blobFile);
|
|
40
|
-
if (!existsSync(blobSrcPath)) {
|
|
41
|
-
console.error(`[vibe-splain import] Missing blob for pointer ${entry.pointerId}: ${entry.blobFile}`);
|
|
42
|
-
hashErrors++;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
// Verify hash before importing
|
|
46
|
-
const content = await readFile(blobSrcPath);
|
|
47
|
-
const actualHash = `sha256:${createHash('sha256').update(content).digest('hex')}`;
|
|
48
|
-
if (actualHash !== entry.contentHash) {
|
|
49
|
-
console.error(`[vibe-splain import] Hash mismatch for ${entry.pointerId}: expected ${entry.contentHash}, got ${actualHash}`);
|
|
50
|
-
hashErrors++;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
// Write blob to local store (atomic)
|
|
54
|
-
const { blobPath } = await blobStore.writeAtomic(content);
|
|
55
|
-
// Insert pointer under bundle namespace alias
|
|
56
|
-
const namespacedPointerId = `${namespace}::${entry.pointerId}`;
|
|
57
|
-
const namespacedScanId = `${namespace}::${entry.scanId}`;
|
|
58
|
-
await pointerStore.insertPointer({
|
|
59
|
-
pointerId: namespacedPointerId,
|
|
60
|
-
scanId: namespacedScanId,
|
|
61
|
-
artifactName: entry.artifactName,
|
|
62
|
-
contentHash: entry.contentHash,
|
|
63
|
-
blobPath,
|
|
64
|
-
schemaVersion: entry.schemaVersion,
|
|
65
|
-
createdAt: entry.createdAt,
|
|
66
|
-
expiresAt: entry.expiresAt,
|
|
67
|
-
});
|
|
68
|
-
imported++;
|
|
69
|
-
}
|
|
70
|
-
if (hashErrors > 0) {
|
|
71
|
-
console.error(`[vibe-splain import] Warning: ${hashErrors} blobs failed hash verification and were skipped`);
|
|
72
|
-
}
|
|
73
|
-
console.error(`[vibe-splain import] Imported ${imported}/${manifest.pointers.length} pointers under namespace "${namespace}"`);
|
|
74
|
-
console.error(`[vibe-splain import] Original scanId: ${manifest.scanId} → namespaced as: ${namespace}::${manifest.scanId}`);
|
|
75
|
-
}
|
|
76
|
-
finally {
|
|
77
|
-
await rm(extractDir, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
//# sourceMappingURL=importBundle.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
2
|
-
import type { Renderer } from './Renderer.js';
|
|
3
|
-
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
|
-
export declare class DeltaRenderer implements Renderer {
|
|
5
|
-
render(_viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
|
|
6
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export class DeltaRenderer {
|
|
2
|
-
render(_viewModel, store) {
|
|
3
|
-
const deltaTargets = Object.values(store.files)
|
|
4
|
-
.filter(pf => pf.isRealSource)
|
|
5
|
-
.sort((a, b) => b.gravity - a.gravity)
|
|
6
|
-
.map(pf => ({
|
|
7
|
-
path: pf.relativePath,
|
|
8
|
-
gravity: Math.round(pf.gravity),
|
|
9
|
-
isLoadBearing: pf.canonicalLoadBearing,
|
|
10
|
-
blastRadius: pf.importedBy,
|
|
11
|
-
pillarHint: pf.pillarHint,
|
|
12
|
-
}));
|
|
13
|
-
return [
|
|
14
|
-
{
|
|
15
|
-
type: 'delta',
|
|
16
|
-
path: 'delta_targets.json',
|
|
17
|
-
content: JSON.stringify(deltaTargets, null, 2),
|
|
18
|
-
}
|
|
19
|
-
];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=DeltaRenderer.js.map
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export declare class StalePatchError extends Error {
|
|
2
|
-
readonly filePath: string;
|
|
3
|
-
readonly expectedHash: string;
|
|
4
|
-
readonly actualHash: string;
|
|
5
|
-
constructor(filePath: string, expectedHash: string, actualHash: string);
|
|
6
|
-
}
|
|
7
|
-
export declare const applyPatchTool: {
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: "object";
|
|
12
|
-
properties: {
|
|
13
|
-
projectRoot: {
|
|
14
|
-
type: string;
|
|
15
|
-
description: string;
|
|
16
|
-
};
|
|
17
|
-
filePath: {
|
|
18
|
-
type: string;
|
|
19
|
-
description: string;
|
|
20
|
-
};
|
|
21
|
-
newContent: {
|
|
22
|
-
type: string;
|
|
23
|
-
description: string;
|
|
24
|
-
};
|
|
25
|
-
expectedPrePatchHash: {
|
|
26
|
-
type: string;
|
|
27
|
-
description: string;
|
|
28
|
-
};
|
|
29
|
-
scanId: {
|
|
30
|
-
type: string;
|
|
31
|
-
description: string;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
required: string[];
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
export declare function handleApplyPatch(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { writeFile, rename, mkdir } from 'fs/promises';
|
|
2
|
-
import { join, dirname } from 'path';
|
|
3
|
-
import { SessionScope, ScopeViolation } from '../SessionScope.js';
|
|
4
|
-
import { hashFile, BlobStore } from '../../store/BlobStore.js';
|
|
5
|
-
import { PointerStore } from '../../store/PointerStore.js';
|
|
6
|
-
import { applyBudgetGuard } from '../BudgetGuard.js';
|
|
7
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
-
export class StalePatchError extends Error {
|
|
9
|
-
filePath;
|
|
10
|
-
expectedHash;
|
|
11
|
-
actualHash;
|
|
12
|
-
constructor(filePath, expectedHash, actualHash) {
|
|
13
|
-
super(`StalePatchError: ${filePath} hash mismatch — expected ${expectedHash}, got ${actualHash}. ` +
|
|
14
|
-
'File was modified since the expectedPrePatchHash was computed. Re-read the file and regenerate the patch.');
|
|
15
|
-
this.filePath = filePath;
|
|
16
|
-
this.expectedHash = expectedHash;
|
|
17
|
-
this.actualHash = actualHash;
|
|
18
|
-
this.name = 'StalePatchError';
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
export const applyPatchTool = {
|
|
22
|
-
name: 'apply_patch',
|
|
23
|
-
description: 'Applies a text patch to a file within the active workOrder scope. Requires expectedPrePatchHash to prevent stale-patch corruption. Records pre- and post-patch hashes.',
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
properties: {
|
|
27
|
-
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
28
|
-
filePath: { type: 'string', description: 'Path relative to projectRoot' },
|
|
29
|
-
newContent: { type: 'string', description: 'Full new content of the file after the patch' },
|
|
30
|
-
expectedPrePatchHash: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
description: 'sha256:<hex> hash of the file BEFORE patching. Obtain via hashFile or the sourceHash from get_file_skeleton.',
|
|
33
|
-
},
|
|
34
|
-
scanId: { type: 'string', description: 'Current scan ID for pointer registration' },
|
|
35
|
-
},
|
|
36
|
-
required: ['projectRoot', 'filePath', 'newContent', 'expectedPrePatchHash', 'scanId'],
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
export async function handleApplyPatch(args) {
|
|
40
|
-
const projectRoot = args.projectRoot;
|
|
41
|
-
const filePath = args.filePath;
|
|
42
|
-
const newContent = args.newContent;
|
|
43
|
-
const expectedPrePatchHash = args.expectedPrePatchHash;
|
|
44
|
-
const scanId = args.scanId;
|
|
45
|
-
if (!projectRoot || !filePath || !newContent || !expectedPrePatchHash || !scanId) {
|
|
46
|
-
throw new Error('projectRoot, filePath, newContent, expectedPrePatchHash, and scanId are all required');
|
|
47
|
-
}
|
|
48
|
-
// 1. Scope enforcement
|
|
49
|
-
try {
|
|
50
|
-
SessionScope.enforce(filePath);
|
|
51
|
-
}
|
|
52
|
-
catch (e) {
|
|
53
|
-
if (e instanceof ScopeViolation)
|
|
54
|
-
throw e;
|
|
55
|
-
throw e;
|
|
56
|
-
}
|
|
57
|
-
const absolutePath = filePath.startsWith('/') ? filePath : join(projectRoot, filePath);
|
|
58
|
-
// 2. Preimage hash check
|
|
59
|
-
let actualPreHash;
|
|
60
|
-
try {
|
|
61
|
-
actualPreHash = await hashFile(absolutePath);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// File doesn't exist yet — for new files, expectedPrePatchHash must be 'sha256:new'
|
|
65
|
-
actualPreHash = 'sha256:new';
|
|
66
|
-
}
|
|
67
|
-
if (actualPreHash !== expectedPrePatchHash) {
|
|
68
|
-
throw new StalePatchError(filePath, expectedPrePatchHash, actualPreHash);
|
|
69
|
-
}
|
|
70
|
-
// 3. Atomic write
|
|
71
|
-
const dir = dirname(absolutePath);
|
|
72
|
-
await mkdir(dir, { recursive: true });
|
|
73
|
-
const tmpPath = absolutePath + `.tmp_${Date.now()}`;
|
|
74
|
-
await writeFile(tmpPath, newContent, 'utf8');
|
|
75
|
-
await rename(tmpPath, absolutePath);
|
|
76
|
-
// 4. Compute post-patch hash
|
|
77
|
-
const postPatchHash = await hashFile(absolutePath);
|
|
78
|
-
// 5. Record both hashes in blob store
|
|
79
|
-
const blobStore = new BlobStore(projectRoot);
|
|
80
|
-
const pointerStore = PointerStore.open(projectRoot);
|
|
81
|
-
const { blobPath } = await blobStore.writeAtomic(newContent);
|
|
82
|
-
const pointerId = `ptr_patch_${uuidv4().replace(/-/g, '').slice(0, 12)}`;
|
|
83
|
-
await pointerStore.insertPointer({
|
|
84
|
-
pointerId,
|
|
85
|
-
scanId,
|
|
86
|
-
artifactName: 'patch_record',
|
|
87
|
-
contentHash: postPatchHash,
|
|
88
|
-
blobPath,
|
|
89
|
-
schemaVersion: '1.0.0',
|
|
90
|
-
createdAt: Date.now(),
|
|
91
|
-
expiresAt: null,
|
|
92
|
-
});
|
|
93
|
-
const result = {
|
|
94
|
-
ok: true,
|
|
95
|
-
filePath,
|
|
96
|
-
prePatchHash: actualPreHash,
|
|
97
|
-
postPatchHash,
|
|
98
|
-
pointerId,
|
|
99
|
-
message: `Patch applied to ${filePath}`,
|
|
100
|
-
};
|
|
101
|
-
return await applyBudgetGuard(projectRoot, scanId, 'patch_record', result);
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=apply_patch.js.map
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export declare const getCallChainTool: {
|
|
2
|
-
name: string;
|
|
3
|
-
description: string;
|
|
4
|
-
inputSchema: {
|
|
5
|
-
type: "object";
|
|
6
|
-
properties: {
|
|
7
|
-
projectRoot: {
|
|
8
|
-
type: string;
|
|
9
|
-
};
|
|
10
|
-
entrypointPath: {
|
|
11
|
-
type: string;
|
|
12
|
-
description: string;
|
|
13
|
-
};
|
|
14
|
-
maxDepth: {
|
|
15
|
-
type: string;
|
|
16
|
-
description: string;
|
|
17
|
-
};
|
|
18
|
-
targetActionKind: {
|
|
19
|
-
type: string;
|
|
20
|
-
description: string;
|
|
21
|
-
};
|
|
22
|
-
targetModel: {
|
|
23
|
-
type: string;
|
|
24
|
-
description: string;
|
|
25
|
-
};
|
|
26
|
-
targetOperation: {
|
|
27
|
-
type: string;
|
|
28
|
-
description: string;
|
|
29
|
-
};
|
|
30
|
-
targetFunctionName: {
|
|
31
|
-
type: string;
|
|
32
|
-
description: string;
|
|
33
|
-
};
|
|
34
|
-
includeTests: {
|
|
35
|
-
type: string;
|
|
36
|
-
description: string;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
required: string[];
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
export declare function handleGetCallChain(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { traverseCallChain } from '@vibe-splain/brain';
|
|
2
|
-
export const getCallChainTool = {
|
|
3
|
-
name: 'get_call_chain',
|
|
4
|
-
description: `Trace how behavior is reached from an entrypoint by following function call edges through the codebase. Returns a step-by-step chain with exact function names, file paths, line numbers, action kinds, and evidence text. Every edge has a confidence level; unresolved edges are listed explicitly.
|
|
5
|
-
|
|
6
|
-
Use structured filters when you know the target:
|
|
7
|
-
targetModel + targetOperation: "where does Booking get created?"
|
|
8
|
-
targetActionKind: "where is auth enforced?"
|
|
9
|
-
targetFunctionName: "how is function X reached?"
|
|
10
|
-
No filter returns the full call tree up to maxDepth.
|
|
11
|
-
|
|
12
|
-
Run scan_project first — this tool reads from the generated action_bindings.json.`,
|
|
13
|
-
inputSchema: {
|
|
14
|
-
type: 'object',
|
|
15
|
-
properties: {
|
|
16
|
-
projectRoot: { type: 'string' },
|
|
17
|
-
entrypointPath: { type: 'string', description: 'Relative path to the entrypoint file' },
|
|
18
|
-
maxDepth: { type: 'number', description: 'Max traversal depth. Default 6, max 12.' },
|
|
19
|
-
targetActionKind: { type: 'string', description: 'Stop at this semantic action kind.' },
|
|
20
|
-
targetModel: { type: 'string', description: 'Stop at functions touching this model.' },
|
|
21
|
-
targetOperation: { type: 'string', description: 'Narrow targetModel to this operation.' },
|
|
22
|
-
targetFunctionName: { type: 'string', description: 'Stop at a specific function name.' },
|
|
23
|
-
includeTests: { type: 'boolean', description: 'Include test files in traversal. Default false.' },
|
|
24
|
-
},
|
|
25
|
-
required: ['projectRoot', 'entrypointPath'],
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
export async function handleGetCallChain(args) {
|
|
29
|
-
const projectRoot = args.projectRoot;
|
|
30
|
-
const entrypointPath = args.entrypointPath;
|
|
31
|
-
if (!projectRoot || !entrypointPath)
|
|
32
|
-
throw new Error('projectRoot and entrypointPath are required');
|
|
33
|
-
const getCallChainArgs = {
|
|
34
|
-
entrypointPath,
|
|
35
|
-
maxDepth: typeof args.maxDepth === 'number' ? args.maxDepth : undefined,
|
|
36
|
-
targetActionKind: typeof args.targetActionKind === 'string' ? args.targetActionKind : undefined,
|
|
37
|
-
targetModel: typeof args.targetModel === 'string' ? args.targetModel : undefined,
|
|
38
|
-
targetOperation: typeof args.targetOperation === 'string' ? args.targetOperation : undefined,
|
|
39
|
-
targetFunctionName: typeof args.targetFunctionName === 'string' ? args.targetFunctionName : undefined,
|
|
40
|
-
includeTests: typeof args.includeTests === 'boolean' ? args.includeTests : undefined,
|
|
41
|
-
};
|
|
42
|
-
try {
|
|
43
|
-
const result = await traverseCallChain(projectRoot, getCallChainArgs);
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
throw new Error(`get_call_chain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=get_call_chain.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
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>;
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
|
@@ -1,68 +0,0 @@
|
|
|
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>;
|