@kentwynn/kgraph 0.1.19 → 0.1.21
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 +14 -8
- package/dist/cli/help.js +6 -6
- package/dist/config/config.js +12 -1
- package/dist/context/context-query.js +7 -0
- package/dist/integrations/adapters/cline.d.ts +2 -0
- package/dist/integrations/adapters/cline.js +17 -0
- package/dist/integrations/adapters/gemini.d.ts +2 -0
- package/dist/integrations/adapters/gemini.js +17 -0
- package/dist/integrations/adapters/windsurf.d.ts +2 -0
- package/dist/integrations/adapters/windsurf.js +17 -0
- package/dist/integrations/integration-registry.js +7 -1
- package/dist/scanner/ts-symbol-extractor.js +94 -7
- package/dist/types/config.d.ts +1 -1
- package/dist/types/maps.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Persistent repository intelligence for AI coding tools.
|
|
4
4
|
|
|
5
|
-
KGraph gives Codex, GitHub Copilot, Cursor,
|
|
5
|
+
KGraph gives Codex, GitHub Copilot, Cursor, Claude Code, Gemini CLI, Windsurf, and Cline a local knowledge layer for your repo: file maps, symbols, imports, relationships, and durable notes from previous AI sessions. The goal is simple: your assistant should not spend every session re-learning the same codebase.
|
|
6
6
|
|
|
7
7
|
## The Workflow
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Use KGraph in two steps:
|
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# Required once per repository
|
|
13
|
-
kgraph init --integrations codex,copilot,cursor,claude-code
|
|
13
|
+
kgraph init --integrations codex,copilot,cursor,claude-code,gemini,windsurf,cline
|
|
14
14
|
|
|
15
15
|
# Normal daily command
|
|
16
16
|
kgraph "auth token refresh"
|
|
@@ -51,6 +51,7 @@ KGraph stores the reusable parts locally:
|
|
|
51
51
|
- What files exist and what language they use.
|
|
52
52
|
- What symbols each source file defines.
|
|
53
53
|
- Which files import each other.
|
|
54
|
+
- Which TypeScript/JavaScript functions and methods directly call each other when KGraph can infer it cheaply.
|
|
54
55
|
- Which notes, decisions, debugging findings, and gotchas were captured from prior sessions.
|
|
55
56
|
- Which cognition references are current, mixed, stale, or unresolved after code moves.
|
|
56
57
|
|
|
@@ -89,7 +90,7 @@ From the root of a repository:
|
|
|
89
90
|
kgraph init
|
|
90
91
|
|
|
91
92
|
# 2. Optional: connect AI tools so they know the KGraph workflow
|
|
92
|
-
kgraph integrate add codex copilot cursor claude-code
|
|
93
|
+
kgraph integrate add codex copilot cursor claude-code gemini windsurf cline
|
|
93
94
|
|
|
94
95
|
# 3. Run the normal workflow for a topic
|
|
95
96
|
kgraph "auth token refresh"
|
|
@@ -120,7 +121,7 @@ kgraph init
|
|
|
120
121
|
Required once per repo. Creates `.kgraph/` and the local config.
|
|
121
122
|
|
|
122
123
|
```bash
|
|
123
|
-
kgraph init --integrations codex,copilot,cursor,claude-code
|
|
124
|
+
kgraph init --integrations codex,copilot,cursor,claude-code,gemini,windsurf,cline
|
|
124
125
|
```
|
|
125
126
|
|
|
126
127
|
Initializes KGraph and writes local instruction files for supported AI tools.
|
|
@@ -196,7 +197,7 @@ Show processed cognition sessions.
|
|
|
196
197
|
KGraph integrations are local files. They do not start background agents, call AI providers, or send data anywhere.
|
|
197
198
|
|
|
198
199
|
```bash
|
|
199
|
-
kgraph integrate add codex copilot cursor claude-code
|
|
200
|
+
kgraph integrate add codex copilot cursor claude-code gemini windsurf cline
|
|
200
201
|
kgraph integrate list
|
|
201
202
|
kgraph integrate remove cursor
|
|
202
203
|
```
|
|
@@ -207,6 +208,11 @@ kgraph integrate remove cursor
|
|
|
207
208
|
| GitHub Copilot | `.github/copilot-instructions.md`, `.github/prompts/*` |
|
|
208
209
|
| Cursor | `.cursor/rules/kgraph.mdc` |
|
|
209
210
|
| Claude Code | `CLAUDE.md`, `.claude/commands/*` |
|
|
211
|
+
| Gemini CLI | `GEMINI.md` |
|
|
212
|
+
| Windsurf | `.windsurf/rules/kgraph.md` |
|
|
213
|
+
| Cline | `.clinerules/kgraph.md` |
|
|
214
|
+
|
|
215
|
+
Antigravity is supported through the existing agent instruction surfaces it can read, especially `AGENTS.md` and `GEMINI.md`; it does not need a separate KGraph adapter yet.
|
|
210
216
|
|
|
211
217
|
KGraph preserves existing user-authored content and updates only its marked instruction blocks or generated command files.
|
|
212
218
|
|
|
@@ -235,7 +241,7 @@ The files are local, inspectable, and human-readable. There is no database, tele
|
|
|
235
241
|
|
|
236
242
|
KGraph deeply scans:
|
|
237
243
|
|
|
238
|
-
- TypeScript and JavaScript
|
|
244
|
+
- TypeScript and JavaScript, including lightweight function/method call relationships
|
|
239
245
|
- Python
|
|
240
246
|
- Go
|
|
241
247
|
- Rust
|
|
@@ -243,7 +249,7 @@ KGraph deeply scans:
|
|
|
243
249
|
- C and C++
|
|
244
250
|
- C#
|
|
245
251
|
|
|
246
|
-
Other
|
|
252
|
+
Other languages keep practical file, import, and symbol depth without full call graph analysis. Common file types still appear in the file map with generic metadata, so context queries can still point to docs, config, SQL, CSS, HTML, YAML, and similar files.
|
|
247
253
|
|
|
248
254
|
## Visualization
|
|
249
255
|
|
|
@@ -251,7 +257,7 @@ Other common file types still appear in the file map with generic metadata, so c
|
|
|
251
257
|
kgraph visualize
|
|
252
258
|
```
|
|
253
259
|
|
|
254
|
-
The graph shows files, symbols, imports, cognition notes, and relationship edges. Cognition notes are colored by reference health:
|
|
260
|
+
The graph shows files, symbols, imports, TypeScript/JavaScript call edges, ownership edges, cognition notes, and relationship edges. Cognition notes are colored by reference health:
|
|
255
261
|
|
|
256
262
|
- current
|
|
257
263
|
- mixed
|
package/dist/cli/help.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Chalk } from 'chalk';
|
|
|
2
2
|
import figlet from 'figlet';
|
|
3
3
|
export function renderRootHelp(useColor = supportsColor()) {
|
|
4
4
|
const theme = new Chalk({ level: useColor ? 3 : 0 });
|
|
5
|
-
const command = (name, description) => ` ${theme.green(name.padEnd(
|
|
5
|
+
const command = (name, description) => ` ${theme.green(name.padEnd(42))} ${description}`;
|
|
6
6
|
const logo = renderLogo();
|
|
7
7
|
return [
|
|
8
8
|
'',
|
|
@@ -11,7 +11,7 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
11
11
|
` ${theme.bold('KGraph')} ${theme.dim('Persistent repo intelligence for AI coding tools')}`,
|
|
12
12
|
'',
|
|
13
13
|
` ${theme.hex('#c084fc')('Build a local knowledge layer that helps Codex, Copilot, Cursor,')}`,
|
|
14
|
-
` ${theme.hex('#c084fc')('
|
|
14
|
+
` ${theme.hex('#c084fc')('Claude Code, Gemini, Windsurf, and Cline reuse repo intelligence.')}`,
|
|
15
15
|
'',
|
|
16
16
|
theme.bold('Usage'),
|
|
17
17
|
' kgraph [topic]',
|
|
@@ -19,7 +19,7 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
19
19
|
'',
|
|
20
20
|
theme.bold('Start'),
|
|
21
21
|
command('init', 'Required once: create .kgraph/ workspace'),
|
|
22
|
-
command('init --integrations codex,
|
|
22
|
+
command('init --integrations codex,gemini', 'Initialize and connect AI tools'),
|
|
23
23
|
'',
|
|
24
24
|
theme.bold('Daily workflow'),
|
|
25
25
|
command('kgraph', 'Refresh scan maps and process pending cognition notes'),
|
|
@@ -38,7 +38,7 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
38
38
|
'',
|
|
39
39
|
theme.bold('Integrations'),
|
|
40
40
|
command('integrate list', 'Show configured AI tool integrations'),
|
|
41
|
-
command('integrate add
|
|
41
|
+
command('integrate add gemini windsurf cline', 'Write KGraph instructions for AI tools'),
|
|
42
42
|
command('integrate remove cursor', 'Remove KGraph-managed instruction blocks'),
|
|
43
43
|
'',
|
|
44
44
|
theme.bold('Options'),
|
|
@@ -46,7 +46,7 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
46
46
|
command('-h, --help', 'Show this help'),
|
|
47
47
|
'',
|
|
48
48
|
`${theme.yellow('Examples')}`,
|
|
49
|
-
' kgraph init --integrations codex,copilot,cursor',
|
|
49
|
+
' kgraph init --integrations codex,copilot,cursor,claude-code,gemini,windsurf,cline',
|
|
50
50
|
' kgraph "blog admin token usage"',
|
|
51
51
|
' kgraph doctor',
|
|
52
52
|
'',
|
|
@@ -56,7 +56,7 @@ export function renderRootHelp(useColor = supportsColor()) {
|
|
|
56
56
|
}
|
|
57
57
|
export function renderWorkflowBanner(stats, useColor = supportsColor()) {
|
|
58
58
|
const theme = new Chalk({ level: useColor ? 3 : 0 });
|
|
59
|
-
const command = (name, description) => ` ${theme.green(name.padEnd(
|
|
59
|
+
const command = (name, description) => ` ${theme.green(name.padEnd(42))} ${description}`;
|
|
60
60
|
return [
|
|
61
61
|
'',
|
|
62
62
|
theme.hex('#7dd3fc').bold(renderLogo()),
|
package/dist/config/config.js
CHANGED
|
@@ -25,10 +25,13 @@ export const DEFAULT_CONFIG = {
|
|
|
25
25
|
'specs',
|
|
26
26
|
'.cursor',
|
|
27
27
|
'.claude',
|
|
28
|
+
'.windsurf',
|
|
29
|
+
'.clinerules',
|
|
28
30
|
'.github/copilot-instructions.md',
|
|
29
31
|
'.github/prompts',
|
|
30
32
|
'AGENTS.md',
|
|
31
33
|
'CLAUDE.md',
|
|
34
|
+
'GEMINI.md',
|
|
32
35
|
'REQUIREMENTS.md',
|
|
33
36
|
'*.log',
|
|
34
37
|
'*.tgz',
|
|
@@ -132,7 +135,15 @@ function normalizeIntegrations(value) {
|
|
|
132
135
|
seen.has(candidate.name)) {
|
|
133
136
|
continue;
|
|
134
137
|
}
|
|
135
|
-
if (![
|
|
138
|
+
if (![
|
|
139
|
+
'claude-code',
|
|
140
|
+
'cline',
|
|
141
|
+
'codex',
|
|
142
|
+
'copilot',
|
|
143
|
+
'cursor',
|
|
144
|
+
'gemini',
|
|
145
|
+
'windsurf',
|
|
146
|
+
].includes(candidate.name)) {
|
|
136
147
|
continue;
|
|
137
148
|
}
|
|
138
149
|
seen.add(candidate.name);
|
|
@@ -39,6 +39,13 @@ export async function queryContext(workspace, config, maps, query) {
|
|
|
39
39
|
...domain.item.symbols,
|
|
40
40
|
]),
|
|
41
41
|
]);
|
|
42
|
+
for (const relationship of maps.relationshipMap.relationships) {
|
|
43
|
+
if (relatedIds.has(relationship.sourceId) ||
|
|
44
|
+
relatedIds.has(relationship.targetId)) {
|
|
45
|
+
relatedIds.add(relationship.sourceId);
|
|
46
|
+
relatedIds.add(relationship.targetId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
42
49
|
const rankedRelationships = rankByFields(query, maps.relationshipMap.relationships, [
|
|
43
50
|
{ name: 'source', value: (relationship) => relationship.sourceId },
|
|
44
51
|
{ name: 'target', value: (relationship) => relationship.targetId },
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const clineAdapter = {
|
|
2
|
+
name: 'cline',
|
|
3
|
+
label: 'Cline',
|
|
4
|
+
targetPath: '.clinerules/kgraph.md',
|
|
5
|
+
instructions: `# KGraph Workflow
|
|
6
|
+
|
|
7
|
+
- **Before exploring the repository**, run \`kgraph "<topic>"\` to refresh maps, process pending inbox notes, and load focused repo intelligence. Use the returned files, symbols, relationships, and cognition before any broad exploration.
|
|
8
|
+
- Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong. Run \`kgraph doctor --quality\` when context shows stale/noisy cognition references.
|
|
9
|
+
- At the end of any session that changed repository files, check the KGraph capture workflow before finishing.
|
|
10
|
+
- If the file change has future value, write one concise Markdown note to \`.kgraph/inbox/<slug>.md\`; otherwise explicitly skip capture as trivial.
|
|
11
|
+
- Skip capture only for read-only work, trivial formatting, typo-only docs, dependency-only churn, mechanical cleanup with no future value, or sessions where no repo files changed.
|
|
12
|
+
- Do not run KGraph repeatedly. Run it once at the start with \`kgraph "<topic>"\`. If repo files changed, write any needed inbox note first, then run \`kgraph\` once at the end.
|
|
13
|
+
- Run \`kgraph repair --dry-run\` before cleanup when stale/noisy cognition needs fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.
|
|
14
|
+
- Run \`kgraph visualize\` to open the interactive dependency graph at http://localhost:4242 with PNG export.
|
|
15
|
+
- Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
|
|
16
|
+
`,
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const geminiAdapter = {
|
|
2
|
+
name: 'gemini',
|
|
3
|
+
label: 'Gemini CLI',
|
|
4
|
+
targetPath: 'GEMINI.md',
|
|
5
|
+
instructions: `## KGraph Workflow
|
|
6
|
+
|
|
7
|
+
- **Before exploring the repository**, run \`kgraph "<topic>"\` to refresh maps, process pending inbox notes, and load focused repo intelligence. Use the returned files, symbols, relationships, and cognition before any broad exploration.
|
|
8
|
+
- Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong. Run \`kgraph doctor --quality\` when context shows stale/noisy cognition references.
|
|
9
|
+
- At the end of any session that changed repository files, check the KGraph capture workflow before finishing.
|
|
10
|
+
- If the file change has future value, write one concise Markdown note to \`.kgraph/inbox/<slug>.md\`; otherwise explicitly skip capture as trivial.
|
|
11
|
+
- Skip capture only for read-only work, trivial formatting, typo-only docs, dependency-only churn, mechanical cleanup with no future value, or sessions where no repo files changed.
|
|
12
|
+
- Do not run KGraph repeatedly. Run it once at the start with \`kgraph "<topic>"\`. If repo files changed, write any needed inbox note first, then run \`kgraph\` once at the end.
|
|
13
|
+
- Run \`kgraph repair --dry-run\` before cleanup when stale/noisy cognition needs fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.
|
|
14
|
+
- Run \`kgraph visualize\` to open the interactive dependency graph at http://localhost:4242 with PNG export.
|
|
15
|
+
- Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
|
|
16
|
+
`,
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const windsurfAdapter = {
|
|
2
|
+
name: 'windsurf',
|
|
3
|
+
label: 'Windsurf',
|
|
4
|
+
targetPath: '.windsurf/rules/kgraph.md',
|
|
5
|
+
instructions: `# KGraph Workflow
|
|
6
|
+
|
|
7
|
+
- **Before exploring the repository**, run \`kgraph "<topic>"\` to refresh maps, process pending inbox notes, and load focused repo intelligence. Use the returned files, symbols, relationships, and cognition before any broad exploration.
|
|
8
|
+
- Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong. Run \`kgraph doctor --quality\` when context shows stale/noisy cognition references.
|
|
9
|
+
- At the end of any session that changed repository files, check the KGraph capture workflow before finishing.
|
|
10
|
+
- If the file change has future value, write one concise Markdown note to \`.kgraph/inbox/<slug>.md\`; otherwise explicitly skip capture as trivial.
|
|
11
|
+
- Skip capture only for read-only work, trivial formatting, typo-only docs, dependency-only churn, mechanical cleanup with no future value, or sessions where no repo files changed.
|
|
12
|
+
- Do not run KGraph repeatedly. Run it once at the start with \`kgraph "<topic>"\`. If repo files changed, write any needed inbox note first, then run \`kgraph\` once at the end.
|
|
13
|
+
- Run \`kgraph repair --dry-run\` before cleanup when stale/noisy cognition needs fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.
|
|
14
|
+
- Run \`kgraph visualize\` to open the interactive dependency graph at http://localhost:4242 with PNG export.
|
|
15
|
+
- Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
|
|
16
|
+
`,
|
|
17
|
+
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { claudeCodeAdapter } from "./adapters/claude-code.js";
|
|
2
|
+
import { clineAdapter } from "./adapters/cline.js";
|
|
2
3
|
import { codexAdapter } from "./adapters/codex.js";
|
|
3
4
|
import { copilotAdapter } from "./adapters/copilot.js";
|
|
4
5
|
import { cursorAdapter } from "./adapters/cursor.js";
|
|
6
|
+
import { geminiAdapter } from "./adapters/gemini.js";
|
|
7
|
+
import { windsurfAdapter } from "./adapters/windsurf.js";
|
|
5
8
|
const ADAPTERS = [
|
|
6
9
|
claudeCodeAdapter,
|
|
10
|
+
clineAdapter,
|
|
7
11
|
codexAdapter,
|
|
8
12
|
copilotAdapter,
|
|
9
|
-
cursorAdapter
|
|
13
|
+
cursorAdapter,
|
|
14
|
+
geminiAdapter,
|
|
15
|
+
windsurfAdapter
|
|
10
16
|
].sort((left, right) => left.name.localeCompare(right.name));
|
|
11
17
|
export function listIntegrationAdapters() {
|
|
12
18
|
return ADAPTERS;
|
|
@@ -6,11 +6,14 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
6
6
|
const dependencies = [];
|
|
7
7
|
const relationships = [];
|
|
8
8
|
const warnings = [];
|
|
9
|
+
const symbolIdsByNode = new Map();
|
|
10
|
+
const symbolsByName = new Map();
|
|
11
|
+
const importedBindings = new Map();
|
|
9
12
|
const addSymbol = (name, kind, node, exported = false, parentName) => {
|
|
10
13
|
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
11
14
|
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
12
15
|
const id = [filePath, kind, parentName, name, start.line + 1, end.line + 1].filter(Boolean).join("#");
|
|
13
|
-
|
|
16
|
+
const symbol = {
|
|
14
17
|
id,
|
|
15
18
|
name,
|
|
16
19
|
kind,
|
|
@@ -19,7 +22,12 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
19
22
|
endLine: end.line + 1,
|
|
20
23
|
exported,
|
|
21
24
|
parentName
|
|
22
|
-
}
|
|
25
|
+
};
|
|
26
|
+
symbols.push(symbol);
|
|
27
|
+
symbolIdsByNode.set(node, id);
|
|
28
|
+
const byName = symbolsByName.get(name) ?? [];
|
|
29
|
+
byName.push(symbol);
|
|
30
|
+
symbolsByName.set(name, byName);
|
|
23
31
|
relationships.push({
|
|
24
32
|
sourceType: "file",
|
|
25
33
|
sourceId: filePath,
|
|
@@ -28,8 +36,9 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
28
36
|
relationshipType: "contains",
|
|
29
37
|
confidence: "high"
|
|
30
38
|
});
|
|
39
|
+
return symbol;
|
|
31
40
|
};
|
|
32
|
-
const
|
|
41
|
+
const collectSymbols = (node, parentName) => {
|
|
33
42
|
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
34
43
|
const specifier = node.moduleSpecifier.text;
|
|
35
44
|
const dependency = {
|
|
@@ -48,6 +57,7 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
48
57
|
relationshipType: "import",
|
|
49
58
|
confidence: dependency.resolvedFile ? "high" : "medium"
|
|
50
59
|
});
|
|
60
|
+
collectImportedBindings(node, specifier, dependency.resolvedFile, importedBindings);
|
|
51
61
|
}
|
|
52
62
|
if (ts.isExportDeclaration(node)) {
|
|
53
63
|
const name = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : "export";
|
|
@@ -66,10 +76,18 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
if (ts.isClassDeclaration(node) && node.name) {
|
|
69
|
-
addSymbol(node.name.text, "class", node, isExported(node), parentName);
|
|
79
|
+
const classSymbol = addSymbol(node.name.text, "class", node, isExported(node), parentName);
|
|
70
80
|
node.members.forEach((member) => {
|
|
71
81
|
if (ts.isMethodDeclaration(member) && member.name && ts.isIdentifier(member.name)) {
|
|
72
|
-
addSymbol(member.name.text, "method", member, false, node.name?.text);
|
|
82
|
+
const methodSymbol = addSymbol(member.name.text, "method", member, false, node.name?.text);
|
|
83
|
+
relationships.push({
|
|
84
|
+
sourceType: "symbol",
|
|
85
|
+
sourceId: classSymbol.id,
|
|
86
|
+
targetType: "symbol",
|
|
87
|
+
targetId: methodSymbol.id,
|
|
88
|
+
relationshipType: "symbol-contains",
|
|
89
|
+
confidence: "high"
|
|
90
|
+
});
|
|
73
91
|
}
|
|
74
92
|
});
|
|
75
93
|
}
|
|
@@ -79,16 +97,85 @@ export function extractTsSymbols(sourceText, filePath) {
|
|
|
79
97
|
if (ts.isTypeAliasDeclaration(node)) {
|
|
80
98
|
addSymbol(node.name.text, "type", node, isExported(node), parentName);
|
|
81
99
|
}
|
|
82
|
-
ts.forEachChild(node, (child) =>
|
|
100
|
+
ts.forEachChild(node, (child) => collectSymbols(child, parentName));
|
|
101
|
+
};
|
|
102
|
+
const collectCalls = (node, currentSymbolId) => {
|
|
103
|
+
const nextSymbolId = symbolIdsByNode.get(node) ?? currentSymbolId;
|
|
104
|
+
if (ts.isCallExpression(node) && nextSymbolId) {
|
|
105
|
+
const target = resolveCallTarget(node, symbolsByName, importedBindings);
|
|
106
|
+
if (target) {
|
|
107
|
+
relationships.push({
|
|
108
|
+
sourceType: "symbol",
|
|
109
|
+
sourceId: nextSymbolId,
|
|
110
|
+
targetType: target.targetType,
|
|
111
|
+
targetId: target.targetId,
|
|
112
|
+
relationshipType: "calls",
|
|
113
|
+
confidence: target.confidence
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
ts.forEachChild(node, (child) => collectCalls(child, nextSymbolId));
|
|
83
118
|
};
|
|
84
119
|
try {
|
|
85
|
-
|
|
120
|
+
collectSymbols(sourceFile);
|
|
121
|
+
collectCalls(sourceFile);
|
|
86
122
|
}
|
|
87
123
|
catch (error) {
|
|
88
124
|
warnings.push(error instanceof Error ? error.message : String(error));
|
|
89
125
|
}
|
|
90
126
|
return { symbols, dependencies, relationships, warnings };
|
|
91
127
|
}
|
|
128
|
+
function collectImportedBindings(node, specifier, resolvedFile, importedBindings) {
|
|
129
|
+
const clause = node.importClause;
|
|
130
|
+
if (!clause) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (clause.name) {
|
|
134
|
+
importedBindings.set(clause.name.text, { specifier, resolvedFile });
|
|
135
|
+
}
|
|
136
|
+
const bindings = clause.namedBindings;
|
|
137
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
138
|
+
for (const element of bindings.elements) {
|
|
139
|
+
importedBindings.set(element.name.text, { specifier, resolvedFile });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function resolveCallTarget(node, symbolsByName, importedBindings) {
|
|
144
|
+
const expression = node.expression;
|
|
145
|
+
if (ts.isIdentifier(expression)) {
|
|
146
|
+
const localSymbols = symbolsByName
|
|
147
|
+
.get(expression.text)
|
|
148
|
+
?.filter((symbol) => symbol.kind === "function" || symbol.kind === "method");
|
|
149
|
+
if (localSymbols?.[0]) {
|
|
150
|
+
return {
|
|
151
|
+
targetType: "symbol",
|
|
152
|
+
targetId: localSymbols[0].id,
|
|
153
|
+
confidence: "high"
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const imported = importedBindings.get(expression.text);
|
|
157
|
+
if (imported) {
|
|
158
|
+
return {
|
|
159
|
+
targetType: "symbol",
|
|
160
|
+
targetId: imported.resolvedFile ? `${imported.resolvedFile}#${expression.text}` : expression.text,
|
|
161
|
+
confidence: imported.resolvedFile ? "medium" : "low"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
targetType: "symbol",
|
|
166
|
+
targetId: expression.text,
|
|
167
|
+
confidence: "low"
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (ts.isPropertyAccessExpression(expression)) {
|
|
171
|
+
return {
|
|
172
|
+
targetType: "symbol",
|
|
173
|
+
targetId: expression.getText(),
|
|
174
|
+
confidence: "low"
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
92
179
|
function isExported(node) {
|
|
93
180
|
return ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword));
|
|
94
181
|
}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface DomainHint {
|
|
|
12
12
|
paths?: string[];
|
|
13
13
|
tags?: string[];
|
|
14
14
|
}
|
|
15
|
-
export type IntegrationName = "claude-code" | "codex" | "copilot" | "cursor";
|
|
15
|
+
export type IntegrationName = "claude-code" | "cline" | "codex" | "copilot" | "cursor" | "gemini" | "windsurf";
|
|
16
16
|
export interface IntegrationConfig {
|
|
17
17
|
name: IntegrationName;
|
|
18
18
|
enabled: boolean;
|
package/dist/types/maps.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type ScanStatus = "mapped" | "generic" | "failed";
|
|
2
2
|
export type DependencyKind = "local" | "package" | "unknown";
|
|
3
3
|
export type SymbolKind = "function" | "class" | "method" | "type" | "interface" | "export" | "import";
|
|
4
|
-
export type RelationshipType = "import" | "contains" | "mentions" | "belongs-to-domain" | "stale-reference" | "moved-from";
|
|
4
|
+
export type RelationshipType = "import" | "contains" | "symbol-contains" | "calls" | "mentions" | "belongs-to-domain" | "stale-reference" | "moved-from";
|
|
5
5
|
export interface RepositoryFile {
|
|
6
6
|
id: string;
|
|
7
7
|
path: string;
|