scai 0.1.157 → 0.1.158
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.
|
@@ -5,16 +5,28 @@ import { generate } from "../../lib/generate.js";
|
|
|
5
5
|
const MAX_CODE_CHARS = 6000; // conservative prompt-safe limit
|
|
6
6
|
export const semanticAnalysisModule = {
|
|
7
7
|
name: "semanticAnalysis",
|
|
8
|
-
description: "Performs semantic analysis for
|
|
8
|
+
description: "Performs semantic analysis for a single target file defined by the current execution step.",
|
|
9
9
|
groups: ["analysis"],
|
|
10
10
|
run: async (input) => {
|
|
11
11
|
var _a, _b;
|
|
12
12
|
const context = input.context;
|
|
13
13
|
if (!context)
|
|
14
14
|
throw new Error("[semanticAnalysisStep] No context provided");
|
|
15
|
+
const step = context.currentStep;
|
|
16
|
+
if (!step || step.action !== "semanticAnalysis") {
|
|
17
|
+
const notes = "[semanticAnalysisStep] Invoked without semanticAnalysis step";
|
|
18
|
+
logInputOutput("semanticAnalysisStep", "output", { notes });
|
|
19
|
+
return { query: input.query, data: { notes }, context };
|
|
20
|
+
}
|
|
21
|
+
if (!step.targetFile) {
|
|
22
|
+
const notes = "[semanticAnalysisStep] semanticAnalysis step missing targetFile";
|
|
23
|
+
logInputOutput("semanticAnalysisStep", "output", { notes });
|
|
24
|
+
return { query: input.query, data: { notes }, context };
|
|
25
|
+
}
|
|
15
26
|
const workingFiles = context.workingFiles ?? [];
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
const file = workingFiles.find(f => f.path === step.targetFile);
|
|
28
|
+
if (!file) {
|
|
29
|
+
const notes = `[semanticAnalysisStep] targetFile not found: ${step.targetFile}`;
|
|
18
30
|
logInputOutput("semanticAnalysisStep", "output", { notes });
|
|
19
31
|
return { query: input.query, data: { notes }, context };
|
|
20
32
|
}
|
|
@@ -22,56 +34,37 @@ export const semanticAnalysisModule = {
|
|
|
22
34
|
(_a = context.analysis).fileAnalysis || (_a.fileAnalysis = {});
|
|
23
35
|
(_b = context.analysis).combinedAnalysis || (_b.combinedAnalysis = {});
|
|
24
36
|
const intentCategory = context.analysis.intent?.intentCategory ?? "other";
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const semanticAnalysis = await analyzeFile(file, input.query, context, isRelevant, shouldModify);
|
|
45
|
-
context.analysis.fileAnalysis[filePath] = {
|
|
46
|
-
...prevAnalysis, // keep evidence and prior info
|
|
47
|
-
...semanticAnalysis, // add semantic insights
|
|
48
|
-
action: { isRelevant, shouldModify },
|
|
49
|
-
};
|
|
50
|
-
logInputOutput("semanticAnalysisStep - per-file", "output", {
|
|
51
|
-
file: filePath,
|
|
52
|
-
analysis: context.analysis.fileAnalysis[filePath],
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
// ----------------------------
|
|
56
|
-
// 2️⃣ Cross-file combined analysis (optional)
|
|
57
|
-
// Only consider relevant files
|
|
58
|
-
// ----------------------------
|
|
37
|
+
const filePath = file.path;
|
|
38
|
+
const prevAnalysis = context.analysis.fileAnalysis[filePath];
|
|
39
|
+
// Default: allow semantic analysis to determine relevance
|
|
40
|
+
const isRelevant = prevAnalysis?.action?.isRelevant ?? true;
|
|
41
|
+
const shouldModify = prevAnalysis?.action?.shouldModify ??
|
|
42
|
+
determineShouldModify(intentCategory, isRelevant);
|
|
43
|
+
const semanticAnalysis = await analyzeFile(file, input.query, context, isRelevant, shouldModify);
|
|
44
|
+
context.analysis.fileAnalysis[filePath] = {
|
|
45
|
+
...prevAnalysis,
|
|
46
|
+
...semanticAnalysis,
|
|
47
|
+
action: { isRelevant, shouldModify },
|
|
48
|
+
};
|
|
49
|
+
logInputOutput("semanticAnalysisStep - per-file", "output", {
|
|
50
|
+
file: filePath,
|
|
51
|
+
analysis: context.analysis.fileAnalysis[filePath],
|
|
52
|
+
});
|
|
53
|
+
// -------------------------------------------------
|
|
54
|
+
// ⚠️ Transitional: opportunistic combined analysis
|
|
55
|
+
// -------------------------------------------------
|
|
59
56
|
const relevantFiles = Object.entries(context.analysis.fileAnalysis)
|
|
60
57
|
.filter(([_, analysis]) => analysis.action?.isRelevant)
|
|
61
58
|
.map(([path]) => path);
|
|
62
|
-
let combinedAnalysis = {
|
|
63
|
-
sharedPatterns: [],
|
|
64
|
-
architectureSummary: "[skipped]",
|
|
65
|
-
hotspots: [],
|
|
66
|
-
};
|
|
67
59
|
if (relevantFiles.length > 2) {
|
|
68
|
-
const
|
|
69
|
-
for (const path of relevantFiles)
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
const filtered = {};
|
|
61
|
+
for (const path of relevantFiles) {
|
|
62
|
+
filtered[path] = context.analysis.fileAnalysis[path];
|
|
63
|
+
}
|
|
64
|
+
const combinedAnalysis = await analyzeCombined(filtered, input.query);
|
|
65
|
+
context.analysis.combinedAnalysis = combinedAnalysis;
|
|
72
66
|
logInputOutput("semanticAnalysisStep - combined", "output", combinedAnalysis);
|
|
73
67
|
}
|
|
74
|
-
context.analysis.combinedAnalysis = combinedAnalysis;
|
|
75
68
|
return {
|
|
76
69
|
query: input.query,
|
|
77
70
|
data: { notes: "Semantic analysis completed" },
|
|
@@ -104,9 +97,9 @@ ${contextSnippet}
|
|
|
104
97
|
File path: ${file.path}
|
|
105
98
|
|
|
106
99
|
Task:
|
|
107
|
-
- Analyze this file thoroughly based on
|
|
108
|
-
- Identify key functions, classes, data structures, and
|
|
109
|
-
- Provide semantic insights
|
|
100
|
+
- Analyze this file thoroughly based on its role in the system.
|
|
101
|
+
- Identify key functions, classes, data structures, and responsibilities.
|
|
102
|
+
- Provide semantic insights useful for future reasoning or transformations.
|
|
110
103
|
|
|
111
104
|
Code excerpt:
|
|
112
105
|
${slicedCode ?? "[no code]"}
|
|
@@ -141,15 +134,18 @@ Return STRICT JSON:
|
|
|
141
134
|
data = {};
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
|
-
const intent = data.intent === "relevant" || data.intent === "irrelevant"
|
|
137
|
+
const intent = data.intent === "relevant" || data.intent === "irrelevant"
|
|
138
|
+
? data.intent
|
|
139
|
+
: "irrelevant";
|
|
145
140
|
return {
|
|
146
141
|
intent,
|
|
147
142
|
relevance: typeof data.relevance === "string" && data.relevance.trim()
|
|
148
143
|
? data.relevance
|
|
149
144
|
: intent === "relevant"
|
|
150
145
|
? "This file appears relevant to the query."
|
|
151
|
-
: "This file does not appear relevant to
|
|
152
|
-
role: intent === "relevant" &&
|
|
146
|
+
: "This file does not appear relevant to the query.",
|
|
147
|
+
role: intent === "relevant" &&
|
|
148
|
+
["primary", "supporting", "contextual"].includes(data.role)
|
|
153
149
|
? data.role
|
|
154
150
|
: undefined,
|
|
155
151
|
action: { shouldModify },
|
|
@@ -157,8 +153,12 @@ Return STRICT JSON:
|
|
|
157
153
|
? {
|
|
158
154
|
summary: String(data.proposedChanges.summary ?? ""),
|
|
159
155
|
scope: data.proposedChanges.scope ?? "none",
|
|
160
|
-
targets: Array.isArray(data.proposedChanges.targets)
|
|
161
|
-
|
|
156
|
+
targets: Array.isArray(data.proposedChanges.targets)
|
|
157
|
+
? data.proposedChanges.targets
|
|
158
|
+
: undefined,
|
|
159
|
+
rationale: typeof data.proposedChanges.rationale === "string"
|
|
160
|
+
? data.proposedChanges.rationale
|
|
161
|
+
: undefined,
|
|
162
162
|
}
|
|
163
163
|
: { summary: "No changes required for this file.", scope: "none" },
|
|
164
164
|
risks: Array.isArray(data.risks) ? data.risks : [],
|
|
@@ -214,7 +214,9 @@ Return STRICT JSON:
|
|
|
214
214
|
try {
|
|
215
215
|
const ai = await generate({ query: "cross-file analysis", content: prompt });
|
|
216
216
|
const cleaned = await cleanupModule.run({ query, content: ai.data });
|
|
217
|
-
const data = typeof cleaned.data === "object" && cleaned.data
|
|
217
|
+
const data = typeof cleaned.data === "object" && cleaned.data
|
|
218
|
+
? cleaned.data
|
|
219
|
+
: JSON.parse(String(cleaned.content ?? "{}"));
|
|
218
220
|
return {
|
|
219
221
|
sharedPatterns: data.sharedPatterns ?? [],
|
|
220
222
|
architectureSummary: data.architectureSummary ?? "[unparsed]",
|