@tienne/gestalt 0.9.1 → 0.9.2
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/CLAUDE.md +75 -0
- package/dist/package.json +1 -1
- package/dist/skills/blast-radius/SKILL.md +134 -0
- package/dist/skills/build-graph/SKILL.md +100 -0
- package/dist/skills/diff-radius/SKILL.md +125 -0
- package/dist/src/cli/commands/init.d.ts +5 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -0
- package/dist/src/cli/commands/init.js +79 -0
- package/dist/src/cli/commands/init.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +9 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/code-graph/blast-radius.d.ts +9 -0
- package/dist/src/code-graph/blast-radius.d.ts.map +1 -0
- package/dist/src/code-graph/blast-radius.js +139 -0
- package/dist/src/code-graph/blast-radius.js.map +1 -0
- package/dist/src/code-graph/engine.d.ts +46 -0
- package/dist/src/code-graph/engine.d.ts.map +1 -0
- package/dist/src/code-graph/engine.js +301 -0
- package/dist/src/code-graph/engine.js.map +1 -0
- package/dist/src/code-graph/git-hook.d.ts +22 -0
- package/dist/src/code-graph/git-hook.d.ts.map +1 -0
- package/dist/src/code-graph/git-hook.js +100 -0
- package/dist/src/code-graph/git-hook.js.map +1 -0
- package/dist/src/code-graph/index.d.ts +7 -0
- package/dist/src/code-graph/index.d.ts.map +1 -0
- package/dist/src/code-graph/index.js +6 -0
- package/dist/src/code-graph/index.js.map +1 -0
- package/dist/src/code-graph/plugins/go.d.ts +3 -0
- package/dist/src/code-graph/plugins/go.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/go.js +126 -0
- package/dist/src/code-graph/plugins/go.js.map +1 -0
- package/dist/src/code-graph/plugins/index.d.ts +16 -0
- package/dist/src/code-graph/plugins/index.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/index.js +36 -0
- package/dist/src/code-graph/plugins/index.js.map +1 -0
- package/dist/src/code-graph/plugins/java.d.ts +3 -0
- package/dist/src/code-graph/plugins/java.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/java.js +108 -0
- package/dist/src/code-graph/plugins/java.js.map +1 -0
- package/dist/src/code-graph/plugins/kotlin.d.ts +3 -0
- package/dist/src/code-graph/plugins/kotlin.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/kotlin.js +94 -0
- package/dist/src/code-graph/plugins/kotlin.js.map +1 -0
- package/dist/src/code-graph/plugins/objc.d.ts +3 -0
- package/dist/src/code-graph/plugins/objc.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/objc.js +129 -0
- package/dist/src/code-graph/plugins/objc.js.map +1 -0
- package/dist/src/code-graph/plugins/python.d.ts +3 -0
- package/dist/src/code-graph/plugins/python.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/python.js +116 -0
- package/dist/src/code-graph/plugins/python.js.map +1 -0
- package/dist/src/code-graph/plugins/rust.d.ts +3 -0
- package/dist/src/code-graph/plugins/rust.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/rust.js +108 -0
- package/dist/src/code-graph/plugins/rust.js.map +1 -0
- package/dist/src/code-graph/plugins/swift.d.ts +3 -0
- package/dist/src/code-graph/plugins/swift.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/swift.js +106 -0
- package/dist/src/code-graph/plugins/swift.js.map +1 -0
- package/dist/src/code-graph/plugins/typescript.d.ts +3 -0
- package/dist/src/code-graph/plugins/typescript.d.ts.map +1 -0
- package/dist/src/code-graph/plugins/typescript.js +209 -0
- package/dist/src/code-graph/plugins/typescript.js.map +1 -0
- package/dist/src/code-graph/storage.d.ts +18 -0
- package/dist/src/code-graph/storage.d.ts.map +1 -0
- package/dist/src/code-graph/storage.js +175 -0
- package/dist/src/code-graph/storage.js.map +1 -0
- package/dist/src/code-graph/types.d.ts +93 -0
- package/dist/src/code-graph/types.d.ts.map +1 -0
- package/dist/src/code-graph/types.js +17 -0
- package/dist/src/code-graph/types.js.map +1 -0
- package/dist/src/core/config.d.ts +1 -0
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +1 -1
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/types.d.ts +1 -0
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/execute/passthrough-engine.d.ts +4 -1
- package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
- package/dist/src/execute/passthrough-engine.js +54 -4
- package/dist/src/execute/passthrough-engine.js.map +1 -1
- package/dist/src/execute/prompts.d.ts +2 -2
- package/dist/src/execute/prompts.d.ts.map +1 -1
- package/dist/src/execute/prompts.js +7 -3
- package/dist/src/execute/prompts.js.map +1 -1
- package/dist/src/execute/repository.d.ts.map +1 -1
- package/dist/src/execute/repository.js +1 -0
- package/dist/src/execute/repository.js.map +1 -1
- package/dist/src/execute/session.d.ts +3 -1
- package/dist/src/execute/session.d.ts.map +1 -1
- package/dist/src/execute/session.js +3 -1
- package/dist/src/execute/session.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +46 -0
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +23 -0
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +19 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/code-graph-passthrough.d.ts +16 -0
- package/dist/src/mcp/tools/code-graph-passthrough.d.ts.map +1 -0
- package/dist/src/mcp/tools/code-graph-passthrough.js +65 -0
- package/dist/src/mcp/tools/code-graph-passthrough.js.map +1 -0
- package/dist/src/mcp/tools/execute-passthrough.d.ts.map +1 -1
- package/dist/src/mcp/tools/execute-passthrough.js +1 -1
- package/dist/src/mcp/tools/execute-passthrough.js.map +1 -1
- package/dist/src/mcp/tools/index.d.ts +1 -0
- package/dist/src/mcp/tools/index.d.ts.map +1 -1
- package/dist/src/mcp/tools/index.js +1 -0
- package/dist/src/mcp/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/blast-radius/SKILL.md +134 -0
- package/skills/build-graph/SKILL.md +100 -0
- package/skills/diff-radius/SKILL.md +125 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { NodeKind } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Computes blast-radius for a set of changed files using reverse BFS.
|
|
4
|
+
* Reverse BFS: finds all nodes that depend on (import/call) the changed files.
|
|
5
|
+
* Test files are prioritized in the result.
|
|
6
|
+
*/
|
|
7
|
+
export function computeBlastRadius(store, changedFiles, maxDepth = 2) {
|
|
8
|
+
if (changedFiles.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
changedFiles: [],
|
|
11
|
+
impactedFiles: [],
|
|
12
|
+
impactedNodes: [],
|
|
13
|
+
riskScore: 0,
|
|
14
|
+
maxDepthUsed: maxDepth,
|
|
15
|
+
summary: 'No changed files provided.',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// 1. Collect seed nodes from changed files
|
|
19
|
+
const seedNodeIds = new Set();
|
|
20
|
+
for (const filePath of changedFiles) {
|
|
21
|
+
const nodes = store.getNodesByFile(filePath);
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
seedNodeIds.add(node.id);
|
|
24
|
+
}
|
|
25
|
+
// Always include the file node itself
|
|
26
|
+
seedNodeIds.add(`file:${filePath}`);
|
|
27
|
+
}
|
|
28
|
+
// 2. BFS: find all nodes that depend on seeds (reverse direction)
|
|
29
|
+
// An edge (source → target) means source depends on target.
|
|
30
|
+
// So we look for edges where target is in our current frontier.
|
|
31
|
+
const visited = new Set(seedNodeIds);
|
|
32
|
+
const impactedNodeIds = new Set();
|
|
33
|
+
let frontier = Array.from(seedNodeIds);
|
|
34
|
+
for (let depth = 0; depth < maxDepth && frontier.length > 0; depth++) {
|
|
35
|
+
const nextFrontier = [];
|
|
36
|
+
for (const targetId of frontier) {
|
|
37
|
+
// Find all edges where this node is the target (i.e., who imports/calls this)
|
|
38
|
+
const incomingEdges = store.getEdgesByTarget(targetId);
|
|
39
|
+
for (const edge of incomingEdges) {
|
|
40
|
+
if (!visited.has(edge.sourceId)) {
|
|
41
|
+
visited.add(edge.sourceId);
|
|
42
|
+
impactedNodeIds.add(edge.sourceId);
|
|
43
|
+
nextFrontier.push(edge.sourceId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
frontier = nextFrontier;
|
|
48
|
+
}
|
|
49
|
+
// 3. Resolve impacted node details and extract file paths
|
|
50
|
+
const impactedFileSet = new Set(changedFiles);
|
|
51
|
+
const impactedNodes = [];
|
|
52
|
+
for (const nodeId of impactedNodeIds) {
|
|
53
|
+
const node = store.getNodeById(nodeId);
|
|
54
|
+
if (!node)
|
|
55
|
+
continue;
|
|
56
|
+
impactedFileSet.add(node.filePath);
|
|
57
|
+
// Compute hop distance (approximate: BFS level)
|
|
58
|
+
const hopDistance = computeHopDistance(nodeId, seedNodeIds, store, maxDepth);
|
|
59
|
+
impactedNodes.push({
|
|
60
|
+
nodeId: node.id,
|
|
61
|
+
filePath: node.filePath,
|
|
62
|
+
kind: node.kind,
|
|
63
|
+
name: node.name,
|
|
64
|
+
hopDistance,
|
|
65
|
+
isTest: node.isTest,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// 4. Sort: test files first, then by hop distance
|
|
69
|
+
impactedNodes.sort((a, b) => {
|
|
70
|
+
if (a.isTest && !b.isTest)
|
|
71
|
+
return -1;
|
|
72
|
+
if (!a.isTest && b.isTest)
|
|
73
|
+
return 1;
|
|
74
|
+
return a.hopDistance - b.hopDistance;
|
|
75
|
+
});
|
|
76
|
+
// 5. Build impactedFiles list: test files first
|
|
77
|
+
const allFiles = Array.from(impactedFileSet);
|
|
78
|
+
const testFiles = allFiles.filter((f) => isTestFile(f));
|
|
79
|
+
const nonTestFiles = allFiles.filter((f) => !isTestFile(f));
|
|
80
|
+
const impactedFiles = [...testFiles, ...nonTestFiles];
|
|
81
|
+
// 6. Calculate risk score
|
|
82
|
+
const stats = store.getStats('');
|
|
83
|
+
const totalNodes = stats.totalNodes;
|
|
84
|
+
const riskScore = totalNodes > 0 ? Math.min(1, impactedNodeIds.size / totalNodes) : 0;
|
|
85
|
+
// 7. Build summary
|
|
86
|
+
const summary = buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore);
|
|
87
|
+
return {
|
|
88
|
+
changedFiles,
|
|
89
|
+
impactedFiles,
|
|
90
|
+
impactedNodes,
|
|
91
|
+
riskScore,
|
|
92
|
+
maxDepthUsed: maxDepth,
|
|
93
|
+
summary,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Approximates the minimum hop distance from a node to the seed set.
|
|
98
|
+
* Uses a simple BFS from the node backward toward the seeds.
|
|
99
|
+
*/
|
|
100
|
+
function computeHopDistance(nodeId, seedNodeIds, store, maxDepth) {
|
|
101
|
+
if (seedNodeIds.has(nodeId))
|
|
102
|
+
return 0;
|
|
103
|
+
const visited = new Set([nodeId]);
|
|
104
|
+
let frontier = [nodeId];
|
|
105
|
+
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
106
|
+
const nextFrontier = [];
|
|
107
|
+
for (const id of frontier) {
|
|
108
|
+
// Check outgoing edges (what this node depends on)
|
|
109
|
+
const outgoingEdges = store.getEdgesBySource(id);
|
|
110
|
+
for (const edge of outgoingEdges) {
|
|
111
|
+
if (seedNodeIds.has(edge.targetId))
|
|
112
|
+
return depth;
|
|
113
|
+
if (!visited.has(edge.targetId)) {
|
|
114
|
+
visited.add(edge.targetId);
|
|
115
|
+
nextFrontier.push(edge.targetId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
frontier = nextFrontier;
|
|
120
|
+
}
|
|
121
|
+
return maxDepth; // fallback
|
|
122
|
+
}
|
|
123
|
+
function isTestFile(filePath) {
|
|
124
|
+
return (filePath.includes('.test.') ||
|
|
125
|
+
filePath.includes('.spec.') ||
|
|
126
|
+
filePath.includes('__tests__') ||
|
|
127
|
+
filePath.includes('/tests/') ||
|
|
128
|
+
filePath.includes('_test.') ||
|
|
129
|
+
filePath.includes('test_'));
|
|
130
|
+
}
|
|
131
|
+
function buildSummary(changedFiles, impactedFiles, impactedNodes, riskScore) {
|
|
132
|
+
const testFileCount = impactedFiles.filter(isTestFile).length;
|
|
133
|
+
const riskLabel = riskScore > 0.6 ? 'HIGH' : riskScore > 0.3 ? 'MEDIUM' : 'LOW';
|
|
134
|
+
const testNodes = impactedNodes.filter((n) => n.kind === NodeKind.Function && n.isTest).length;
|
|
135
|
+
return (`Changed ${changedFiles.length} file(s) impact ${impactedFiles.length} file(s) ` +
|
|
136
|
+
`(${testFileCount} test files, ${testNodes} test functions). ` +
|
|
137
|
+
`Risk: ${riskLabel} (${(riskScore * 100).toFixed(1)}%).`);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=blast-radius.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blast-radius.js","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BuildOptions, BuildResult, BlastRadiusOptions, BlastRadiusResult, DiffRadiusOptions, QueryPattern, QueryResult, CodeGraphStats } from './types.js';
|
|
2
|
+
export declare class CodeGraphEngine {
|
|
3
|
+
private storeCache;
|
|
4
|
+
private getDbPath;
|
|
5
|
+
private getStore;
|
|
6
|
+
/**
|
|
7
|
+
* Build or incrementally update the code graph for a repository.
|
|
8
|
+
*/
|
|
9
|
+
build(repoRoot: string, opts?: BuildOptions): BuildResult;
|
|
10
|
+
/**
|
|
11
|
+
* Compute blast-radius for changed files.
|
|
12
|
+
* Auto-detects changed files from git diff if not provided.
|
|
13
|
+
*/
|
|
14
|
+
blastRadius(repoRoot: string, opts?: BlastRadiusOptions): BlastRadiusResult;
|
|
15
|
+
/**
|
|
16
|
+
* Compute blast-radius for uncommitted changes (staged / unstaged / all).
|
|
17
|
+
* - staged: git diff --cached --name-only
|
|
18
|
+
* - unstaged: git diff --name-only
|
|
19
|
+
* - all (default): git diff HEAD --name-only
|
|
20
|
+
*/
|
|
21
|
+
diffRadius(repoRoot: string, opts?: DiffRadiusOptions): BlastRadiusResult;
|
|
22
|
+
/**
|
|
23
|
+
* Query the graph with a specific pattern.
|
|
24
|
+
*/
|
|
25
|
+
query(repoRoot: string, pattern: QueryPattern, target: string): QueryResult;
|
|
26
|
+
/**
|
|
27
|
+
* Returns statistics about the code graph.
|
|
28
|
+
*/
|
|
29
|
+
stats(repoRoot: string): CodeGraphStats;
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a code-graph.db exists for the given repo root.
|
|
32
|
+
*/
|
|
33
|
+
dbExists(repoRoot: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Search for files whose paths or node names contain any of the given keywords.
|
|
36
|
+
* Returns unique file paths sorted by match count descending.
|
|
37
|
+
*/
|
|
38
|
+
searchByKeywords(repoRoot: string, keywords: string[]): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Close all open database connections.
|
|
41
|
+
*/
|
|
42
|
+
close(): void;
|
|
43
|
+
private getGitChangedFiles;
|
|
44
|
+
}
|
|
45
|
+
export declare const codeGraphEngine: CodeGraphEngine;
|
|
46
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/code-graph/engine.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAwCpB,qBAAa,eAAe;IAC1B,OAAO,CAAC,UAAU,CAAqC;IAEvD,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,QAAQ;IAQhB;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,WAAW;IA8D7D;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,iBAAiB;IAiB/E;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,iBAAiB;IA8B7E;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW;IA6D3E;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAMvC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAInC;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;IA2BhE;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,kBAAkB;CAiB3B;AAGD,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { execSync } from 'node:child_process';
|
|
5
|
+
import { CodeGraphStore } from './storage.js';
|
|
6
|
+
import { computeBlastRadius } from './blast-radius.js';
|
|
7
|
+
import { getPluginForFile } from './plugins/index.js';
|
|
8
|
+
function getFilesRecursively(repoRoot, excludePatterns, include) {
|
|
9
|
+
const excludeSegments = new Set(excludePatterns.filter(p => p.endsWith('/**')).map(p => p.slice(0, -3)));
|
|
10
|
+
const excludeSuffixes = excludePatterns
|
|
11
|
+
.filter(p => !p.includes('/') && p.startsWith('*.'))
|
|
12
|
+
.map(p => p.slice(1));
|
|
13
|
+
const includeRoots = include?.filter(p => !p.startsWith('**')).map(p => p.replace(/\*\*.*$/, '').replace(/\/$/, ''));
|
|
14
|
+
const results = [];
|
|
15
|
+
try {
|
|
16
|
+
const entries = readdirSync(repoRoot, { withFileTypes: true, recursive: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if (!entry.isFile())
|
|
19
|
+
continue;
|
|
20
|
+
const fullPath = join(entry.parentPath, entry.name);
|
|
21
|
+
const rel = fullPath.slice(repoRoot.length + 1);
|
|
22
|
+
const segments = rel.split('/');
|
|
23
|
+
if (segments.some(s => excludeSegments.has(s)))
|
|
24
|
+
continue;
|
|
25
|
+
if (excludeSuffixes.some(s => entry.name.endsWith(s)))
|
|
26
|
+
continue;
|
|
27
|
+
if (includeRoots && includeRoots.length > 0 && !includeRoots.some(r => rel.startsWith(r)))
|
|
28
|
+
continue;
|
|
29
|
+
results.push(fullPath);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// directory doesn't exist or not readable
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
const DEFAULT_EXCLUDE = [
|
|
38
|
+
'node_modules/**',
|
|
39
|
+
'.git/**',
|
|
40
|
+
'.gestalt/**',
|
|
41
|
+
'dist/**',
|
|
42
|
+
'build/**',
|
|
43
|
+
'coverage/**',
|
|
44
|
+
'*.min.js',
|
|
45
|
+
];
|
|
46
|
+
export class CodeGraphEngine {
|
|
47
|
+
storeCache = new Map();
|
|
48
|
+
getDbPath(repoRoot) {
|
|
49
|
+
return join(repoRoot, '.gestalt', 'code-graph.db');
|
|
50
|
+
}
|
|
51
|
+
getStore(repoRoot) {
|
|
52
|
+
const dbPath = this.getDbPath(repoRoot);
|
|
53
|
+
if (!this.storeCache.has(dbPath)) {
|
|
54
|
+
this.storeCache.set(dbPath, new CodeGraphStore(dbPath));
|
|
55
|
+
}
|
|
56
|
+
return this.storeCache.get(dbPath);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build or incrementally update the code graph for a repository.
|
|
60
|
+
*/
|
|
61
|
+
build(repoRoot, opts = {}) {
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
const store = this.getStore(repoRoot);
|
|
64
|
+
const { include, exclude = [], mode = 'full' } = opts;
|
|
65
|
+
// Collect files
|
|
66
|
+
const excludePatterns = [...DEFAULT_EXCLUDE, ...exclude];
|
|
67
|
+
const files = getFilesRecursively(repoRoot, excludePatterns, include);
|
|
68
|
+
// Filter files that have supported plugins
|
|
69
|
+
const supportedFiles = files.filter((f) => getPluginForFile(f) !== null);
|
|
70
|
+
let nodesBuilt = 0;
|
|
71
|
+
let edgesBuilt = 0;
|
|
72
|
+
for (const filePath of supportedFiles) {
|
|
73
|
+
// Incremental: skip unchanged files (hash comparison)
|
|
74
|
+
if (mode === 'incremental') {
|
|
75
|
+
const existingHash = store.getFileHash(filePath);
|
|
76
|
+
if (existingHash) {
|
|
77
|
+
try {
|
|
78
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
79
|
+
const currentHash = createHash('sha256').update(content).digest('hex');
|
|
80
|
+
if (currentHash === existingHash)
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// If can't read, skip
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const plugin = getPluginForFile(filePath);
|
|
90
|
+
if (!plugin)
|
|
91
|
+
continue;
|
|
92
|
+
try {
|
|
93
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
94
|
+
const result = plugin.parse(filePath, content);
|
|
95
|
+
// Delete old data for this file before updating
|
|
96
|
+
store.deleteByFile(filePath);
|
|
97
|
+
for (const node of result.nodes) {
|
|
98
|
+
store.upsertNode(node);
|
|
99
|
+
nodesBuilt++;
|
|
100
|
+
}
|
|
101
|
+
for (const edge of result.edges) {
|
|
102
|
+
store.upsertEdge(edge);
|
|
103
|
+
edgesBuilt++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Skip files that fail to parse
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
nodesBuilt,
|
|
112
|
+
edgesBuilt,
|
|
113
|
+
timeTakenMs: Date.now() - start,
|
|
114
|
+
installedHook: false, // Hook installation handled separately via GitHookManager
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compute blast-radius for changed files.
|
|
119
|
+
* Auto-detects changed files from git diff if not provided.
|
|
120
|
+
*/
|
|
121
|
+
blastRadius(repoRoot, opts = {}) {
|
|
122
|
+
const store = this.getStore(repoRoot);
|
|
123
|
+
const { changedFiles, base = 'HEAD~1', maxDepth = 2 } = opts;
|
|
124
|
+
let files = changedFiles;
|
|
125
|
+
if (!files || files.length === 0) {
|
|
126
|
+
files = this.getGitChangedFiles(repoRoot, base);
|
|
127
|
+
}
|
|
128
|
+
// Convert to absolute paths
|
|
129
|
+
const absoluteFiles = files.map((f) => f.startsWith('/') ? f : resolve(repoRoot, f));
|
|
130
|
+
return computeBlastRadius(store, absoluteFiles, maxDepth);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Compute blast-radius for uncommitted changes (staged / unstaged / all).
|
|
134
|
+
* - staged: git diff --cached --name-only
|
|
135
|
+
* - unstaged: git diff --name-only
|
|
136
|
+
* - all (default): git diff HEAD --name-only
|
|
137
|
+
*/
|
|
138
|
+
diffRadius(repoRoot, opts = {}) {
|
|
139
|
+
const { mode = 'all', maxDepth = 2 } = opts;
|
|
140
|
+
const store = this.getStore(repoRoot);
|
|
141
|
+
const gitCmd = mode === 'staged'
|
|
142
|
+
? 'git diff --cached --name-only'
|
|
143
|
+
: mode === 'unstaged'
|
|
144
|
+
? 'git diff --name-only'
|
|
145
|
+
: 'git diff HEAD --name-only';
|
|
146
|
+
let files;
|
|
147
|
+
try {
|
|
148
|
+
const output = execSync(gitCmd, {
|
|
149
|
+
cwd: repoRoot,
|
|
150
|
+
encoding: 'utf-8',
|
|
151
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
152
|
+
});
|
|
153
|
+
files = output
|
|
154
|
+
.trim()
|
|
155
|
+
.split('\n')
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
.map((f) => resolve(repoRoot, f));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
files = [];
|
|
161
|
+
}
|
|
162
|
+
return computeBlastRadius(store, files, maxDepth);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Query the graph with a specific pattern.
|
|
166
|
+
*/
|
|
167
|
+
query(repoRoot, pattern, target) {
|
|
168
|
+
const store = this.getStore(repoRoot);
|
|
169
|
+
const nodes = [];
|
|
170
|
+
const edges = [];
|
|
171
|
+
switch (pattern) {
|
|
172
|
+
case 'callers_of': {
|
|
173
|
+
// Find all nodes that call the target
|
|
174
|
+
const targetEdges = store.getEdgesByTarget(`function:${repoRoot}:${target}`);
|
|
175
|
+
for (const edge of targetEdges) {
|
|
176
|
+
if (edge.kind === 'CALLS') {
|
|
177
|
+
const node = store.getNodeById(edge.sourceId);
|
|
178
|
+
if (node)
|
|
179
|
+
nodes.push(node);
|
|
180
|
+
edges.push(edge);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case 'callees_of': {
|
|
186
|
+
// Find all nodes called by the target
|
|
187
|
+
const outEdges = store.getEdgesBySource(`function:${repoRoot}:${target}`);
|
|
188
|
+
for (const edge of outEdges) {
|
|
189
|
+
if (edge.kind === 'CALLS') {
|
|
190
|
+
const node = store.getNodeById(edge.targetId);
|
|
191
|
+
if (node)
|
|
192
|
+
nodes.push(node);
|
|
193
|
+
edges.push(edge);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case 'tests_for': {
|
|
199
|
+
// Find test nodes that depend on the target file
|
|
200
|
+
const targetFileId = `file:${target.startsWith('/') ? target : resolve(repoRoot, target)}`;
|
|
201
|
+
const incomingEdges = store.getEdgesByTarget(targetFileId);
|
|
202
|
+
for (const edge of incomingEdges) {
|
|
203
|
+
const node = store.getNodeById(edge.sourceId);
|
|
204
|
+
if (node?.isTest) {
|
|
205
|
+
nodes.push(node);
|
|
206
|
+
edges.push(edge);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case 'imports_of': {
|
|
212
|
+
// Find files that import the target
|
|
213
|
+
const targetFileId = `file:${target.startsWith('/') ? target : resolve(repoRoot, target)}`;
|
|
214
|
+
const incomingEdges = store.getEdgesByTarget(targetFileId);
|
|
215
|
+
for (const edge of incomingEdges) {
|
|
216
|
+
if (edge.kind === 'IMPORTS_FROM') {
|
|
217
|
+
const node = store.getNodeById(edge.sourceId);
|
|
218
|
+
if (node)
|
|
219
|
+
nodes.push(node);
|
|
220
|
+
edges.push(edge);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { nodes, edges };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Returns statistics about the code graph.
|
|
230
|
+
*/
|
|
231
|
+
stats(repoRoot) {
|
|
232
|
+
const dbPath = this.getDbPath(repoRoot);
|
|
233
|
+
const store = this.getStore(repoRoot);
|
|
234
|
+
return store.getStats(dbPath);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Checks if a code-graph.db exists for the given repo root.
|
|
238
|
+
*/
|
|
239
|
+
dbExists(repoRoot) {
|
|
240
|
+
return existsSync(this.getDbPath(repoRoot));
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Search for files whose paths or node names contain any of the given keywords.
|
|
244
|
+
* Returns unique file paths sorted by match count descending.
|
|
245
|
+
*/
|
|
246
|
+
searchByKeywords(repoRoot, keywords) {
|
|
247
|
+
if (keywords.length === 0)
|
|
248
|
+
return [];
|
|
249
|
+
const store = this.getStore(repoRoot);
|
|
250
|
+
const nodes = store.getAllNodes();
|
|
251
|
+
const lower = keywords.map((k) => k.toLowerCase());
|
|
252
|
+
const scoreMap = new Map();
|
|
253
|
+
for (const node of nodes) {
|
|
254
|
+
const fileLower = node.filePath.toLowerCase();
|
|
255
|
+
const nameLower = node.name.toLowerCase();
|
|
256
|
+
let score = 0;
|
|
257
|
+
for (const kw of lower) {
|
|
258
|
+
if (fileLower.includes(kw) || nameLower.includes(kw)) {
|
|
259
|
+
score++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (score > 0) {
|
|
263
|
+
const prev = scoreMap.get(node.filePath) ?? 0;
|
|
264
|
+
scoreMap.set(node.filePath, Math.max(prev, score));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return [...scoreMap.entries()]
|
|
268
|
+
.sort((a, b) => b[1] - a[1])
|
|
269
|
+
.map(([filePath]) => filePath);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Close all open database connections.
|
|
273
|
+
*/
|
|
274
|
+
close() {
|
|
275
|
+
for (const store of this.storeCache.values()) {
|
|
276
|
+
store.close();
|
|
277
|
+
}
|
|
278
|
+
this.storeCache.clear();
|
|
279
|
+
}
|
|
280
|
+
getGitChangedFiles(repoRoot, base) {
|
|
281
|
+
try {
|
|
282
|
+
const output = execSync(`git diff --name-only ${base}`, {
|
|
283
|
+
cwd: repoRoot,
|
|
284
|
+
encoding: 'utf-8',
|
|
285
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
286
|
+
});
|
|
287
|
+
return output
|
|
288
|
+
.trim()
|
|
289
|
+
.split('\n')
|
|
290
|
+
.filter(Boolean)
|
|
291
|
+
.map((f) => resolve(repoRoot, f));
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// Not a git repo or no changes — return empty
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Singleton for shared use across MCP tools
|
|
300
|
+
export const codeGraphEngine = new CodeGraphEngine();
|
|
301
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/code-graph/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAYtD,SAAS,mBAAmB,CAAC,QAAgB,EAAE,eAAyB,EAAE,OAAkB;IAC1F,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CACxE,CAAC;IACF,MAAM,eAAe,GAAG,eAAe;SACpC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,YAAY,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IAErH,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAa,CAAC;QAC5F,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBAAE,SAAS;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAAE,SAAS;YACzD,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAAE,SAAS;YAChE,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAAE,SAAS;YACpG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,eAAe,GAAG;IACtB,iBAAiB;IACjB,SAAS;IACT,aAAa;IACb,SAAS;IACT,UAAU;IACV,aAAa;IACb,UAAU;CACX,CAAC;AAEF,MAAM,OAAO,eAAe;IAClB,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE/C,SAAS,CAAC,QAAgB;QAChC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;IACrD,CAAC;IAEO,QAAQ,CAAC,QAAgB;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAgB,EAAE,OAAqB,EAAE;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,EAAE,IAAI,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;QAEtD,gBAAgB;QAChB,MAAM,eAAe,GAAG,CAAC,GAAG,eAAe,EAAE,GAAG,OAAO,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;QAEtE,2CAA2C;QAC3C,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAEzE,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;YACtC,sDAAsD;YACtD,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBACjD,IAAI,YAAY,EAAE,CAAC;oBACjB,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;wBAChD,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;wBACvE,IAAI,WAAW,KAAK,YAAY;4BAAE,SAAS;oBAC7C,CAAC;oBAAC,MAAM,CAAC;wBACP,sBAAsB;wBACtB,SAAS;oBACX,CAAC;gBACH,CAAC;YACH,CAAC;YAED,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM;gBAAE,SAAS;YAEtB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAE/C,gDAAgD;gBAChD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAE7B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAChC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;oBACvB,UAAU,EAAE,CAAC;gBACf,CAAC;gBACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAChC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;oBACvB,UAAU,EAAE,CAAC;gBACf,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;QACH,CAAC;QAED,OAAO;YACL,UAAU;YACV,UAAU;YACV,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;YAC/B,aAAa,EAAE,KAAK,EAAE,0DAA0D;SACjF,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,QAAgB,EAAE,OAA2B,EAAE;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,EAAE,YAAY,EAAE,IAAI,GAAG,QAAQ,EAAE,QAAQ,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC;QAE7D,IAAI,KAAK,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC;QAED,4BAA4B;QAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAC7C,CAAC;QAEF,OAAO,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC5D,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,QAAgB,EAAE,OAA0B,EAAE;QACvD,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEtC,MAAM,MAAM,GACV,IAAI,KAAK,QAAQ;YACf,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,IAAI,KAAK,UAAU;gBACnB,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,2BAA2B,CAAC;QAEpC,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;gBAC9B,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAChC,CAAC,CAAC;YACH,KAAK,GAAG,MAAM;iBACX,IAAI,EAAE;iBACN,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;QAED,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAgB,EAAE,OAAqB,EAAE,MAAc;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,EAAE,CAAC;QAEjB,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,sCAAsC;gBACtC,MAAM,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,YAAY,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;gBAC7E,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;oBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAC9C,IAAI,IAAI;4BAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,sCAAsC;gBACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,gBAAgB,CAAC,YAAY,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;gBAC1E,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;oBAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAC9C,IAAI,IAAI;4BAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,iDAAiD;gBACjD,MAAM,YAAY,GAAG,QAAQ,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC3F,MAAM,aAAa,GAAG,KAAK,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;gBAC3D,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;oBACjC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC9C,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;wBACjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBACjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,oCAAoC;gBACpC,MAAM,YAAY,GAAG,QAAQ,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;gBAC3F,MAAM,aAAa,GAAG,KAAK,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;gBAC3D,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;oBACjC,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;wBACjC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAC9C,IAAI,IAAI;4BAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;QACH,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAgB;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,QAAgB;QACvB,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,QAAgB,EAAE,QAAkB;QACnD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;gBACvB,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBACrD,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC;YACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC9C,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;aAC3B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAEO,kBAAkB,CAAC,QAAgB,EAAE,IAAY;QACvD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,IAAI,EAAE,EAAE;gBACtD,GAAG,EAAE,QAAQ;gBACb,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aAChC,CAAC,CAAC;YACH,OAAO,MAAM;iBACV,IAAI,EAAE;iBACN,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,OAAO,CAAC;iBACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;YAC9C,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class GitHookManager {
|
|
2
|
+
private getHookPath;
|
|
3
|
+
/**
|
|
4
|
+
* Install the Gestalt post-commit hook into the given repo root.
|
|
5
|
+
* - Creates the hook file with mode 0o755 if it doesn't exist.
|
|
6
|
+
* - Appends the Gestalt section to an existing file only when the marker is absent.
|
|
7
|
+
* - No-ops if the marker is already present.
|
|
8
|
+
*/
|
|
9
|
+
installHook(repoRoot: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Remove only the Gestalt-managed section from the post-commit hook.
|
|
12
|
+
* Preserves any pre-existing hook content.
|
|
13
|
+
* Deletes the file entirely if it becomes empty after removal.
|
|
14
|
+
*/
|
|
15
|
+
uninstallHook(repoRoot: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Returns true if the Gestalt hook marker is present in the post-commit hook file.
|
|
18
|
+
*/
|
|
19
|
+
isInstalled(repoRoot: string): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare const gitHookManager: GitHookManager;
|
|
22
|
+
//# sourceMappingURL=git-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-hook.d.ts","sourceRoot":"","sources":["../../../src/code-graph/git-hook.ts"],"names":[],"mappings":"AA4BA,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW;IAInB;;;;;OAKG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAqBnC;;;;OAIG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgCrC;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAQvC;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync, chmodSync } from 'node:fs';
|
|
2
|
+
import { join, resolve, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const GESTALT_MARKER = '# GESTALT_CODE_GRAPH_HOOK';
|
|
5
|
+
// Absolute path to the fallback dist entry (resolved relative to this source file)
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const FALLBACK_PATH = resolve(dirname(__filename), '../../dist/code-graph/index.js');
|
|
8
|
+
const HOOK_SCRIPT = `#!/bin/sh
|
|
9
|
+
${GESTALT_MARKER} - managed by Gestalt, do not remove this comment
|
|
10
|
+
node --input-type=module << 'GESTALT_HOOK_EOF'
|
|
11
|
+
import { createRequire } from 'module';
|
|
12
|
+
const req = createRequire(import.meta.url);
|
|
13
|
+
try {
|
|
14
|
+
const pkgMain = req.resolve('@tienne/gestalt');
|
|
15
|
+
const { codeGraphEngine } = await import(pkgMain);
|
|
16
|
+
await codeGraphEngine.build(process.cwd(), { mode: 'incremental' });
|
|
17
|
+
} catch {
|
|
18
|
+
try {
|
|
19
|
+
const { codeGraphEngine } = await import('${FALLBACK_PATH}');
|
|
20
|
+
await codeGraphEngine.build(process.cwd(), { mode: 'incremental' });
|
|
21
|
+
} catch {}
|
|
22
|
+
}
|
|
23
|
+
GESTALT_HOOK_EOF
|
|
24
|
+
`;
|
|
25
|
+
export class GitHookManager {
|
|
26
|
+
getHookPath(repoRoot) {
|
|
27
|
+
return join(repoRoot, '.git', 'hooks', 'post-commit');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Install the Gestalt post-commit hook into the given repo root.
|
|
31
|
+
* - Creates the hook file with mode 0o755 if it doesn't exist.
|
|
32
|
+
* - Appends the Gestalt section to an existing file only when the marker is absent.
|
|
33
|
+
* - No-ops if the marker is already present.
|
|
34
|
+
*/
|
|
35
|
+
installHook(repoRoot) {
|
|
36
|
+
const hookPath = this.getHookPath(repoRoot);
|
|
37
|
+
if (existsSync(hookPath)) {
|
|
38
|
+
const existing = readFileSync(hookPath, 'utf-8');
|
|
39
|
+
if (existing.includes(GESTALT_MARKER)) {
|
|
40
|
+
// Already installed — skip
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Append to existing file preserving original content
|
|
44
|
+
const appended = existing.endsWith('\n')
|
|
45
|
+
? existing + '\n' + HOOK_SCRIPT
|
|
46
|
+
: existing + '\n\n' + HOOK_SCRIPT;
|
|
47
|
+
writeFileSync(hookPath, appended, { encoding: 'utf-8' });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
writeFileSync(hookPath, HOOK_SCRIPT, { encoding: 'utf-8' });
|
|
51
|
+
}
|
|
52
|
+
chmodSync(hookPath, 0o755);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Remove only the Gestalt-managed section from the post-commit hook.
|
|
56
|
+
* Preserves any pre-existing hook content.
|
|
57
|
+
* Deletes the file entirely if it becomes empty after removal.
|
|
58
|
+
*/
|
|
59
|
+
uninstallHook(repoRoot) {
|
|
60
|
+
const hookPath = this.getHookPath(repoRoot);
|
|
61
|
+
if (!existsSync(hookPath)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const content = readFileSync(hookPath, 'utf-8');
|
|
65
|
+
if (!content.includes(GESTALT_MARKER)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Remove the Gestalt block: from the marker line to the end of the block.
|
|
69
|
+
// Strategy: split on the marker and keep only content before it.
|
|
70
|
+
// The marker line is always preceded by a newline (or is at the start).
|
|
71
|
+
const markerIndex = content.indexOf(GESTALT_MARKER);
|
|
72
|
+
// Walk backwards to find the beginning of the line containing the marker
|
|
73
|
+
let lineStart = markerIndex;
|
|
74
|
+
while (lineStart > 0 && content[lineStart - 1] !== '\n') {
|
|
75
|
+
lineStart--;
|
|
76
|
+
}
|
|
77
|
+
const before = content.slice(0, lineStart).trimEnd();
|
|
78
|
+
if (before.length === 0) {
|
|
79
|
+
// Nothing remains — delete the file
|
|
80
|
+
unlinkSync(hookPath);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
writeFileSync(hookPath, before + '\n', { encoding: 'utf-8' });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns true if the Gestalt hook marker is present in the post-commit hook file.
|
|
88
|
+
*/
|
|
89
|
+
isInstalled(repoRoot) {
|
|
90
|
+
const hookPath = this.getHookPath(repoRoot);
|
|
91
|
+
if (!existsSync(hookPath)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const content = readFileSync(hookPath, 'utf-8');
|
|
95
|
+
return content.includes(GESTALT_MARKER);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Singleton for shared use across MCP tools
|
|
99
|
+
export const gitHookManager = new GitHookManager();
|
|
100
|
+
//# sourceMappingURL=git-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-hook.js","sourceRoot":"","sources":["../../../src/code-graph/git-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CodeGraphEngine, codeGraphEngine } from './engine.js';
|
|
2
|
+
export { CodeGraphStore } from './storage.js';
|
|
3
|
+
export { computeBlastRadius } from './blast-radius.js';
|
|
4
|
+
export { getPluginForFile, pluginRegistry } from './plugins/index.js';
|
|
5
|
+
export type { CodeGraphNode, CodeGraphEdge, BlastRadiusResult, BlastRadiusNode, BlastRadiusOptions, BuildOptions, BuildResult, QueryPattern, QueryResult, CodeGraphStats, AnalyzerPlugin, ParseResult, } from './types.js';
|
|
6
|
+
export { NodeKind, EdgeKind } from './types.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|