scai 0.1.116 → 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 +186 -198
- package/dist/db/functionExtractors/extractFromTs.js +181 -192
- 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 -0
- 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 +35 -6
- 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 +156 -91
- package/dist/utils/buildContextualPrompt.js +245 -164
- 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/sharedUtils.js +8 -0
- 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,267 +1,255 @@
|
|
|
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
|
-
|
|
9
|
+
import { getUniqueId } from '../../utils/sharedUtils.js';
|
|
10
|
+
import { BUILTINS } from '../../fileRules/builtins.js';
|
|
11
|
+
/** Determine function name from AST node and its parent */
|
|
12
|
+
function getFunctionName(node, parent) {
|
|
10
13
|
if (node.id?.name)
|
|
11
14
|
return node.id.name;
|
|
12
15
|
if (parent?.type === 'VariableDeclarator' && parent.id?.name)
|
|
13
|
-
return parent.id
|
|
16
|
+
return parent.id?.name;
|
|
14
17
|
if (parent?.type === 'Property' && parent.key?.name)
|
|
15
|
-
return parent.key
|
|
18
|
+
return parent.key?.name;
|
|
16
19
|
if (parent?.type === 'AssignmentExpression' && parent.left?.name)
|
|
17
|
-
return parent.left
|
|
20
|
+
return parent.left?.name;
|
|
18
21
|
if (parent?.type === 'MethodDefinition' && parent.key?.name)
|
|
19
|
-
return parent.key
|
|
22
|
+
return parent.key?.name;
|
|
20
23
|
return '<anon>';
|
|
21
24
|
}
|
|
22
25
|
export async function extractFromJS(filePath, content, fileId) {
|
|
23
26
|
const db = getDbForRepo();
|
|
24
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
|
+
}
|
|
25
37
|
try {
|
|
26
38
|
const ast = parse(content, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
27
39
|
const functions = [];
|
|
28
40
|
const classes = [];
|
|
29
41
|
const imports = [];
|
|
30
42
|
const exports = [];
|
|
31
|
-
// ---
|
|
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 ---
|
|
32
74
|
walkAncestor(ast, {
|
|
33
|
-
ImportDeclaration(node) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
FunctionDeclaration(node, ancestors) {
|
|
46
|
-
const parent = ancestors[ancestors.length - 2];
|
|
47
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
48
|
-
const uniqueId = name !== '<anon>'
|
|
49
|
-
? `${name}@${normalizedPath}`
|
|
50
|
-
: `${path.basename(filePath)}:<anon>@${normalizedPath}:${node.loc?.start.line}`;
|
|
51
|
-
functions.push({
|
|
52
|
-
name,
|
|
53
|
-
start_line: node.loc?.start.line ?? -1,
|
|
54
|
-
end_line: node.loc?.end.line ?? -1,
|
|
55
|
-
content: content.slice(node.start, node.end),
|
|
56
|
-
uniqueId,
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
FunctionExpression(node, ancestors) {
|
|
60
|
-
const parent = ancestors[ancestors.length - 2];
|
|
61
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
62
|
-
const uniqueId = name !== '<anon>'
|
|
63
|
-
? `${name}@${normalizedPath}`
|
|
64
|
-
: `${path.basename(filePath)}:<anon>@${normalizedPath}:${node.loc?.start.line}`;
|
|
65
|
-
functions.push({
|
|
66
|
-
name,
|
|
67
|
-
start_line: node.loc?.start.line ?? -1,
|
|
68
|
-
end_line: node.loc?.end.line ?? -1,
|
|
69
|
-
content: content.slice(node.start, node.end),
|
|
70
|
-
uniqueId,
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
ArrowFunctionExpression(node, ancestors) {
|
|
74
|
-
const parent = ancestors[ancestors.length - 2];
|
|
75
|
-
const name = getFunctionName(node, parent, path.basename(filePath));
|
|
76
|
-
const uniqueId = name !== '<anon>'
|
|
77
|
-
? `${name}@${normalizedPath}`
|
|
78
|
-
: `${path.basename(filePath)}:<anon>@${normalizedPath}:${node.loc?.start.line}`;
|
|
79
|
-
functions.push({
|
|
80
|
-
name,
|
|
81
|
-
start_line: node.loc?.start.line ?? -1,
|
|
82
|
-
end_line: node.loc?.end.line ?? -1,
|
|
83
|
-
content: content.slice(node.start, node.end),
|
|
84
|
-
uniqueId,
|
|
85
|
-
});
|
|
86
|
-
},
|
|
87
|
-
ClassDeclaration(node) {
|
|
88
|
-
const className = node.id?.name || '<anon-class>';
|
|
89
|
-
const uniqueId = className !== '<anon-class>'
|
|
90
|
-
? `${className}@${normalizedPath}`
|
|
91
|
-
: `${path.basename(filePath)}:<anon-class>@${normalizedPath}:${node.loc?.start.line}`;
|
|
92
|
-
classes.push({
|
|
93
|
-
name: className,
|
|
94
|
-
start_line: node.loc?.start.line ?? -1,
|
|
95
|
-
end_line: node.loc?.end.line ?? -1,
|
|
96
|
-
content: content.slice(node.start, node.end),
|
|
97
|
-
superClass: node.superClass?.name ?? null,
|
|
98
|
-
uniqueId,
|
|
99
|
-
});
|
|
100
|
-
},
|
|
101
|
-
ClassExpression(node) {
|
|
102
|
-
const className = node.id?.name || '<anon-class>';
|
|
103
|
-
const uniqueId = className !== '<anon-class>'
|
|
104
|
-
? `${className}@${normalizedPath}`
|
|
105
|
-
: `${path.basename(filePath)}:<anon-class>@${normalizedPath}:${node.loc?.start.line}`;
|
|
106
|
-
classes.push({
|
|
107
|
-
name: className,
|
|
108
|
-
start_line: node.loc?.start.line ?? -1,
|
|
109
|
-
end_line: node.loc?.end.line ?? -1,
|
|
110
|
-
content: content.slice(node.start, node.end),
|
|
111
|
-
superClass: node.superClass?.name ?? null,
|
|
112
|
-
uniqueId,
|
|
113
|
-
});
|
|
114
|
-
},
|
|
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,
|
|
115
86
|
});
|
|
116
87
|
if (functions.length === 0 && classes.length === 0) {
|
|
117
|
-
log(`⚠️ No functions/classes found in: ${filePath}`);
|
|
118
|
-
|
|
88
|
+
log(`⚠️ No functions/classes found in JS file: ${filePath}`);
|
|
89
|
+
try {
|
|
90
|
+
db.prepare(markFileAsSkippedTemplate).run({ id: fileId });
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
119
93
|
return false;
|
|
120
94
|
}
|
|
121
|
-
log(`🔍 Found ${functions.length} functions and ${classes.length} classes in ${filePath}`);
|
|
122
95
|
// --- KG tagging ---
|
|
123
96
|
try {
|
|
124
97
|
const kgInput = { fileId, filepath: filePath, summary: undefined };
|
|
125
98
|
const kgResult = await kgModule.run(kgInput, content);
|
|
126
|
-
if (kgResult.entities?.length
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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')
|
|
132
121
|
continue;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
entity_type: entity.type,
|
|
146
|
-
entity_unique_id: matchedUniqueId,
|
|
147
|
-
tag_id: tagRow.id,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
console.error('❌ Failed to persist entity/tag', { entity, tag, error: err });
|
|
152
|
-
}
|
|
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
|
+
});
|
|
153
134
|
}
|
|
135
|
+
catch { /* ignore */ }
|
|
154
136
|
}
|
|
155
|
-
|
|
156
|
-
|
|
137
|
+
};
|
|
138
|
+
kgResult.entities.forEach(persistEntityTags);
|
|
157
139
|
}
|
|
158
140
|
catch (kgErr) {
|
|
159
|
-
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;
|
|
160
144
|
}
|
|
161
|
-
|
|
145
|
+
const seenEdges = new Set();
|
|
146
|
+
const jsBuiltins = BUILTINS.ts;
|
|
147
|
+
// --- Insert functions ---
|
|
162
148
|
for (const fn of functions) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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)) {
|
|
175
161
|
db.prepare(insertEdgeTemplate).run({
|
|
176
162
|
source_type: 'file',
|
|
177
163
|
source_unique_id: normalizedPath,
|
|
178
164
|
target_type: 'function',
|
|
179
|
-
target_unique_id: fn.
|
|
165
|
+
target_unique_id: fn.unique_id,
|
|
180
166
|
relation: 'contains',
|
|
181
167
|
});
|
|
168
|
+
seenEdges.add(edgeKey);
|
|
169
|
+
}
|
|
170
|
+
// --- JS call edges ---
|
|
171
|
+
try {
|
|
182
172
|
const fnAst = parse(fn.content, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
|
|
183
173
|
walkAncestor(fnAst, {
|
|
184
174
|
CallExpression(node) {
|
|
185
|
-
|
|
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))
|
|
181
|
+
return;
|
|
186
182
|
const targetUniqueId = `${calleeName}@${normalizedPath}`;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
183
|
+
const callEdgeKey = `${fn.unique_id}->${targetUniqueId}`;
|
|
184
|
+
if (!seenEdges.has(callEdgeKey)) {
|
|
185
|
+
try {
|
|
186
|
+
db.prepare(insertEdgeTemplate).run({
|
|
187
|
+
source_type: 'function',
|
|
188
|
+
source_unique_id: fn.unique_id,
|
|
189
|
+
target_type: 'function',
|
|
190
|
+
target_unique_id: targetUniqueId,
|
|
191
|
+
relation: 'calls',
|
|
192
|
+
});
|
|
193
|
+
seenEdges.add(callEdgeKey);
|
|
194
|
+
}
|
|
195
|
+
catch { /* ignore */ }
|
|
196
|
+
}
|
|
194
197
|
},
|
|
195
198
|
});
|
|
196
|
-
log(`📌 Indexed JS function: ${fn.name}`);
|
|
197
199
|
}
|
|
198
|
-
catch (
|
|
199
|
-
console.
|
|
200
|
+
catch (parseErr) {
|
|
201
|
+
console.log(chalk.yellow(`⚠️ [Extract] Failed to parse function ${fn.name} in ${filePath}: ${parseErr.message}`));
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
|
-
// --- Insert classes
|
|
204
|
+
// --- Insert classes ---
|
|
203
205
|
for (const cls of classes) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
lang: 'js',
|
|
214
|
-
unique_id: cls.uniqueId,
|
|
215
|
-
});
|
|
216
|
-
db.prepare(insertEdgeTemplate).run({
|
|
217
|
-
source_type: 'file',
|
|
218
|
-
source_unique_id: normalizedPath,
|
|
219
|
-
target_type: 'class',
|
|
220
|
-
target_unique_id: cls.uniqueId,
|
|
221
|
-
relation: 'contains',
|
|
222
|
-
});
|
|
223
|
-
if (cls.superClass) {
|
|
224
|
-
db.prepare(insertEdgeTemplate).run({
|
|
225
|
-
source_type: 'class',
|
|
226
|
-
source_unique_id: cls.uniqueId,
|
|
227
|
-
target_type: `unresolved:${cls.superClass}`,
|
|
228
|
-
relation: 'inherits',
|
|
229
|
-
});
|
|
230
|
-
log(`🔗 Class ${cls.name} extends ${cls.superClass}`);
|
|
231
|
-
}
|
|
232
|
-
log(`🏷 Indexed JS class: ${cls.name}`);
|
|
233
|
-
}
|
|
234
|
-
catch (err) {
|
|
235
|
-
console.error('❌ Failed to insert class or edges', { cls, error: err });
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
// --- Imports / Exports edges ---
|
|
239
|
-
for (const imp of imports) {
|
|
240
|
-
db.prepare(insertEdgeTemplate).run({
|
|
241
|
-
source_type: 'file',
|
|
242
|
-
source_unique_id: normalizedPath,
|
|
243
|
-
target_type: 'file',
|
|
244
|
-
target_unique_id: `file@${imp}`,
|
|
245
|
-
relation: 'imports',
|
|
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,
|
|
246
215
|
});
|
|
247
|
-
}
|
|
248
|
-
for (const exp of exports) {
|
|
249
216
|
db.prepare(insertEdgeTemplate).run({
|
|
250
217
|
source_type: 'file',
|
|
251
218
|
source_unique_id: normalizedPath,
|
|
252
|
-
target_type: '
|
|
253
|
-
target_unique_id:
|
|
254
|
-
relation: '
|
|
219
|
+
target_type: 'class',
|
|
220
|
+
target_unique_id: cls.unique_id,
|
|
221
|
+
relation: 'contains',
|
|
255
222
|
});
|
|
223
|
+
if (cls.superClass) {
|
|
224
|
+
db.prepare(insertEdgeTemplate).run({
|
|
225
|
+
source_type: 'class',
|
|
226
|
+
source_unique_id: cls.unique_id,
|
|
227
|
+
target_type: `unresolved:${cls.superClass}`,
|
|
228
|
+
relation: 'inherits',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
256
231
|
}
|
|
257
|
-
|
|
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, '/');
|
|
237
|
+
db.prepare(insertEdgeTemplate).run({
|
|
238
|
+
source_type: 'file',
|
|
239
|
+
source_unique_id: normalizedPath,
|
|
240
|
+
target_type: 'file',
|
|
241
|
+
target_unique_id: normalizedTarget,
|
|
242
|
+
relation,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
handleEdges(imports, 'imports');
|
|
247
|
+
handleEdges(exports, 'exports');
|
|
258
248
|
db.prepare(markFileAsExtractedTemplate).run({ id: fileId });
|
|
259
|
-
log(`✅ Marked JS functions/classes as extracted for ${filePath}`);
|
|
260
249
|
return true;
|
|
261
250
|
}
|
|
262
251
|
catch (err) {
|
|
263
|
-
log(
|
|
264
|
-
log(` ↳ ${err.message}`);
|
|
252
|
+
console.log(chalk.yellow(`⚠️ [Extract] Failed for ${filePath}: ${err.message}`));
|
|
265
253
|
db.prepare(markFileAsFailedTemplate).run({ id: fileId });
|
|
266
254
|
return false;
|
|
267
255
|
}
|