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.
Files changed (95) hide show
  1. package/dist/agents/MainAgent.js +255 -0
  2. package/dist/agents/contextReviewStep.js +104 -0
  3. package/dist/agents/finalPlanGenStep.js +123 -0
  4. package/dist/agents/infoPlanGenStep.js +126 -0
  5. package/dist/agents/planGeneratorStep.js +118 -0
  6. package/dist/agents/planResolverStep.js +95 -0
  7. package/dist/agents/planTargetFilesStep.js +48 -0
  8. package/dist/agents/preFileSearchCheckStep.js +95 -0
  9. package/dist/agents/selectRelevantSourcesStep.js +100 -0
  10. package/dist/agents/semanticAnalysisStep.js +144 -0
  11. package/dist/agents/structuralAnalysisStep.js +46 -0
  12. package/dist/agents/transformPlanGenStep.js +107 -0
  13. package/dist/agents/understandIntentStep.js +72 -0
  14. package/dist/agents/validationAnalysisStep.js +87 -0
  15. package/dist/commands/AskCmd.js +47 -116
  16. package/dist/commands/ChangeLogUpdateCmd.js +11 -5
  17. package/dist/commands/CommitSuggesterCmd.js +50 -75
  18. package/dist/commands/DaemonCmd.js +119 -29
  19. package/dist/commands/IndexCmd.js +41 -24
  20. package/dist/commands/InspectCmd.js +0 -1
  21. package/dist/commands/ReadlineSingleton.js +18 -0
  22. package/dist/commands/ResetDbCmd.js +20 -21
  23. package/dist/commands/ReviewCmd.js +89 -54
  24. package/dist/commands/SummaryCmd.js +12 -18
  25. package/dist/commands/WorkflowCmd.js +41 -0
  26. package/dist/commands/factory.js +254 -0
  27. package/dist/config.js +67 -15
  28. package/dist/constants.js +20 -4
  29. package/dist/context.js +10 -11
  30. package/dist/daemon/daemonQueues.js +63 -0
  31. package/dist/daemon/daemonWorker.js +40 -63
  32. package/dist/daemon/generateSummaries.js +58 -0
  33. package/dist/daemon/runFolderCapsuleBatch.js +247 -0
  34. package/dist/daemon/runIndexingBatch.js +147 -0
  35. package/dist/daemon/runKgBatch.js +104 -0
  36. package/dist/db/fileIndex.js +168 -63
  37. package/dist/db/functionExtractors/extractFromJava.js +210 -6
  38. package/dist/db/functionExtractors/extractFromJs.js +173 -214
  39. package/dist/db/functionExtractors/extractFromTs.js +159 -160
  40. package/dist/db/functionExtractors/index.js +7 -5
  41. package/dist/db/schema.js +55 -20
  42. package/dist/db/sqlTemplates.js +50 -19
  43. package/dist/fileRules/builtins.js +31 -14
  44. package/dist/fileRules/codeAllowedExtensions.js +4 -0
  45. package/dist/fileRules/fileExceptions.js +0 -13
  46. package/dist/fileRules/ignoredExtensions.js +10 -0
  47. package/dist/index.js +128 -325
  48. package/dist/lib/generate.js +37 -14
  49. package/dist/lib/generateFolderCapsules.js +109 -0
  50. package/dist/lib/spinner.js +12 -5
  51. package/dist/modelSetup.js +0 -10
  52. package/dist/pipeline/modules/changeLogModule.js +16 -19
  53. package/dist/pipeline/modules/chunkManagerModule.js +24 -0
  54. package/dist/pipeline/modules/cleanupModule.js +96 -91
  55. package/dist/pipeline/modules/codeTransformModule.js +208 -0
  56. package/dist/pipeline/modules/commentModule.js +20 -11
  57. package/dist/pipeline/modules/commitSuggesterModule.js +36 -14
  58. package/dist/pipeline/modules/contextReviewModule.js +52 -0
  59. package/dist/pipeline/modules/fileReaderModule.js +72 -0
  60. package/dist/pipeline/modules/fileSearchModule.js +136 -0
  61. package/dist/pipeline/modules/finalAnswerModule.js +53 -0
  62. package/dist/pipeline/modules/gatherInfoModule.js +176 -0
  63. package/dist/pipeline/modules/generateTestsModule.js +63 -54
  64. package/dist/pipeline/modules/kgModule.js +26 -11
  65. package/dist/pipeline/modules/preserveCodeModule.js +91 -49
  66. package/dist/pipeline/modules/refactorModule.js +19 -7
  67. package/dist/pipeline/modules/repairTestsModule.js +44 -36
  68. package/dist/pipeline/modules/reviewModule.js +23 -13
  69. package/dist/pipeline/modules/summaryModule.js +27 -35
  70. package/dist/pipeline/modules/writeFileModule.js +86 -0
  71. package/dist/pipeline/registry/moduleRegistry.js +38 -93
  72. package/dist/pipeline/runModulePipeline.js +22 -19
  73. package/dist/scripts/dbcheck.js +143 -228
  74. package/dist/utils/buildContextualPrompt.js +245 -172
  75. package/dist/utils/debugContext.js +24 -0
  76. package/dist/utils/fileTree.js +16 -6
  77. package/dist/utils/loadRelevantFolderCapsules.js +64 -0
  78. package/dist/utils/log.js +2 -0
  79. package/dist/utils/normalizeData.js +23 -0
  80. package/dist/utils/planActions.js +60 -0
  81. package/dist/utils/promptBuilderHelper.js +67 -0
  82. package/dist/utils/promptLogHelper.js +52 -0
  83. package/dist/utils/sanitizeQuery.js +20 -8
  84. package/dist/utils/sleep.js +3 -0
  85. package/dist/utils/splitCodeIntoChunk.js +65 -32
  86. package/dist/utils/vscode.js +49 -0
  87. package/dist/workflow/workflowResolver.js +14 -0
  88. package/dist/workflow/workflowRunner.js +103 -0
  89. package/package.json +6 -5
  90. package/dist/agent/agentManager.js +0 -39
  91. package/dist/agent/workflowManager.js +0 -95
  92. package/dist/commands/ModulePipelineCmd.js +0 -31
  93. package/dist/daemon/daemonBatch.js +0 -186
  94. package/dist/fileRules/scoreFiles.js +0 -71
  95. 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
- function getFunctionName(node, parent, fileName) {
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.name;
16
+ return parent.id?.name;
16
17
  if (parent?.type === 'Property' && parent.key?.name)
17
- return parent.key.name;
18
+ return parent.key?.name;
18
19
  if (parent?.type === 'AssignmentExpression' && parent.left?.name)
19
- return parent.left.name;
20
+ return parent.left?.name;
20
21
  if (parent?.type === 'MethodDefinition' && parent.key?.name)
21
- return parent.key.name;
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
- // --- Traverse AST ---
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
- if (node.source?.value)
37
- imports.push(node.source.value);
38
- },
39
- ExportNamedDeclaration(node) {
40
- if (node.source?.value)
41
- exports.push(node.source.value);
42
- },
43
- ExportAllDeclaration(node) {
44
- if (node.source?.value)
45
- exports.push(node.source.value);
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
- db.prepare(markFileAsSkippedTemplate).run({ id: fileId });
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
- const insertTag = db.prepare(insertGraphTagTemplate);
129
- const getTagId = db.prepare(selectGraphTagIdTemplate);
130
- const insertEntityTag = db.prepare(insertGraphEntityTagTemplate);
131
- for (const entity of kgResult.entities) {
132
- if (!entity.type || !Array.isArray(entity.tags) || !entity.tags.length)
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
- for (const tag of entity.tags) {
135
- if (!tag || typeof tag !== 'string')
136
- continue;
137
- try {
138
- insertTag.run({ name: tag });
139
- const tagRow = getTagId.get({ name: tag });
140
- if (!tagRow)
141
- continue;
142
- const matchedUniqueId = functions.find(f => f.name === entity.name)?.unique_id ||
143
- classes.find(c => c.name === entity.name)?.unique_id ||
144
- `${entity.name}@${filePath}`;
145
- insertEntityTag.run({
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
- log(`🏷 Persisted LLM-generated tags for ${filePath}`);
157
- }
137
+ };
138
+ kgResult.entities.forEach(persistEntityTags);
158
139
  }
159
140
  catch (kgErr) {
160
- console.warn(`⚠️ KG tagging failed for ${filePath}:`, kgErr instanceof Error ? kgErr.message : kgErr);
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
- try {
166
- const embedding = await generateEmbedding(fn.content);
167
- db.prepare(insertFunctionTemplate).run({
168
- file_id: fileId,
169
- name: fn.name,
170
- start_line: fn.start_line,
171
- end_line: fn.end_line,
172
- content: fn.content,
173
- embedding: JSON.stringify(embedding),
174
- lang: 'js',
175
- unique_id: fn.unique_id,
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
- // File -> Function 'contains' edge
178
- const containsEdgeKey = `file->${fn.unique_id}`;
179
- if (!seenEdges.has(containsEdgeKey)) {
180
- db.prepare(insertEdgeTemplate).run({
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
- const calleeName = node.callee?.name;
194
- if (!calleeName || BUILTINS.has(calleeName))
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 edgeKey = `${fn.unique_id}->${targetUniqueId}`;
198
- if (!seenEdges.has(edgeKey)) {
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(edgeKey);
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 (err) {
218
- console.error('❌ Failed to insert function or call edges', { fn, error: err });
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 + edges ---
204
+ // --- Insert classes ---
222
205
  for (const cls of classes) {
223
- try {
224
- const embedding = await generateEmbedding(cls.content);
225
- db.prepare(insertGraphClassTemplate).run({
226
- file_id: fileId,
227
- name: cls.name,
228
- start_line: cls.start_line,
229
- end_line: cls.end_line,
230
- content: cls.content,
231
- embedding: JSON.stringify(embedding),
232
- lang: 'js',
233
- unique_id: cls.unique_id,
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: 'file',
237
- source_unique_id: normalizedPath,
238
- target_type: 'class',
239
- target_unique_id: cls.unique_id,
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 / Exports edges ---
258
- for (const imp of imports) {
259
- try {
260
- db.prepare(insertEdgeTemplate).run({
261
- source_type: 'file',
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: `file@${exp}`,
279
- relation: 'exports',
241
+ target_unique_id: normalizedTarget,
242
+ relation,
280
243
  });
281
244
  }
282
- catch (err) {
283
- console.error('❌ Failed to persist export edge', { exp, error: err });
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(`❌ Failed to extract from JS file: ${filePath}`);
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
  }