scai 0.1.117 → 0.1.118
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/dist/agents/MainAgent.js +255 -0
- package/dist/agents/contextReviewStep.js +104 -0
- package/dist/agents/finalPlanGenStep.js +123 -0
- package/dist/agents/infoPlanGenStep.js +126 -0
- package/dist/agents/planGeneratorStep.js +118 -0
- package/dist/agents/planResolverStep.js +95 -0
- package/dist/agents/planTargetFilesStep.js +48 -0
- package/dist/agents/preFileSearchCheckStep.js +95 -0
- package/dist/agents/selectRelevantSourcesStep.js +100 -0
- package/dist/agents/semanticAnalysisStep.js +144 -0
- package/dist/agents/structuralAnalysisStep.js +46 -0
- package/dist/agents/transformPlanGenStep.js +107 -0
- package/dist/agents/understandIntentStep.js +72 -0
- package/dist/agents/validationAnalysisStep.js +87 -0
- package/dist/commands/AskCmd.js +47 -116
- package/dist/commands/ChangeLogUpdateCmd.js +11 -5
- package/dist/commands/CommitSuggesterCmd.js +50 -75
- package/dist/commands/DaemonCmd.js +119 -29
- package/dist/commands/IndexCmd.js +41 -24
- package/dist/commands/InspectCmd.js +0 -1
- package/dist/commands/ReadlineSingleton.js +18 -0
- package/dist/commands/ResetDbCmd.js +20 -21
- package/dist/commands/ReviewCmd.js +89 -54
- package/dist/commands/SummaryCmd.js +12 -18
- package/dist/commands/WorkflowCmd.js +41 -0
- package/dist/commands/factory.js +254 -0
- package/dist/config.js +67 -15
- package/dist/constants.js +20 -4
- package/dist/context.js +10 -11
- package/dist/daemon/daemonQueues.js +63 -0
- package/dist/daemon/daemonWorker.js +40 -63
- package/dist/daemon/generateSummaries.js +58 -0
- package/dist/daemon/runFolderCapsuleBatch.js +247 -0
- package/dist/daemon/runIndexingBatch.js +147 -0
- package/dist/daemon/runKgBatch.js +104 -0
- package/dist/db/fileIndex.js +168 -63
- package/dist/db/functionExtractors/extractFromJava.js +210 -6
- package/dist/db/functionExtractors/extractFromJs.js +173 -214
- package/dist/db/functionExtractors/extractFromTs.js +159 -160
- package/dist/db/functionExtractors/index.js +7 -5
- package/dist/db/schema.js +55 -20
- package/dist/db/sqlTemplates.js +50 -19
- package/dist/fileRules/builtins.js +31 -14
- package/dist/fileRules/codeAllowedExtensions.js +4 -0
- package/dist/fileRules/fileExceptions.js +0 -13
- package/dist/fileRules/ignoredExtensions.js +10 -0
- package/dist/index.js +128 -325
- package/dist/lib/generate.js +37 -14
- package/dist/lib/generateFolderCapsules.js +109 -0
- package/dist/lib/spinner.js +12 -5
- package/dist/modelSetup.js +0 -10
- package/dist/pipeline/modules/changeLogModule.js +16 -19
- package/dist/pipeline/modules/chunkManagerModule.js +24 -0
- package/dist/pipeline/modules/cleanupModule.js +96 -91
- package/dist/pipeline/modules/codeTransformModule.js +208 -0
- package/dist/pipeline/modules/commentModule.js +20 -11
- package/dist/pipeline/modules/commitSuggesterModule.js +36 -14
- package/dist/pipeline/modules/contextReviewModule.js +52 -0
- package/dist/pipeline/modules/fileReaderModule.js +72 -0
- package/dist/pipeline/modules/fileSearchModule.js +136 -0
- package/dist/pipeline/modules/finalAnswerModule.js +53 -0
- package/dist/pipeline/modules/gatherInfoModule.js +176 -0
- package/dist/pipeline/modules/generateTestsModule.js +63 -54
- package/dist/pipeline/modules/kgModule.js +26 -11
- package/dist/pipeline/modules/preserveCodeModule.js +91 -49
- package/dist/pipeline/modules/refactorModule.js +19 -7
- package/dist/pipeline/modules/repairTestsModule.js +44 -36
- package/dist/pipeline/modules/reviewModule.js +23 -13
- package/dist/pipeline/modules/summaryModule.js +27 -35
- package/dist/pipeline/modules/writeFileModule.js +86 -0
- package/dist/pipeline/registry/moduleRegistry.js +38 -93
- package/dist/pipeline/runModulePipeline.js +22 -19
- package/dist/scripts/dbcheck.js +143 -228
- package/dist/utils/buildContextualPrompt.js +245 -172
- package/dist/utils/debugContext.js +24 -0
- package/dist/utils/fileTree.js +16 -6
- package/dist/utils/loadRelevantFolderCapsules.js +64 -0
- package/dist/utils/log.js +2 -0
- package/dist/utils/normalizeData.js +23 -0
- package/dist/utils/planActions.js +60 -0
- package/dist/utils/promptBuilderHelper.js +67 -0
- package/dist/utils/promptLogHelper.js +52 -0
- package/dist/utils/sanitizeQuery.js +20 -8
- package/dist/utils/sleep.js +3 -0
- package/dist/utils/splitCodeIntoChunk.js +65 -32
- package/dist/utils/vscode.js +49 -0
- package/dist/workflow/workflowResolver.js +14 -0
- package/dist/workflow/workflowRunner.js +103 -0
- package/package.json +6 -5
- package/dist/agent/agentManager.js +0 -39
- package/dist/agent/workflowManager.js +0 -95
- package/dist/commands/ModulePipelineCmd.js +0 -31
- package/dist/daemon/daemonBatch.js +0 -186
- package/dist/fileRules/scoreFiles.js +0 -71
- package/dist/lib/generateEmbedding.js +0 -22
|
@@ -1,201 +1,187 @@
|
|
|
1
1
|
import { parse } from 'acorn';
|
|
2
2
|
import { ancestor as walkAncestor } from 'acorn-walk';
|
|
3
|
-
import { generateEmbedding } from '../../lib/generateEmbedding.js';
|
|
4
3
|
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
5
|
import { log } from '../../utils/log.js';
|
|
6
6
|
import { markFileAsSkippedTemplate, markFileAsExtractedTemplate, markFileAsFailedTemplate, insertFunctionTemplate, insertGraphClassTemplate, insertEdgeTemplate, insertGraphEntityTagTemplate, insertGraphTagTemplate, selectGraphTagIdTemplate, } from '../sqlTemplates.js';
|
|
7
7
|
import { getDbForRepo } from '../client.js';
|
|
8
8
|
import { kgModule } from '../../pipeline/modules/kgModule.js';
|
|
9
|
-
import { BUILTINS } from '../../fileRules/builtins.js';
|
|
10
9
|
import { getUniqueId } from '../../utils/sharedUtils.js';
|
|
11
|
-
|
|
10
|
+
import { BUILTINS } from '../../fileRules/builtins.js';
|
|
11
|
+
/** Determine function name from AST node and its parent */
|
|
12
|
+
function getFunctionName(node, parent) {
|
|
12
13
|
if (node.id?.name)
|
|
13
14
|
return node.id.name;
|
|
14
15
|
if (parent?.type === 'VariableDeclarator' && parent.id?.name)
|
|
15
|
-
return parent.id
|
|
16
|
+
return parent.id?.name;
|
|
16
17
|
if (parent?.type === 'Property' && parent.key?.name)
|
|
17
|
-
return parent.key
|
|
18
|
+
return parent.key?.name;
|
|
18
19
|
if (parent?.type === 'AssignmentExpression' && parent.left?.name)
|
|
19
|
-
return parent.left
|
|
20
|
+
return parent.left?.name;
|
|
20
21
|
if (parent?.type === 'MethodDefinition' && parent.key?.name)
|
|
21
|
-
return parent.key
|
|
22
|
+
return parent.key?.name;
|
|
22
23
|
return '<anon>';
|
|
23
24
|
}
|
|
24
25
|
export async function extractFromJS(filePath, content, fileId) {
|
|
25
26
|
const db = getDbForRepo();
|
|
26
27
|
const normalizedPath = path.normalize(filePath).replace(/\\/g, '/');
|
|
28
|
+
if (!fileId || typeof fileId !== 'number') {
|
|
29
|
+
console.error(`❌ extractFromJS: invalid or missing fileId for ${filePath}`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (!content || typeof content !== 'string' || !content.trim()) {
|
|
33
|
+
console.error(`❌ extractFromJS: empty or invalid file content for ${filePath}`);
|
|
34
|
+
db.prepare(markFileAsFailedTemplate).run({ id: fileId });
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
27
37
|
try {
|
|
28
38
|
const ast = parse(content, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
29
39
|
const functions = [];
|
|
30
40
|
const classes = [];
|
|
31
41
|
const imports = [];
|
|
32
42
|
const exports = [];
|
|
33
|
-
// ---
|
|
43
|
+
// --- Function info ---
|
|
44
|
+
const handleFunctionNode = (node, ancestors) => {
|
|
45
|
+
const parent = ancestors[ancestors.length - 2];
|
|
46
|
+
const name = getFunctionName(node, parent);
|
|
47
|
+
const funcContent = content.slice(node.start, node.end);
|
|
48
|
+
const unique_id = getUniqueId(name, filePath, node.loc?.start.line ?? -1, node.start, funcContent);
|
|
49
|
+
functions.push({
|
|
50
|
+
name,
|
|
51
|
+
start_line: node.loc?.start.line ?? -1,
|
|
52
|
+
end_line: node.loc?.end.line ?? -1,
|
|
53
|
+
content: funcContent,
|
|
54
|
+
unique_id,
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
// --- Class info ---
|
|
58
|
+
const handleClassNode = (node) => {
|
|
59
|
+
const className = node.id?.name || `${path.basename(filePath)}:<anon-class>`;
|
|
60
|
+
const classContent = content.slice(node.start, node.end);
|
|
61
|
+
const unique_id = node.id?.name
|
|
62
|
+
? `${className}@${normalizedPath}`
|
|
63
|
+
: getUniqueId(className, filePath, node.loc?.start.line ?? -1, node.start, classContent);
|
|
64
|
+
classes.push({
|
|
65
|
+
name: className,
|
|
66
|
+
start_line: node.loc?.start.line ?? -1,
|
|
67
|
+
end_line: node.loc?.end.line ?? -1,
|
|
68
|
+
content: classContent,
|
|
69
|
+
superClass: node.superClass?.name ?? null,
|
|
70
|
+
unique_id,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
// --- AST traversal ---
|
|
34
74
|
walkAncestor(ast, {
|
|
35
|
-
ImportDeclaration(node) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
FunctionDeclaration(node, ancestors) {
|
|
48
|
-
const parent = ancestors[ancestors.length - 2];
|
|
49
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
50
|
-
const funcContent = content.slice(node.start, node.end);
|
|
51
|
-
const unique_id = getUniqueId(name, filePath, node.loc?.start.line ?? -1, node.start, funcContent);
|
|
52
|
-
functions.push({
|
|
53
|
-
name,
|
|
54
|
-
start_line: node.loc?.start.line ?? -1,
|
|
55
|
-
end_line: node.loc?.end.line ?? -1,
|
|
56
|
-
content: funcContent,
|
|
57
|
-
unique_id,
|
|
58
|
-
});
|
|
59
|
-
},
|
|
60
|
-
FunctionExpression(node, ancestors) {
|
|
61
|
-
const parent = ancestors[ancestors.length - 2];
|
|
62
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
63
|
-
const funcContent = content.slice(node.start, node.end);
|
|
64
|
-
const unique_id = getUniqueId(name, filePath, node.loc?.start.line ?? -1, node.start, funcContent);
|
|
65
|
-
functions.push({
|
|
66
|
-
name,
|
|
67
|
-
start_line: node.loc?.start.line ?? -1,
|
|
68
|
-
end_line: node.loc?.end.line ?? -1,
|
|
69
|
-
content: funcContent,
|
|
70
|
-
unique_id,
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
ArrowFunctionExpression(node, ancestors) {
|
|
74
|
-
const parent = ancestors[ancestors.length - 2];
|
|
75
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
76
|
-
const funcContent = content.slice(node.start, node.end);
|
|
77
|
-
const unique_id = getUniqueId(name, filePath, node.loc?.start.line ?? -1, node.start, funcContent);
|
|
78
|
-
functions.push({
|
|
79
|
-
name,
|
|
80
|
-
start_line: node.loc?.start.line ?? -1,
|
|
81
|
-
end_line: node.loc?.end.line ?? -1,
|
|
82
|
-
content: funcContent,
|
|
83
|
-
unique_id,
|
|
84
|
-
});
|
|
85
|
-
},
|
|
86
|
-
ClassDeclaration(node) {
|
|
87
|
-
const className = node.id?.name || `${path.basename(filePath)}:<anon-class>`;
|
|
88
|
-
const classContent = content.slice(node.start, node.end);
|
|
89
|
-
const unique_id = node.id?.name
|
|
90
|
-
? `${className}@${normalizedPath}`
|
|
91
|
-
: getUniqueId(className, filePath, node.loc?.start.line ?? -1, node.start, classContent);
|
|
92
|
-
classes.push({
|
|
93
|
-
name: className,
|
|
94
|
-
start_line: node.loc?.start.line ?? -1,
|
|
95
|
-
end_line: node.loc?.end.line ?? -1,
|
|
96
|
-
content: classContent,
|
|
97
|
-
superClass: node.superClass?.name ?? null,
|
|
98
|
-
unique_id,
|
|
99
|
-
});
|
|
100
|
-
},
|
|
101
|
-
ClassExpression(node) {
|
|
102
|
-
const className = node.id?.name || `${path.basename(filePath)}:<anon-class>`;
|
|
103
|
-
const classContent = content.slice(node.start, node.end);
|
|
104
|
-
const unique_id = node.id?.name
|
|
105
|
-
? `${className}@${normalizedPath}`
|
|
106
|
-
: getUniqueId(className, filePath, node.loc?.start.line ?? -1, node.start, classContent);
|
|
107
|
-
classes.push({
|
|
108
|
-
name: className,
|
|
109
|
-
start_line: node.loc?.start.line ?? -1,
|
|
110
|
-
end_line: node.loc?.end.line ?? -1,
|
|
111
|
-
content: classContent,
|
|
112
|
-
superClass: node.superClass?.name ?? null,
|
|
113
|
-
unique_id,
|
|
114
|
-
});
|
|
115
|
-
},
|
|
75
|
+
ImportDeclaration(node) { if (node.source?.value)
|
|
76
|
+
imports.push(node.source.value); },
|
|
77
|
+
ExportNamedDeclaration(node) { if (node.source?.value)
|
|
78
|
+
exports.push(node.source.value); },
|
|
79
|
+
ExportAllDeclaration(node) { if (node.source?.value)
|
|
80
|
+
exports.push(node.source.value); },
|
|
81
|
+
FunctionDeclaration: handleFunctionNode,
|
|
82
|
+
FunctionExpression: handleFunctionNode,
|
|
83
|
+
ArrowFunctionExpression: handleFunctionNode,
|
|
84
|
+
ClassDeclaration: handleClassNode,
|
|
85
|
+
ClassExpression: handleClassNode,
|
|
116
86
|
});
|
|
117
87
|
if (functions.length === 0 && classes.length === 0) {
|
|
118
88
|
log(`⚠️ No functions/classes found in JS file: ${filePath}`);
|
|
119
|
-
|
|
89
|
+
try {
|
|
90
|
+
db.prepare(markFileAsSkippedTemplate).run({ id: fileId });
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
120
93
|
return false;
|
|
121
94
|
}
|
|
122
|
-
log(`🔍 Found ${functions.length} functions and ${classes.length} classes in ${filePath}`);
|
|
123
95
|
// --- KG tagging ---
|
|
124
96
|
try {
|
|
125
97
|
const kgInput = { fileId, filepath: filePath, summary: undefined };
|
|
126
98
|
const kgResult = await kgModule.run(kgInput, content);
|
|
127
|
-
if (kgResult.entities?.length) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
99
|
+
if (!kgResult.entities?.length) {
|
|
100
|
+
console.log(chalk.yellow(`⚠️ [KG] Garbage or empty result for ${filePath}`));
|
|
101
|
+
db.prepare(markFileAsFailedTemplate).run({ id: fileId });
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const insertTagStmt = db.prepare(insertGraphTagTemplate);
|
|
105
|
+
const getTagIdStmt = db.prepare(selectGraphTagIdTemplate);
|
|
106
|
+
const insertEntityTagStmt = db.prepare(insertGraphEntityTagTemplate);
|
|
107
|
+
const persistTag = (tag) => {
|
|
108
|
+
try {
|
|
109
|
+
insertTagStmt.run({ name: tag });
|
|
110
|
+
return getTagIdStmt.get({ name: tag })?.id;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const persistEntityTags = (entity) => {
|
|
117
|
+
if (!entity.type || !Array.isArray(entity.tags) || !entity.tags.length)
|
|
118
|
+
return;
|
|
119
|
+
for (const tag of entity.tags) {
|
|
120
|
+
if (!tag || typeof tag !== 'string')
|
|
133
121
|
continue;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
entity_type: entity.type,
|
|
147
|
-
entity_unique_id: matchedUniqueId,
|
|
148
|
-
tag_id: tagRow.id,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
console.error('❌ Failed to persist entity/tag', { entity, tag, error: err });
|
|
153
|
-
}
|
|
122
|
+
const tagId = persistTag(tag);
|
|
123
|
+
if (!tagId)
|
|
124
|
+
continue;
|
|
125
|
+
const matchedUniqueId = functions.find(f => f.name === entity.name)?.unique_id ||
|
|
126
|
+
classes.find(c => c.name === entity.name)?.unique_id ||
|
|
127
|
+
`${entity.name}@${filePath}`;
|
|
128
|
+
try {
|
|
129
|
+
insertEntityTagStmt.run({
|
|
130
|
+
entity_type: entity.type,
|
|
131
|
+
entity_unique_id: matchedUniqueId,
|
|
132
|
+
tag_id: tagId,
|
|
133
|
+
});
|
|
154
134
|
}
|
|
135
|
+
catch { /* ignore */ }
|
|
155
136
|
}
|
|
156
|
-
|
|
157
|
-
|
|
137
|
+
};
|
|
138
|
+
kgResult.entities.forEach(persistEntityTags);
|
|
158
139
|
}
|
|
159
140
|
catch (kgErr) {
|
|
160
|
-
console.
|
|
141
|
+
console.log(chalk.yellow(`⚠️ [KG] Garbage or failed parse for ${filePath}: ${kgErr instanceof Error ? kgErr.message : kgErr}`));
|
|
142
|
+
db.prepare(markFileAsFailedTemplate).run({ id: fileId });
|
|
143
|
+
return false;
|
|
161
144
|
}
|
|
162
|
-
// --- Insert functions + call edges ---
|
|
163
145
|
const seenEdges = new Set();
|
|
146
|
+
const jsBuiltins = BUILTINS.ts;
|
|
147
|
+
// --- Insert functions ---
|
|
164
148
|
for (const fn of functions) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
149
|
+
db.prepare(insertFunctionTemplate).run({
|
|
150
|
+
file_id: fileId,
|
|
151
|
+
name: fn.name,
|
|
152
|
+
start_line: fn.start_line,
|
|
153
|
+
end_line: fn.end_line,
|
|
154
|
+
content: fn.content,
|
|
155
|
+
embedding: null,
|
|
156
|
+
lang: 'js',
|
|
157
|
+
unique_id: fn.unique_id,
|
|
158
|
+
});
|
|
159
|
+
const edgeKey = `file->${fn.unique_id}`;
|
|
160
|
+
if (!seenEdges.has(edgeKey)) {
|
|
161
|
+
db.prepare(insertEdgeTemplate).run({
|
|
162
|
+
source_type: 'file',
|
|
163
|
+
source_unique_id: normalizedPath,
|
|
164
|
+
target_type: 'function',
|
|
165
|
+
target_unique_id: fn.unique_id,
|
|
166
|
+
relation: 'contains',
|
|
176
167
|
});
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
source_type: 'file',
|
|
182
|
-
source_unique_id: normalizedPath,
|
|
183
|
-
target_type: 'function',
|
|
184
|
-
target_unique_id: fn.unique_id,
|
|
185
|
-
relation: 'contains',
|
|
186
|
-
});
|
|
187
|
-
seenEdges.add(containsEdgeKey);
|
|
188
|
-
}
|
|
189
|
-
// Call edges
|
|
168
|
+
seenEdges.add(edgeKey);
|
|
169
|
+
}
|
|
170
|
+
// --- JS call edges ---
|
|
171
|
+
try {
|
|
190
172
|
const fnAst = parse(fn.content, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
191
173
|
walkAncestor(fnAst, {
|
|
192
174
|
CallExpression(node) {
|
|
193
|
-
|
|
194
|
-
if (
|
|
175
|
+
let calleeName;
|
|
176
|
+
if (node.callee?.name)
|
|
177
|
+
calleeName = node.callee.name;
|
|
178
|
+
else if (node.callee?.property?.name)
|
|
179
|
+
calleeName = node.callee.property.name;
|
|
180
|
+
if (!calleeName || jsBuiltins.has(calleeName))
|
|
195
181
|
return;
|
|
196
182
|
const targetUniqueId = `${calleeName}@${normalizedPath}`;
|
|
197
|
-
const
|
|
198
|
-
if (!seenEdges.has(
|
|
183
|
+
const callEdgeKey = `${fn.unique_id}->${targetUniqueId}`;
|
|
184
|
+
if (!seenEdges.has(callEdgeKey)) {
|
|
199
185
|
try {
|
|
200
186
|
db.prepare(insertEdgeTemplate).run({
|
|
201
187
|
source_type: 'function',
|
|
@@ -204,93 +190,66 @@ export async function extractFromJS(filePath, content, fileId) {
|
|
|
204
190
|
target_unique_id: targetUniqueId,
|
|
205
191
|
relation: 'calls',
|
|
206
192
|
});
|
|
207
|
-
seenEdges.add(
|
|
208
|
-
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
console.error('❌ Failed to persist call edge', { fn: fn.name, calleeName, error: err });
|
|
193
|
+
seenEdges.add(callEdgeKey);
|
|
211
194
|
}
|
|
195
|
+
catch { /* ignore */ }
|
|
212
196
|
}
|
|
213
197
|
},
|
|
214
198
|
});
|
|
215
|
-
log(`📌 Indexed JS function: ${fn.name}`);
|
|
216
199
|
}
|
|
217
|
-
catch (
|
|
218
|
-
console.
|
|
200
|
+
catch (parseErr) {
|
|
201
|
+
console.log(chalk.yellow(`⚠️ [Extract] Failed to parse function ${fn.name} in ${filePath}: ${parseErr.message}`));
|
|
219
202
|
}
|
|
220
203
|
}
|
|
221
|
-
// --- Insert classes
|
|
204
|
+
// --- Insert classes ---
|
|
222
205
|
for (const cls of classes) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
206
|
+
db.prepare(insertGraphClassTemplate).run({
|
|
207
|
+
file_id: fileId,
|
|
208
|
+
name: cls.name,
|
|
209
|
+
start_line: cls.start_line,
|
|
210
|
+
end_line: cls.end_line,
|
|
211
|
+
content: cls.content,
|
|
212
|
+
embedding: null,
|
|
213
|
+
lang: 'js',
|
|
214
|
+
unique_id: cls.unique_id,
|
|
215
|
+
});
|
|
216
|
+
db.prepare(insertEdgeTemplate).run({
|
|
217
|
+
source_type: 'file',
|
|
218
|
+
source_unique_id: normalizedPath,
|
|
219
|
+
target_type: 'class',
|
|
220
|
+
target_unique_id: cls.unique_id,
|
|
221
|
+
relation: 'contains',
|
|
222
|
+
});
|
|
223
|
+
if (cls.superClass) {
|
|
235
224
|
db.prepare(insertEdgeTemplate).run({
|
|
236
|
-
source_type: '
|
|
237
|
-
source_unique_id:
|
|
238
|
-
target_type:
|
|
239
|
-
|
|
240
|
-
relation: 'contains',
|
|
225
|
+
source_type: 'class',
|
|
226
|
+
source_unique_id: cls.unique_id,
|
|
227
|
+
target_type: `unresolved:${cls.superClass}`,
|
|
228
|
+
relation: 'inherits',
|
|
241
229
|
});
|
|
242
|
-
if (cls.superClass) {
|
|
243
|
-
db.prepare(insertEdgeTemplate).run({
|
|
244
|
-
source_type: 'class',
|
|
245
|
-
source_unique_id: cls.unique_id,
|
|
246
|
-
target_type: `unresolved:${cls.superClass}`,
|
|
247
|
-
relation: 'inherits',
|
|
248
|
-
});
|
|
249
|
-
log(`🔗 Class ${cls.name} extends ${cls.superClass}`);
|
|
250
|
-
}
|
|
251
|
-
log(`🏷 Indexed JS class: ${cls.name}`);
|
|
252
|
-
}
|
|
253
|
-
catch (err) {
|
|
254
|
-
console.error('❌ Failed to insert class or edges', { cls, error: err });
|
|
255
230
|
}
|
|
256
231
|
}
|
|
257
|
-
// --- Imports
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
source_unique_id: normalizedPath,
|
|
263
|
-
target_type: 'file',
|
|
264
|
-
target_unique_id: `file@${imp}`,
|
|
265
|
-
relation: 'imports',
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
catch (err) {
|
|
269
|
-
console.error('❌ Failed to persist import edge', { imp, error: err });
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
for (const exp of exports) {
|
|
273
|
-
try {
|
|
232
|
+
// --- Imports/Exports edges ---
|
|
233
|
+
const handleEdges = (items, relation) => {
|
|
234
|
+
for (const item of items) {
|
|
235
|
+
const resolved = item.startsWith('.') ? path.resolve(path.dirname(filePath), item) : item;
|
|
236
|
+
const normalizedTarget = path.normalize(resolved).replace(/\\/g, '/');
|
|
274
237
|
db.prepare(insertEdgeTemplate).run({
|
|
275
238
|
source_type: 'file',
|
|
276
239
|
source_unique_id: normalizedPath,
|
|
277
240
|
target_type: 'file',
|
|
278
|
-
target_unique_id:
|
|
279
|
-
relation
|
|
241
|
+
target_unique_id: normalizedTarget,
|
|
242
|
+
relation,
|
|
280
243
|
});
|
|
281
244
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
log(`📊 Extraction summary for ${filePath}: ${functions.length} functions, ${classes.length} classes, ${imports.length} imports, ${exports.length} exports`);
|
|
245
|
+
};
|
|
246
|
+
handleEdges(imports, 'imports');
|
|
247
|
+
handleEdges(exports, 'exports');
|
|
287
248
|
db.prepare(markFileAsExtractedTemplate).run({ id: fileId });
|
|
288
|
-
log(`✅ Marked JS functions/classes as extracted for ${filePath}`);
|
|
289
249
|
return true;
|
|
290
250
|
}
|
|
291
251
|
catch (err) {
|
|
292
|
-
log(
|
|
293
|
-
log(` ↳ ${err.message}`);
|
|
252
|
+
console.log(chalk.yellow(`⚠️ [Extract] Failed for ${filePath}: ${err.message}`));
|
|
294
253
|
db.prepare(markFileAsFailedTemplate).run({ id: fileId });
|
|
295
254
|
return false;
|
|
296
255
|
}
|