vibe-splain 3.5.0 → 4.0.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 +110 -158
- package/dist/commands/install.d.ts +0 -5
- package/dist/commands/install.js +5 -55
- package/dist/commands/serve.js +1 -1
- package/dist/export/ArtifactBundleWriter.js +3 -3
- package/dist/export/ExportOrchestrator.d.ts +2 -20
- package/dist/export/ExportOrchestrator.js +5 -116
- package/dist/export/Watcher.js +5 -5
- package/dist/export/renderers/AgentMarkdownRenderer.d.ts +1 -1
- package/dist/export/renderers/GraphRenderer.d.ts +2 -2
- package/dist/export/renderers/HtmlRenderer.d.ts +1 -1
- package/dist/export/renderers/HtmlRenderer.js +1 -1
- package/dist/export/renderers/JsonRenderer.d.ts +1 -1
- package/dist/export/renderers/RawAnalysisRenderer.d.ts +1 -1
- package/dist/export/renderers/Renderer.d.ts +1 -1
- package/dist/export/renderers/ValidationRenderer.d.ts +1 -1
- package/dist/index.js +441 -3787
- package/dist/mcp/server.js +6 -23
- package/dist/mcp/tools/get_file_context.js +1 -1
- package/dist/mcp/tools/get_project_map.js +1 -1
- package/dist/mcp/tools/get_strategic_overview.js +1 -1
- package/dist/mcp/tools/get_wild_discoveries.js +1 -1
- package/dist/mcp/tools/inspect_pillar.js +1 -1
- package/dist/mcp/tools/mark_stale.js +1 -1
- package/dist/mcp/tools/scan_project.d.ts +2 -3
- package/dist/mcp/tools/scan_project.js +12 -13
- package/dist/mcp/tools/set_project_brief.js +1 -1
- package/dist/mcp/tools/write_decision_card.d.ts +1 -1
- package/dist/mcp/tools/write_decision_card.js +2 -2
- package/dist/ui/index.html +1 -1
- package/package.json +9 -14
- package/dist/commands/hook.d.ts +0 -9
- package/dist/commands/hook.js +0 -84
- package/dist/hook/preToolUse.d.ts +0 -30
- package/dist/hook/preToolUse.js +0 -81
- package/dist/hook.d.ts +0 -1
- package/dist/hook.js +0 -337
- package/dist/mcp/BudgetGuard.d.ts +0 -13
- package/dist/mcp/BudgetGuard.js +0 -55
- package/dist/mcp/SessionScope.d.ts +0 -26
- package/dist/mcp/SessionScope.js +0 -56
- package/dist/mcp/tools/get_file_skeleton.d.ts +0 -23
- package/dist/mcp/tools/get_file_skeleton.js +0 -124
- package/dist/mcp/tools/hydration/get_evidence_slice.d.ts +0 -31
- package/dist/mcp/tools/hydration/get_evidence_slice.js +0 -57
- package/dist/mcp/tools/hydration/get_project_summary.d.ts +0 -23
- package/dist/mcp/tools/hydration/get_project_summary.js +0 -58
- package/dist/mcp/tools/hydration/get_start_here.d.ts +0 -23
- package/dist/mcp/tools/hydration/get_start_here.js +0 -52
- package/dist/mcp/tools/read_file.d.ts +0 -31
- package/dist/mcp/tools/read_file.js +0 -90
- package/dist/store/BlobStore.d.ts +0 -22
- package/dist/store/BlobStore.js +0 -96
- package/dist/store/PointerStore.d.ts +0 -52
- package/dist/store/PointerStore.js +0 -154
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { WorkOrderRow, ProofDescriptor } from '../store/PointerStore.js';
|
|
2
|
-
export interface ScopePolicy {
|
|
3
|
-
workOrderId: string;
|
|
4
|
-
allowedFiles: string[];
|
|
5
|
-
allowedGlobs: string[];
|
|
6
|
-
deniedGlobs: string[];
|
|
7
|
-
requiredProof: ProofDescriptor[];
|
|
8
|
-
}
|
|
9
|
-
export declare class ScopeViolation extends Error {
|
|
10
|
-
readonly path: string;
|
|
11
|
-
readonly workOrderId: string;
|
|
12
|
-
constructor(path: string, workOrderId: string, reason: string);
|
|
13
|
-
}
|
|
14
|
-
export declare const SessionScope: {
|
|
15
|
-
set(policy: ScopePolicy): void;
|
|
16
|
-
clear(): void;
|
|
17
|
-
get(): ScopePolicy | null;
|
|
18
|
-
/**
|
|
19
|
-
* Enforce scope for a file path.
|
|
20
|
-
* Throws ScopeViolation if:
|
|
21
|
-
* - a scope is active AND the path is not allowed
|
|
22
|
-
* If no scope is active, all paths are permitted.
|
|
23
|
-
*/
|
|
24
|
-
enforce(filePath: string): void;
|
|
25
|
-
fromWorkOrderRow(row: WorkOrderRow): ScopePolicy;
|
|
26
|
-
};
|
package/dist/mcp/SessionScope.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { minimatch } from 'minimatch';
|
|
2
|
-
export class ScopeViolation extends Error {
|
|
3
|
-
path;
|
|
4
|
-
workOrderId;
|
|
5
|
-
constructor(path, workOrderId, reason) {
|
|
6
|
-
super(`ScopeViolation [${workOrderId}]: ${reason} — path: ${path}`);
|
|
7
|
-
this.path = path;
|
|
8
|
-
this.workOrderId = workOrderId;
|
|
9
|
-
this.name = 'ScopeViolation';
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
let activeScope = null;
|
|
13
|
-
export const SessionScope = {
|
|
14
|
-
set(policy) {
|
|
15
|
-
activeScope = policy;
|
|
16
|
-
},
|
|
17
|
-
clear() {
|
|
18
|
-
activeScope = null;
|
|
19
|
-
},
|
|
20
|
-
get() {
|
|
21
|
-
return activeScope;
|
|
22
|
-
},
|
|
23
|
-
/**
|
|
24
|
-
* Enforce scope for a file path.
|
|
25
|
-
* Throws ScopeViolation if:
|
|
26
|
-
* - a scope is active AND the path is not allowed
|
|
27
|
-
* If no scope is active, all paths are permitted.
|
|
28
|
-
*/
|
|
29
|
-
enforce(filePath) {
|
|
30
|
-
if (!activeScope)
|
|
31
|
-
return;
|
|
32
|
-
const { workOrderId, allowedFiles, allowedGlobs, deniedGlobs } = activeScope;
|
|
33
|
-
// Explicit file list match (exact suffix or relative path)
|
|
34
|
-
const inAllowedFiles = allowedFiles.some(f => filePath === f || filePath.endsWith('/' + f) || filePath.endsWith(f));
|
|
35
|
-
// Glob match
|
|
36
|
-
const inAllowedGlobs = allowedGlobs.some(g => minimatch(filePath, g, { matchBase: true }));
|
|
37
|
-
if (!inAllowedFiles && !inAllowedGlobs) {
|
|
38
|
-
throw new ScopeViolation(filePath, workOrderId, 'path not in allowedFiles or allowedGlobs');
|
|
39
|
-
}
|
|
40
|
-
// Deny globs have priority over allow
|
|
41
|
-
const isDenied = deniedGlobs.some(g => minimatch(filePath, g, { matchBase: true }));
|
|
42
|
-
if (isDenied) {
|
|
43
|
-
throw new ScopeViolation(filePath, workOrderId, 'path matches deniedGlobs');
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
fromWorkOrderRow(row) {
|
|
47
|
-
return {
|
|
48
|
-
workOrderId: row.workOrderId,
|
|
49
|
-
allowedFiles: JSON.parse(row.allowedFiles),
|
|
50
|
-
allowedGlobs: JSON.parse(row.allowedGlobs),
|
|
51
|
-
deniedGlobs: JSON.parse(row.deniedGlobs),
|
|
52
|
-
requiredProof: JSON.parse(row.requiredProof),
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
//# sourceMappingURL=SessionScope.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export declare const getFileSkeletonTool: {
|
|
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
|
-
};
|
|
20
|
-
required: string[];
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
export declare function handleGetFileSkeleton(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'fs/promises';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { SessionScope, ScopeViolation } from '../SessionScope.js';
|
|
4
|
-
import { BlobStore, hashFile } 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 const getFileSkeletonTool = {
|
|
9
|
-
name: 'get_file_skeleton',
|
|
10
|
-
description: 'Returns a content-addressed skeleton view of a source file (function signatures, class names, exported symbols). Enforces active workOrder scope. Results are content-addressed — repeated calls on unchanged files return cached pointers.',
|
|
11
|
-
inputSchema: {
|
|
12
|
-
type: 'object',
|
|
13
|
-
properties: {
|
|
14
|
-
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
15
|
-
filePath: { type: 'string', description: 'Path relative to projectRoot' },
|
|
16
|
-
scanId: { type: 'string', description: 'Current scan ID for pointer registration' },
|
|
17
|
-
},
|
|
18
|
-
required: ['projectRoot', 'filePath', 'scanId'],
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
export async function handleGetFileSkeleton(args) {
|
|
22
|
-
const projectRoot = args.projectRoot;
|
|
23
|
-
const filePath = args.filePath;
|
|
24
|
-
const scanId = args.scanId;
|
|
25
|
-
if (!projectRoot || !filePath || !scanId) {
|
|
26
|
-
throw new Error('projectRoot, filePath, and scanId are required');
|
|
27
|
-
}
|
|
28
|
-
// 1. Scope enforcement
|
|
29
|
-
try {
|
|
30
|
-
SessionScope.enforce(filePath);
|
|
31
|
-
}
|
|
32
|
-
catch (e) {
|
|
33
|
-
if (e instanceof ScopeViolation)
|
|
34
|
-
throw e;
|
|
35
|
-
throw e;
|
|
36
|
-
}
|
|
37
|
-
const absolutePath = filePath.startsWith('/') ? filePath : join(projectRoot, filePath);
|
|
38
|
-
// 2. Compute preimage hash of source file
|
|
39
|
-
let currentHash;
|
|
40
|
-
try {
|
|
41
|
-
currentHash = await hashFile(absolutePath);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
throw new Error(`FileNotFound: cannot read ${filePath}`);
|
|
45
|
-
}
|
|
46
|
-
// 3. Check cache: same hash + parser version = return existing pointer
|
|
47
|
-
const parserVersion = '1.0.0';
|
|
48
|
-
const cacheKey = `skeleton:${currentHash}:${parserVersion}`;
|
|
49
|
-
const pointerStore = PointerStore.open(projectRoot);
|
|
50
|
-
// Look up by contentHash in existing pointers for this scan
|
|
51
|
-
const existingPointers = pointerStore.listPointersByScan(scanId);
|
|
52
|
-
const cached = existingPointers.find(p => p.artifactName === 'file_skeleton' && p.contentHash === cacheKey);
|
|
53
|
-
if (cached) {
|
|
54
|
-
return {
|
|
55
|
-
pointerId: cached.pointerId,
|
|
56
|
-
contentHash: currentHash,
|
|
57
|
-
cached: true,
|
|
58
|
-
filePath,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
// 4. Build skeleton
|
|
62
|
-
const source = await readFile(absolutePath, 'utf8');
|
|
63
|
-
const skeleton = extractSkeleton(source, filePath);
|
|
64
|
-
// 5. Record content hash
|
|
65
|
-
const skeletonPayload = {
|
|
66
|
-
filePath,
|
|
67
|
-
sourceHash: currentHash,
|
|
68
|
-
parserVersion,
|
|
69
|
-
skeleton,
|
|
70
|
-
};
|
|
71
|
-
// 6. Budget enforcement
|
|
72
|
-
const blobStore = new BlobStore(projectRoot);
|
|
73
|
-
const serialized = JSON.stringify(skeletonPayload, null, 2);
|
|
74
|
-
const { contentHash: skeletonHash, blobPath } = await blobStore.writeAtomic(serialized);
|
|
75
|
-
const pointerId = `ptr_skel_${uuidv4().replace(/-/g, '').slice(0, 12)}`;
|
|
76
|
-
await pointerStore.insertPointer({
|
|
77
|
-
pointerId,
|
|
78
|
-
scanId,
|
|
79
|
-
artifactName: 'file_skeleton',
|
|
80
|
-
contentHash: cacheKey, // cache key encodes source hash + parser version
|
|
81
|
-
blobPath,
|
|
82
|
-
schemaVersion: '1.0.0',
|
|
83
|
-
createdAt: Date.now(),
|
|
84
|
-
expiresAt: null,
|
|
85
|
-
});
|
|
86
|
-
const result = {
|
|
87
|
-
filePath,
|
|
88
|
-
sourceHash: currentHash,
|
|
89
|
-
pointerId,
|
|
90
|
-
skeleton,
|
|
91
|
-
};
|
|
92
|
-
return await applyBudgetGuard(projectRoot, scanId, 'file_skeleton', result);
|
|
93
|
-
}
|
|
94
|
-
function extractSkeleton(source, filePath) {
|
|
95
|
-
const lines = source.split('\n');
|
|
96
|
-
const skeleton = [];
|
|
97
|
-
const ext = filePath.split('.').pop() ?? '';
|
|
98
|
-
const isTS = ['ts', 'tsx'].includes(ext);
|
|
99
|
-
const isJS = ['js', 'jsx', 'mjs', 'cjs'].includes(ext);
|
|
100
|
-
if (isTS || isJS) {
|
|
101
|
-
for (let i = 0; i < lines.length; i++) {
|
|
102
|
-
const line = lines[i].trim();
|
|
103
|
-
// Exported declarations, function/class/interface/type/enum signatures
|
|
104
|
-
if (/^(export\s+)?(async\s+)?function\b/.test(line) ||
|
|
105
|
-
/^(export\s+)?(abstract\s+)?class\b/.test(line) ||
|
|
106
|
-
/^(export\s+)?interface\b/.test(line) ||
|
|
107
|
-
/^(export\s+)?type\s+\w+/.test(line) ||
|
|
108
|
-
/^(export\s+)?enum\b/.test(line) ||
|
|
109
|
-
/^(export\s+)?const\s+\w+\s*[:=(]/.test(line) ||
|
|
110
|
-
/^(export\s+)?let\s+\w+\s*[:=(]/.test(line) ||
|
|
111
|
-
/^(export\s+)?(default\s+)/.test(line) ||
|
|
112
|
-
/^\s*(public|private|protected|static|readonly|abstract)\s+/.test(line) ||
|
|
113
|
-
/^import\b/.test(line)) {
|
|
114
|
-
skeleton.push(`L${i + 1}: ${lines[i]}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
// Generic: return first 80 lines as skeleton for unsupported types
|
|
120
|
-
return lines.slice(0, 80).map((l, i) => `L${i + 1}: ${l}`);
|
|
121
|
-
}
|
|
122
|
-
return skeleton;
|
|
123
|
-
}
|
|
124
|
-
//# sourceMappingURL=get_file_skeleton.js.map
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare const getEvidenceSliceTool: {
|
|
2
|
-
name: string;
|
|
3
|
-
description: string;
|
|
4
|
-
inputSchema: {
|
|
5
|
-
type: "object";
|
|
6
|
-
properties: {
|
|
7
|
-
projectRoot: {
|
|
8
|
-
type: string;
|
|
9
|
-
description: string;
|
|
10
|
-
};
|
|
11
|
-
pointerId: {
|
|
12
|
-
type: string;
|
|
13
|
-
description: string;
|
|
14
|
-
};
|
|
15
|
-
startLine: {
|
|
16
|
-
type: string;
|
|
17
|
-
description: string;
|
|
18
|
-
};
|
|
19
|
-
endLine: {
|
|
20
|
-
type: string;
|
|
21
|
-
description: string;
|
|
22
|
-
};
|
|
23
|
-
scanId: {
|
|
24
|
-
type: string;
|
|
25
|
-
description: string;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
required: string[];
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
export declare function handleGetEvidenceSlice(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { hydratePointer, applyBudgetGuard } from '../../BudgetGuard.js';
|
|
2
|
-
import { SessionScope } from '../../SessionScope.js';
|
|
3
|
-
export const getEvidenceSliceTool = {
|
|
4
|
-
name: 'get_evidence_slice',
|
|
5
|
-
description: 'Raw fallback: returns a line-range slice from a blob artifact. Pointer must be valid and unexpired. Output is budgeted.',
|
|
6
|
-
inputSchema: {
|
|
7
|
-
type: 'object',
|
|
8
|
-
properties: {
|
|
9
|
-
projectRoot: { type: 'string', description: 'Absolute project root' },
|
|
10
|
-
pointerId: { type: 'string', description: 'Pointer ID for the target artifact' },
|
|
11
|
-
startLine: { type: 'number', description: 'Inclusive start line (1-based)' },
|
|
12
|
-
endLine: { type: 'number', description: 'Inclusive end line (1-based). Capped at startLine+200.' },
|
|
13
|
-
scanId: { type: 'string', description: 'Current scan ID for budget pointer registration' },
|
|
14
|
-
},
|
|
15
|
-
required: ['projectRoot', 'pointerId', 'startLine', 'endLine', 'scanId'],
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
export async function handleGetEvidenceSlice(args) {
|
|
19
|
-
const projectRoot = args.projectRoot;
|
|
20
|
-
const pointerId = args.pointerId;
|
|
21
|
-
const startLine = Number(args.startLine);
|
|
22
|
-
const endLine = Math.min(Number(args.endLine), startLine + 200);
|
|
23
|
-
const scanId = args.scanId;
|
|
24
|
-
if (!projectRoot || !pointerId || !scanId) {
|
|
25
|
-
throw new Error('projectRoot, pointerId, startLine, endLine, and scanId are required');
|
|
26
|
-
}
|
|
27
|
-
// 1. Verify pointer, lifetime, hash
|
|
28
|
-
const { content, row } = await hydratePointer(projectRoot, pointerId);
|
|
29
|
-
const rawText = content.toString('utf8');
|
|
30
|
-
// Scope enforcement: if a Worker scope is active, enforce it for file-type artifacts
|
|
31
|
-
const scope = SessionScope.get();
|
|
32
|
-
if (scope && (row.artifactName === 'file_read' || row.artifactName === 'file_skeleton')) {
|
|
33
|
-
try {
|
|
34
|
-
const parsed = JSON.parse(rawText);
|
|
35
|
-
if (parsed.filePath) {
|
|
36
|
-
SessionScope.enforce(parsed.filePath);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
if (e.name === 'ScopeViolation')
|
|
41
|
-
throw e;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const lines = rawText.split('\n');
|
|
45
|
-
const sliced = lines.slice(startLine - 1, endLine);
|
|
46
|
-
const result = {
|
|
47
|
-
pointerId,
|
|
48
|
-
artifactName: row.artifactName,
|
|
49
|
-
startLine,
|
|
50
|
-
endLine,
|
|
51
|
-
totalLines: lines.length,
|
|
52
|
-
slice: sliced,
|
|
53
|
-
};
|
|
54
|
-
// 2. Budget query result
|
|
55
|
-
return await applyBudgetGuard(projectRoot, scanId, 'evidence_slice', result);
|
|
56
|
-
}
|
|
57
|
-
//# sourceMappingURL=get_evidence_slice.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export declare const getProjectSummaryTool: {
|
|
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 handleGetProjectSummary(args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { hydratePointer, applyBudgetGuard } from '../../BudgetGuard.js';
|
|
2
|
-
export const getProjectSummaryTool = {
|
|
3
|
-
name: 'get_project_summary',
|
|
4
|
-
description: 'Returns high-level project metrics from a scan manifest pointer: file counts, pillar summary, stack. Token-safe. 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' },
|
|
10
|
-
scanId: { type: 'string', description: 'Current scan ID for budget pointer registration' },
|
|
11
|
-
},
|
|
12
|
-
required: ['projectRoot', 'manifestPointer', 'scanId'],
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
export async function handleGetProjectSummary(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
|
-
const { content, row } = await hydratePointer(projectRoot, manifestPointer);
|
|
23
|
-
const payload = JSON.parse(content.toString('utf8'));
|
|
24
|
-
if (row.artifactName === 'artifact_manifest') {
|
|
25
|
-
const manifest = payload;
|
|
26
|
-
const analysisEntry = manifest.artifacts.find(a => a.name === 'analysis' && a.indexes?.startHere);
|
|
27
|
-
let indexData = {};
|
|
28
|
-
if (analysisEntry?.indexes?.startHere) {
|
|
29
|
-
const { content: ic } = await hydratePointer(projectRoot, analysisEntry.indexes.startHere);
|
|
30
|
-
indexData = JSON.parse(ic.toString('utf8'));
|
|
31
|
-
}
|
|
32
|
-
const result = {
|
|
33
|
-
scanId: manifest.scanId,
|
|
34
|
-
generatedAt: manifest.generatedAt,
|
|
35
|
-
artifactCount: manifest.artifacts.length,
|
|
36
|
-
totalArtifactBytes: manifest.artifacts.reduce((s, a) => s + (a.sizeBytes ?? 0), 0),
|
|
37
|
-
startHere: indexData.startHere,
|
|
38
|
-
topHeat: indexData.topHeat,
|
|
39
|
-
pillarSummary: indexData.pillarSummary,
|
|
40
|
-
totalFiles: indexData.totalFiles,
|
|
41
|
-
realSourceFiles: indexData.realSourceFiles,
|
|
42
|
-
};
|
|
43
|
-
return await applyBudgetGuard(projectRoot, scanId, 'get_project_summary_result', result);
|
|
44
|
-
}
|
|
45
|
-
if (row.artifactName === 'analysis.index') {
|
|
46
|
-
const result = {
|
|
47
|
-
scanId: payload.scanId,
|
|
48
|
-
startHere: payload.startHere,
|
|
49
|
-
topHeat: payload.topHeat,
|
|
50
|
-
pillarSummary: payload.pillarSummary,
|
|
51
|
-
totalFiles: payload.totalFiles,
|
|
52
|
-
realSourceFiles: payload.realSourceFiles,
|
|
53
|
-
};
|
|
54
|
-
return await applyBudgetGuard(projectRoot, scanId, 'get_project_summary_result', result);
|
|
55
|
-
}
|
|
56
|
-
throw new Error(`Unsupported artifact type for get_project_summary: ${row.artifactName}`);
|
|
57
|
-
}
|
|
58
|
-
//# sourceMappingURL=get_project_summary.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
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>;
|
|
@@ -1,52 +0,0 @@
|
|
|
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
|
|
@@ -1,31 +0,0 @@
|
|
|
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>;
|
|
@@ -1,90 +0,0 @@
|
|
|
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
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export interface BlobWriteResult {
|
|
2
|
-
contentHash: string;
|
|
3
|
-
blobPath: string;
|
|
4
|
-
}
|
|
5
|
-
export declare class BlobStore {
|
|
6
|
-
private blobsDir;
|
|
7
|
-
private tmpDir;
|
|
8
|
-
constructor(projectRoot: string);
|
|
9
|
-
ensureDirs(): Promise<void>;
|
|
10
|
-
writeAtomic(payload: Buffer | string): Promise<BlobWriteResult>;
|
|
11
|
-
readBlob(blobPath: string): Promise<Buffer>;
|
|
12
|
-
blobExists(contentHash: string): Promise<boolean>;
|
|
13
|
-
blobPathForHash(contentHash: string): string;
|
|
14
|
-
verifyIntegrity(blobPath: string, expectedHash: string): Promise<boolean>;
|
|
15
|
-
/** List all blob paths for GC reference counting */
|
|
16
|
-
listBlobPaths(): Promise<string[]>;
|
|
17
|
-
getBlobSize(blobPath: string): Promise<number>;
|
|
18
|
-
}
|
|
19
|
-
/** Hash a string or buffer without writing it */
|
|
20
|
-
export declare function computeHash(payload: Buffer | string): string;
|
|
21
|
-
/** Hash a file on disk */
|
|
22
|
-
export declare function hashFile(filePath: string): Promise<string>;
|