@tienne/gestalt 0.9.1 → 0.10.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/CLAUDE.md +75 -0
- package/dist/package.json +2 -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/skills/setup/SKILL.md +150 -0
- package/dist/src/cli/commands/graph-visualize.d.ts +11 -0
- package/dist/src/cli/commands/graph-visualize.d.ts.map +1 -0
- package/dist/src/cli/commands/graph-visualize.js +26 -0
- package/dist/src/cli/commands/graph-visualize.js.map +1 -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 +23 -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 +19 -0
- package/dist/src/code-graph/storage.d.ts.map +1 -0
- package/dist/src/code-graph/storage.js +182 -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/graph-viz/engine.d.ts +20 -0
- package/dist/src/graph-viz/engine.d.ts.map +1 -0
- package/dist/src/graph-viz/engine.js +81 -0
- package/dist/src/graph-viz/engine.js.map +1 -0
- package/dist/src/graph-viz/html-generator.d.ts +7 -0
- package/dist/src/graph-viz/html-generator.d.ts.map +1 -0
- package/dist/src/graph-viz/html-generator.js +699 -0
- package/dist/src/graph-viz/html-generator.js.map +1 -0
- package/dist/src/graph-viz/index.d.ts +6 -0
- package/dist/src/graph-viz/index.d.ts.map +1 -0
- package/dist/src/graph-viz/index.js +5 -0
- package/dist/src/graph-viz/index.js.map +1 -0
- package/dist/src/graph-viz/port-finder.d.ts +2 -0
- package/dist/src/graph-viz/port-finder.d.ts.map +1 -0
- package/dist/src/graph-viz/port-finder.js +26 -0
- package/dist/src/graph-viz/port-finder.js.map +1 -0
- package/dist/src/graph-viz/server.d.ts +26 -0
- package/dist/src/graph-viz/server.d.ts.map +1 -0
- package/dist/src/graph-viz/server.js +104 -0
- package/dist/src/graph-viz/server.js.map +1 -0
- package/dist/src/graph-viz/types.d.ts +11 -0
- package/dist/src/graph-viz/types.d.ts.map +1 -0
- package/dist/src/graph-viz/types.js +2 -0
- package/dist/src/graph-viz/types.js.map +1 -0
- package/dist/src/mcp/schemas.d.ts +57 -0
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +28 -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 +28 -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/graph-visualize-passthrough.d.ts +13 -0
- package/dist/src/mcp/tools/graph-visualize-passthrough.d.ts.map +1 -0
- package/dist/src/mcp/tools/graph-visualize-passthrough.js +26 -0
- package/dist/src/mcp/tools/graph-visualize-passthrough.js.map +1 -0
- 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 +2 -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
- package/skills/setup/SKILL.md +150 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/init.ts"],"names":[],"mappings":"AA2BA,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDrG"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import { codeGraphEngine } from '../../code-graph/index.js';
|
|
5
|
+
import { gitHookManager } from '../../code-graph/git-hook.js';
|
|
6
|
+
const CONFIG_FILENAME = 'gestalt.json';
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
$schema: './node_modules/@tienne/gestalt/schemas/gestalt.schema.json',
|
|
9
|
+
notifications: false,
|
|
10
|
+
interview: {
|
|
11
|
+
ambiguityThreshold: 0.2,
|
|
12
|
+
maxRounds: 10,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
async function confirm(question) {
|
|
16
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
17
|
+
return new Promise((res) => {
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
res(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function initCommand(options) {
|
|
25
|
+
const repoRoot = process.cwd();
|
|
26
|
+
const filePath = resolve(repoRoot, CONFIG_FILENAME);
|
|
27
|
+
// Step 1: gestalt.json 생성/덮어쓰기
|
|
28
|
+
if (existsSync(filePath)) {
|
|
29
|
+
const overwrite = await confirm('gestalt.json이 이미 존재합니다. 덮어쓰시겠습니까? (y/N): ');
|
|
30
|
+
if (!overwrite) {
|
|
31
|
+
console.log('gestalt.json 생성을 건너뜁니다.');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
writeFileSync(filePath, JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n', 'utf-8');
|
|
35
|
+
console.log('✓ gestalt.json 덮어쓰기 완료: ' + filePath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
writeFileSync(filePath, JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n', 'utf-8');
|
|
40
|
+
console.log('✓ gestalt.json 생성 완료: ' + filePath);
|
|
41
|
+
}
|
|
42
|
+
// Step 2: 코드 그래프 빌드
|
|
43
|
+
if (!options.skipGraph) {
|
|
44
|
+
console.log('\n코드 그래프를 빌드하는 중...');
|
|
45
|
+
try {
|
|
46
|
+
const result = codeGraphEngine.build(repoRoot, { mode: 'full' });
|
|
47
|
+
console.log(`✓ 코드 그래프 빌드 완료: 노드 ${result.nodesBuilt}개, 엣지 ${result.edgesBuilt}개 (${result.timeTakenMs}ms)`);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
51
|
+
console.warn(`⚠ 코드 그래프 빌드 실패: ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log('코드 그래프 빌드를 건너뜁니다. (--skip-graph)');
|
|
56
|
+
}
|
|
57
|
+
// Step 3: post-commit 훅 설치
|
|
58
|
+
if (!options.skipHook) {
|
|
59
|
+
try {
|
|
60
|
+
const alreadyInstalled = gitHookManager.isInstalled(repoRoot);
|
|
61
|
+
gitHookManager.installHook(repoRoot);
|
|
62
|
+
if (alreadyInstalled) {
|
|
63
|
+
console.log('✓ post-commit 훅이 이미 설치되어 있습니다.');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log('✓ post-commit 훅 설치 완료.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
71
|
+
console.warn(`⚠ post-commit 훅 설치 실패: ${msg}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log('post-commit 훅 설치를 건너뜁니다. (--skip-hook)');
|
|
76
|
+
}
|
|
77
|
+
console.log('\nGestalt 초기화 완료! 이제 /build-graph, /blast-radius 스킬을 바로 사용할 수 있습니다.');
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,SAAS,IAAI,OAAO,CA6EnC"}
|
package/dist/src/cli/index.js
CHANGED
|
@@ -4,7 +4,9 @@ import { specCommand } from './commands/spec.js';
|
|
|
4
4
|
import { serveCommand } from './commands/serve.js';
|
|
5
5
|
import { statusCommand } from './commands/status.js';
|
|
6
6
|
import { setupCommand } from './commands/setup.js';
|
|
7
|
+
import { initCommand } from './commands/init.js';
|
|
7
8
|
import { monitorCommand } from './commands/monitor.js';
|
|
9
|
+
import { graphVisualizeCommand } from './commands/graph-visualize.js';
|
|
8
10
|
import { getVersion } from '../core/version.js';
|
|
9
11
|
export function createCli() {
|
|
10
12
|
const program = new Command();
|
|
@@ -39,6 +41,14 @@ export function createCli() {
|
|
|
39
41
|
.action((sessionId) => {
|
|
40
42
|
statusCommand(sessionId);
|
|
41
43
|
});
|
|
44
|
+
program
|
|
45
|
+
.command('init')
|
|
46
|
+
.description('Initialize Gestalt: create gestalt.json, build code graph, and install post-commit hook')
|
|
47
|
+
.option('--skip-graph', 'Skip code graph build')
|
|
48
|
+
.option('--skip-hook', 'Skip post-commit hook installation')
|
|
49
|
+
.action(async (options) => {
|
|
50
|
+
await initCommand(options);
|
|
51
|
+
});
|
|
42
52
|
program
|
|
43
53
|
.command('setup')
|
|
44
54
|
.description('Generate a gestalt.json configuration file')
|
|
@@ -51,6 +61,19 @@ export function createCli() {
|
|
|
51
61
|
.action(async (sessionId) => {
|
|
52
62
|
await monitorCommand(sessionId);
|
|
53
63
|
});
|
|
64
|
+
program
|
|
65
|
+
.command('graph-visualize')
|
|
66
|
+
.description('Visualize the code knowledge graph in the browser')
|
|
67
|
+
.option('--repo-root <path>', 'Repository root (defaults to cwd)')
|
|
68
|
+
.option('--port <number>', 'Preferred server port (default: 7891)', parseInt)
|
|
69
|
+
.option('--no-browser', 'Do not open the browser automatically')
|
|
70
|
+
.action(async (options) => {
|
|
71
|
+
await graphVisualizeCommand({
|
|
72
|
+
repoRoot: options.repoRoot,
|
|
73
|
+
port: options.port,
|
|
74
|
+
noBrowser: options.browser === false,
|
|
75
|
+
});
|
|
76
|
+
});
|
|
54
77
|
return program;
|
|
55
78
|
}
|
|
56
79
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,UAAU,SAAS;IACvB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,2FAA2F,CAAC;SACxG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEzB,OAAO;SACJ,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACrC,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,wCAAwC,CAAC;SACrD,MAAM,CAAC,aAAa,EAAE,8CAA8C,CAAC;SACrE,MAAM,CAAC,OAAO,EAAE,wCAAwC,CAAC;SACzD,MAAM,CAAC,KAAK,EAAE,KAAyB,EAAE,OAA4C,EAAE,EAAE;QACxF,MAAM,gBAAgB,CAAC,KAAK,IAAI,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,4CAA4C,CAAC;SACzD,MAAM,CAAC,aAAa,EAAE,yDAAyD,CAAC;SAChF,MAAM,CAAC,KAAK,EAAE,SAAiB,EAAE,OAA4B,EAAE,EAAE;QAChE,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,qBAAqB,CAAC;SAC9B,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,CAAC,SAAkB,EAAE,EAAE;QAC7B,aAAa,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,4CAA4C,CAAC;SACzD,MAAM,CAAC,GAAG,EAAE;QACX,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,sBAAsB,CAAC;SAC/B,WAAW,CAAC,wDAAwD,CAAC;SACrE,MAAM,CAAC,KAAK,EAAE,SAAkB,EAAE,EAAE;QACnC,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEL,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CodeGraphStore } from './storage.js';
|
|
2
|
+
import type { BlastRadiusResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Computes blast-radius for a set of changed files using reverse BFS.
|
|
5
|
+
* Reverse BFS: finds all nodes that depend on (import/call) the changed files.
|
|
6
|
+
* Test files are prioritized in the result.
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeBlastRadius(store: CodeGraphStore, changedFiles: string[], maxDepth?: number): BlastRadiusResult;
|
|
9
|
+
//# sourceMappingURL=blast-radius.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blast-radius.d.ts","sourceRoot":"","sources":["../../../src/code-graph/blast-radius.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAC;AAGrE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,YAAY,EAAE,MAAM,EAAE,EACtB,QAAQ,GAAE,MAAU,GACnB,iBAAiB,CAoGnB"}
|
|
@@ -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
|