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.
Files changed (96) 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 +186 -198
  39. package/dist/db/functionExtractors/extractFromTs.js +181 -192
  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 -0
  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 +35 -6
  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 +156 -91
  74. package/dist/utils/buildContextualPrompt.js +245 -164
  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/sharedUtils.js +8 -0
  85. package/dist/utils/sleep.js +3 -0
  86. package/dist/utils/splitCodeIntoChunk.js +65 -32
  87. package/dist/utils/vscode.js +49 -0
  88. package/dist/workflow/workflowResolver.js +14 -0
  89. package/dist/workflow/workflowRunner.js +103 -0
  90. package/package.json +6 -5
  91. package/dist/agent/agentManager.js +0 -39
  92. package/dist/agent/workflowManager.js +0 -95
  93. package/dist/commands/ModulePipelineCmd.js +0 -31
  94. package/dist/daemon/daemonBatch.js +0 -186
  95. package/dist/fileRules/scoreFiles.js +0 -71
  96. package/dist/lib/generateEmbedding.js +0 -22
@@ -0,0 +1,255 @@
1
+ import { builtInModules } from "../pipeline/registry/moduleRegistry.js";
2
+ import { logInputOutput } from "../utils/promptLogHelper.js";
3
+ import { planResolverStep } from "./planResolverStep.js";
4
+ import { infoPlanGen } from "./infoPlanGenStep.js";
5
+ import { understandIntentStep } from "./understandIntentStep.js";
6
+ import { structuralAnalysisStep } from "./structuralAnalysisStep.js";
7
+ import { contextReviewStep } from "./contextReviewStep.js";
8
+ import { planTargetFilesStep } from "./planTargetFilesStep.js";
9
+ import { validationAnalysisStep } from "./validationAnalysisStep.js";
10
+ import { preFileSearchCheckStep } from "./preFileSearchCheckStep.js";
11
+ import { semanticAnalysisStep } from "./semanticAnalysisStep.js";
12
+ import { selectRelevantSourcesStep } from "./selectRelevantSourcesStep.js";
13
+ import { transformPlanGenStep } from "./transformPlanGenStep.js";
14
+ import { finalPlanGenStep } from "./finalPlanGenStep.js";
15
+ /** Build a registry of all built-in modules */
16
+ const MODULE_REGISTRY = Object.fromEntries(Object.entries(builtInModules).map(([name, mod]) => [name, mod]));
17
+ /** Resolve a module from the registry */
18
+ function resolveModuleForAction(action) {
19
+ const mod = MODULE_REGISTRY[action];
20
+ if (!mod)
21
+ console.warn(`⚠️ Missing module for action "${action}" — skipping`);
22
+ return mod;
23
+ }
24
+ export class MainAgent {
25
+ constructor(context) {
26
+ this.runCount = 0;
27
+ this.maxRuns = 2;
28
+ this.context = context;
29
+ this.query = context.initContext?.userQuery ?? '';
30
+ }
31
+ /** Generic step executor */
32
+ async executeStep(step, input) {
33
+ console.log(`\n`);
34
+ console.log(`\n =====================================================================================`);
35
+ console.log(`\n⚡ Executing step: ${step.action}`);
36
+ if (step.description)
37
+ console.log(`\n --> Description: ${step.description}`);
38
+ console.log(`\n =====================================================================================`);
39
+ // attach the current step to the context
40
+ if (input.context) {
41
+ input.context.currentStep = step; // <-- new property
42
+ }
43
+ const mod = resolveModuleForAction(step.action);
44
+ if (!mod) {
45
+ return { query: input.query, content: input.content, data: { skipped: true }, context: input.context };
46
+ }
47
+ const moduleInput = {
48
+ query: step.description ?? input.query,
49
+ content: input.data ?? input.content,
50
+ context: input.context // shared reference
51
+ };
52
+ try {
53
+ console.log(` ▶ Running module: ${mod.name}`);
54
+ const output = await mod.run(moduleInput);
55
+ if (!output)
56
+ throw new Error(`Module '${mod.name}' returned empty output`);
57
+ // Return only what matters — context is always the same reference
58
+ return {
59
+ query: step.description ?? input.query,
60
+ data: output.data,
61
+ context: input.context
62
+ };
63
+ }
64
+ catch (err) {
65
+ console.error(` ❌ Module "${mod?.name}" failed:`, err);
66
+ return {
67
+ query: input.query,
68
+ content: input.content,
69
+ data: { error: String(err) },
70
+ context: input.context
71
+ };
72
+ }
73
+ }
74
+ /** Main run sequence */
75
+ async run() {
76
+ var _a;
77
+ this.runCount++;
78
+ console.log(`\n =====================================================================================`);
79
+ console.log(`🚀 ================= [AutonomousAgent] Starting run #${this.runCount} =================`);
80
+ logInputOutput("GlobalContext (structured)", "input", this.context);
81
+ // ---------------------------------------------------------------------
82
+ // GENERATE FOLDER-CAPSULE SUMMARIES
83
+ // ---------------------------------------------------------------------
84
+ if (this.context.initContext?.folderCapsules?.length) {
85
+ const capsulesSummary = this.context.initContext.folderCapsules.map(fc => ({
86
+ path: fc.path,
87
+ fileCount: fc.stats?.fileCount ?? 0,
88
+ depth: fc.depth ?? 0,
89
+ confidence: fc.confidence ?? 0,
90
+ roles: fc.roles ?? [],
91
+ concerns: fc.concerns ?? []
92
+ }));
93
+ // Add a human-readable summary for the LLM
94
+ (_a = this.context).analysis ?? (_a.analysis = {});
95
+ this.context.analysis.folderCapsulesSummary = capsulesSummary;
96
+ const humanReadable = capsulesSummary.map(fc => `- ${fc.path}: ${fc.fileCount} files, depth ${fc.depth}, confidence ${fc.confidence}`).join('\n');
97
+ this.context.analysis.folderCapsulesHuman = humanReadable;
98
+ console.log('\n📂 Folder Capsules Summary (for agent steps):\n', humanReadable);
99
+ }
100
+ // ---------------------------------------------------------------------
101
+ // 0️⃣ UNDERSTAND INTENT — First-pass interpretation of query
102
+ // ---------------------------------------------------------------------
103
+ console.log("\n🧠 Running understandIntentStep (pre-analysis fixture)\n");
104
+ await understandIntentStep.run({
105
+ context: this.context
106
+ });
107
+ // ---------------------------------------------------------------------
108
+ // 1️⃣ PLAN RESOLVER — Fast path (context-driven routing)
109
+ // ---------------------------------------------------------------------
110
+ await planResolverStep.run(this.context);
111
+ // --------------------------------------------------
112
+ // 🚦 ROUTING DECISION
113
+ // --------------------------------------------------
114
+ const routing = this.context.analysis?.routingDecision;
115
+ // ✅ FAST PATH: if planResolver thinks we have a final answer, wrap it in ModuleIO and return
116
+ if (routing?.decision === 'final-answer' && routing.answer) {
117
+ console.log("⚡ Fast path hit — returning final answer directly");
118
+ return {
119
+ query: this.query,
120
+ data: {
121
+ finalAnswer: routing.answer,
122
+ source: "planResolver",
123
+ },
124
+ context: this.context
125
+ };
126
+ }
127
+ // --------------------------------------------------
128
+ // ⏩ FALL THROUGH: continue with plan generation
129
+ // --------------------------------------------------
130
+ let textPlan;
131
+ // Use rationale from routing if available (temporary compatibility)
132
+ if (typeof routing?.rationale === 'string' && routing.rationale.trim()) {
133
+ textPlan = routing.rationale;
134
+ }
135
+ // Fallback: use the original query if no plan/rationale is available
136
+ if (!textPlan || textPlan.trim().length === 0) {
137
+ textPlan = this.query;
138
+ }
139
+ // textPlan can now be passed to planGeneratorStep
140
+ // ---------------------------------------------------------------------
141
+ // PRE-FILE-SEARCH CHECK (do we have the files required to continue )
142
+ // ---------------------------------------------------------------------
143
+ await preFileSearchCheckStep(this.context);
144
+ // ---------------------------------------------------------------------
145
+ // 2️⃣ INFO PLAN GENERATION (information-only steps)
146
+ // ---------------------------------------------------------------------
147
+ await infoPlanGen.run(this.context);
148
+ const infoPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
149
+ const infoSteps = infoPlan.steps.filter(s => s.groups?.includes("info"));
150
+ console.log("information gathering steps:\n");
151
+ console.dir(infoSteps);
152
+ let stepIO = {
153
+ query: this.query,
154
+ context: this.context
155
+ };
156
+ // =====================================================
157
+ // INFORMATION ACQUISITION PHASE
158
+ // Purpose: gather raw information, no interpretation
159
+ // =====================================================
160
+ //let count = 0;
161
+ for (const step of infoSteps) {
162
+ //count++;
163
+ stepIO = await this.executeStep(step, stepIO);
164
+ /* if (count === 4) {
165
+ debugContext(this.context, {
166
+ step: "first step",
167
+ note: "searchFiles?"
168
+ });
169
+ } */
170
+ }
171
+ // =====================================================
172
+ // ANALYSIS PHASE
173
+ // Purpose: understand what we have and what is being asked
174
+ // =====================================================
175
+ // 🎯 RELEVANT SOURCE SELECTION (authoritative narrowing)
176
+ console.log("\n📁 Selecting relevant source files\n");
177
+ await selectRelevantSourcesStep.run({ query: this.query, context: this.context });
178
+ // ANALYSIS PHASE (deterministic, durable)
179
+ console.log("\n🏗️ Running structural analysis\n");
180
+ await structuralAnalysisStep.run({ query: this.query, context: this.context });
181
+ // SEMANTIC ANALYSIS PHASE (LLM-driven)
182
+ console.log("\n🧠 Running semantic analysis\n");
183
+ await semanticAnalysisStep.run({ query: this.query, context: this.context });
184
+ // SEMANTIC PLAN TARGET FILES (moves focus -> plan.targetFiles)
185
+ console.log("\n🧠 Running plan target files\n");
186
+ await planTargetFilesStep.run({ query: this.query, context: this.context });
187
+ /* debugContext(this.context, {
188
+ step: "Semantic analysis step",
189
+ note: "semantic meaning?"
190
+ }); */
191
+ // VALIDATION PHASE
192
+ console.log("\n🧠 Running validation analysis\n");
193
+ await validationAnalysisStep.run({ query: this.query, context: this.context });
194
+ // =====================================================
195
+ // ROUTING / PLANNING PHASE
196
+ // =====================================================
197
+ const review = await contextReviewStep(this.context);
198
+ if (review.decision === "loopAgain" && this.runCount < this.maxRuns) {
199
+ console.log("🔄 Looping for additional context");
200
+ // Clear noisy bootstrap artifacts before looping
201
+ this.resetInitContextForLoop();
202
+ return this.run();
203
+ }
204
+ // =====================================================
205
+ // TRANSFORM PLAN GENERATION (after context review)
206
+ // Purpose: produce actionable transform steps
207
+ // =====================================================
208
+ console.log("\n🛠️ Generating transform plan\n");
209
+ await transformPlanGenStep.run(this.context);
210
+ // Filter transform steps
211
+ const transformPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
212
+ const transformSteps = transformPlan.steps.filter(s => s.groups?.includes("transform"));
213
+ console.log("transform steps:\n");
214
+ console.dir(transformSteps);
215
+ // =====================================================
216
+ // TRANSFORM PHASE
217
+ // Purpose: produce concrete changes or artifacts
218
+ // =====================================================
219
+ console.log("\n⚡ Running transform steps\n");
220
+ for (const step of transformSteps) {
221
+ stepIO = await this.executeStep(step, stepIO);
222
+ }
223
+ // =====================================================
224
+ // FINAL PLAN GENERATION
225
+ // Purpose: produce finalize / commit steps
226
+ // =====================================================
227
+ console.log("\n✅ Generating final plan\n");
228
+ await finalPlanGenStep.run(this.context);
229
+ // Filter finalize steps
230
+ const finalPlan = this.context?.analysis?.planSuggestion?.plan ?? { steps: [] };
231
+ const finalizeSteps = finalPlan.steps.filter(s => s.groups?.includes("finalize"));
232
+ console.log("finalize steps:\n");
233
+ console.dir(finalizeSteps);
234
+ // =====================================================
235
+ // FINALIZE PHASE
236
+ // Purpose: commit results and respond to the user
237
+ // =====================================================
238
+ for (const step of finalizeSteps) {
239
+ stepIO = await this.executeStep(step, stepIO);
240
+ }
241
+ console.log("🏁 Agent completed.");
242
+ console.log("---------------------------------------------\n");
243
+ console.log("Show final context\n", this.context);
244
+ return stepIO;
245
+ }
246
+ // =====================================================
247
+ // Helper: reset initContext for clean loop
248
+ // =====================================================
249
+ resetInitContextForLoop() {
250
+ if (this.context.initContext) {
251
+ this.context.initContext.relatedFiles = [];
252
+ console.log(" 🧹 Cleared initContext.relatedFiles for clean loop");
253
+ }
254
+ }
255
+ }
@@ -0,0 +1,104 @@
1
+ // File: src/modules/contextReviewStep.ts
2
+ import { generate } from "../lib/generate.js";
3
+ import { logInputOutput } from "../utils/promptLogHelper.js";
4
+ export async function contextReviewStep(context) {
5
+ if (!context.plan?.targetFiles || context.plan.targetFiles.length === 0) {
6
+ throw new Error("[contextReviewStep] No targetFiles defined in plan.");
7
+ }
8
+ // ------------------------------
9
+ // Authoritative view: only target files
10
+ // ------------------------------
11
+ const authoritativeFiles = (context.workingFiles ?? [])
12
+ .filter(f => context.plan.targetFiles.includes(f.path))
13
+ .map((f) => ({
14
+ path: f.path,
15
+ hasCode: Boolean(f.code),
16
+ code: f.code ?? null,
17
+ summary: f.summary ?? null,
18
+ }));
19
+ // ------------------------------
20
+ // Structural verification: only target files
21
+ // ------------------------------
22
+ const structuralProof = context.analysis?.structure
23
+ ? {
24
+ fileCount: context.analysis.structure.files.filter(f => context.plan.targetFiles.includes(f.path)).length,
25
+ files: context.analysis.structure.files
26
+ .filter(f => context.plan.targetFiles.includes(f.path))
27
+ .map(f => ({
28
+ path: f.path,
29
+ hasCode: Boolean(authoritativeFiles.find(af => af.path === f.path)?.code),
30
+ })),
31
+ }
32
+ : null;
33
+ // ------------------------------
34
+ // Include combined architecture analysis
35
+ // ------------------------------
36
+ const combinedAnalysis = context.analysis?.combinedAnalysis ?? {};
37
+ const architectureSummary = combinedAnalysis.architectureSummary ?? "";
38
+ const hotspots = combinedAnalysis.hotspots ?? [];
39
+ // ------------------------------
40
+ // Build prompt
41
+ // ------------------------------
42
+ const prompt = `
43
+ You are a meta-reasoning agent responsible for determining whether the agent
44
+ has sufficient information to proceed.
45
+
46
+ IMPORTANT CONTRACT:
47
+ - Any file listed below with a non-null "code" field MUST be treated as the
48
+ complete and authoritative source code for that file.
49
+ - Do NOT request file contents again if "code" is present.
50
+
51
+ User intent:
52
+ ${JSON.stringify(context.analysis?.intent ?? {}, null, 2)}
53
+
54
+ Authoritative source files:
55
+ ${JSON.stringify(authoritativeFiles, null, 2)}
56
+
57
+ Structural verification:
58
+ ${JSON.stringify(structuralProof, null, 2)}
59
+
60
+ Combined architecture analysis:
61
+ {
62
+ "architectureSummary": ${JSON.stringify(architectureSummary, null, 2)}
63
+ }
64
+
65
+ Your task:
66
+ 1. Determine whether the available information is sufficient to fulfill the user intent.
67
+ 2. If NOT sufficient, explicitly list what is missing.
68
+ 3. Output STRICT JSON with the following shape:
69
+
70
+ {
71
+ "decision": "loopAgain" | "stop",
72
+ "reason": "string",
73
+ "missing": string[]
74
+ }
75
+
76
+ Rules:
77
+ - If the intent involves modifying, commenting, refactoring, or analyzing code,
78
+ and at least one relevant file includes full source code, the context SHOULD
79
+ be considered sufficient.
80
+ - Do NOT request information that is already present in the authoritative files.
81
+ `.trim();
82
+ // ------------------------------
83
+ // Call LLM
84
+ // ------------------------------
85
+ const ai = await generate({
86
+ query: context.initContext?.userQuery ?? '',
87
+ content: prompt,
88
+ });
89
+ const text = typeof ai.data === "string" ? ai.data : JSON.stringify(ai.data);
90
+ logInputOutput("contextReviewHelper", "output", text);
91
+ // ------------------------------
92
+ // Parse JSON or fallback
93
+ // ------------------------------
94
+ try {
95
+ return JSON.parse(text);
96
+ }
97
+ catch {
98
+ return {
99
+ decision: text.toLowerCase().includes("loop") ? "loopAgain" : "stop",
100
+ reason: text,
101
+ missing: [],
102
+ };
103
+ }
104
+ }
@@ -0,0 +1,123 @@
1
+ // File: src/agents/finalPlanGenStep.ts
2
+ import { generate } from '../lib/generate.js';
3
+ import { PLAN_ACTIONS } from '../utils/planActions.js';
4
+ import { logInputOutput } from '../utils/promptLogHelper.js';
5
+ import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
6
+ const MAX_STEPS = 100;
7
+ /**
8
+ * FINAL PLAN GENERATOR
9
+ * Generates steps that finalize results, commit changes, or prepare outputs.
10
+ */
11
+ export const finalPlanGenStep = {
12
+ name: 'finalPlanGen',
13
+ description: 'Generates finalization steps to wrap up and commit results.',
14
+ requires: ['analysis.intent', 'analysis.focus'],
15
+ produces: ['analysis.planSuggestion'],
16
+ async run(context) {
17
+ var _a, _b;
18
+ context.analysis || (context.analysis = {});
19
+ (_a = context.analysis).planSuggestion || (_a.planSuggestion = {});
20
+ (_b = context.analysis.planSuggestion).plan || (_b.plan = { steps: [] });
21
+ // Restrict actions to FINALIZE group only
22
+ const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('finalize'));
23
+ const actionsJson = JSON.stringify(effectiveActions, null, 2);
24
+ const intentText = context.analysis.intent?.normalizedQuery ?? '';
25
+ const intentCategory = context.analysis.intent?.intentCategory ?? '';
26
+ const prompt = `
27
+ You are an autonomous coding agent.
28
+ Your task is to produce a structured plan describing finalization steps
29
+ to wrap up work, commit changes, or produce outputs safely.
30
+
31
+ Intent / task description:
32
+ ${intentText}
33
+
34
+ If the intent indicates that no finalization is required, return an empty plan object with an empty "steps" array:
35
+ { "steps": [] }
36
+
37
+ Allowed actions (finalize only):
38
+ ${actionsJson}
39
+
40
+ Task category:
41
+ ${intentCategory}
42
+
43
+ Folder structure:
44
+ ${context.analysis.folderCapsulesHuman ?? ''}
45
+
46
+ Existing relevant files:
47
+ ${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)}
48
+
49
+ ⚡ Phase guidance:
50
+ - Actions are grouped into phases: info, transform, finalize.
51
+ - Only include finalize steps in this phase.
52
+ - Each step must include: "action", "targetFile" (optional), "description", "metadata"
53
+
54
+ Return a strictly valid JSON plan:
55
+
56
+ {
57
+ "steps": [
58
+ { "action": "stepName", "targetFile": "optional/path.ts", "description": "explanation", "metadata": {} }
59
+ ]
60
+ }
61
+ `.trim();
62
+ try {
63
+ console.log('FinalPlanGenStep prompt', prompt);
64
+ const genInput = { query: intentText, content: prompt };
65
+ const genOutput = await generate(genInput);
66
+ const raw = typeof genOutput.data === 'string'
67
+ ? genOutput.data
68
+ : JSON.stringify(genOutput.data ?? '{}');
69
+ const cleaned = await cleanupModule.run({ query: intentText, content: raw });
70
+ const jsonString = typeof cleaned.content === 'string'
71
+ ? cleaned.content
72
+ : JSON.stringify(cleaned.content ?? '{}');
73
+ let plan = JSON.parse(jsonString);
74
+ if (!plan || !Array.isArray(plan.steps))
75
+ throw new Error('Invalid final plan structure');
76
+ if (plan.steps.length > MAX_STEPS)
77
+ plan.steps = plan.steps.slice(0, MAX_STEPS);
78
+ // Map groups & metadata
79
+ plan.steps = plan.steps.map(step => {
80
+ const actionDef = PLAN_ACTIONS.find(a => a.action === step.action);
81
+ return {
82
+ ...step,
83
+ metadata: {
84
+ ...step.metadata,
85
+ routingConfidence: context.analysis?.routingDecision?.confidence ?? 0
86
+ },
87
+ groups: actionDef?.groups ?? ['finalize']
88
+ };
89
+ });
90
+ // Ensure at least one summary step if plan is empty
91
+ if (plan.steps.length === 0) {
92
+ plan.steps.push({
93
+ action: 'finalizeSummary',
94
+ description: 'Summarize what actions were taken and provide a response to the user.',
95
+ metadata: {
96
+ routingConfidence: context.analysis?.routingDecision?.confidence ?? 0
97
+ },
98
+ groups: ['finalize']
99
+ });
100
+ }
101
+ // Replace existing finalize steps in planSuggestion
102
+ context.analysis.planSuggestion.plan.steps = [
103
+ ...context.analysis.planSuggestion.plan.steps.filter(s => !s.groups?.includes('finalize')),
104
+ ...plan.steps
105
+ ];
106
+ logInputOutput('finalPlanGen', 'output', plan);
107
+ }
108
+ catch (err) {
109
+ console.warn('⚠️ Failed to generate final plan:', err);
110
+ context.analysis.planSuggestion.plan.steps = [
111
+ {
112
+ action: 'finalizeSummary',
113
+ description: 'Summarize what actions were taken and provide a response to the user.',
114
+ metadata: {
115
+ routingConfidence: context.analysis?.routingDecision?.confidence ?? 0
116
+ },
117
+ groups: ['finalize']
118
+ }
119
+ ];
120
+ logInputOutput('finalPlanGen', 'output', context.analysis.planSuggestion.plan);
121
+ }
122
+ }
123
+ };
@@ -0,0 +1,126 @@
1
+ import { generate } from '../lib/generate.js';
2
+ import { PLAN_ACTIONS } from '../utils/planActions.js';
3
+ import { logInputOutput } from '../utils/promptLogHelper.js';
4
+ import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
5
+ const MAX_STEPS = 100;
6
+ /**
7
+ * INFO PLAN GENERATOR
8
+ * Generates information-gathering steps only.
9
+ */
10
+ export const infoPlanGen = {
11
+ name: 'infoPlanGen',
12
+ description: 'Generates an information-acquisition plan.',
13
+ requires: ['userQuery', 'analysis.intent'],
14
+ produces: ['analysis.planSuggestion'],
15
+ async run(context) {
16
+ context.analysis || (context.analysis = {});
17
+ // --------------------------------------------------
18
+ // Always discard any existing info plan
19
+ // (planner may loop; old plans must not survive)
20
+ // --------------------------------------------------
21
+ delete context.analysis.planSuggestion?.plan;
22
+ // --------------------------------------------------
23
+ // Check for missing files
24
+ // --------------------------------------------------
25
+ const missingFiles = Array.isArray(context.analysis.focus?.missingFiles)
26
+ ? context.analysis.focus.missingFiles
27
+ : [];
28
+ if (missingFiles.length === 0) {
29
+ console.log('ℹ️ No missing files — returning empty info plan.');
30
+ context.analysis.planSuggestion = { plan: { steps: [] } };
31
+ logInputOutput('infoPlanGen', 'output', { steps: [] });
32
+ return;
33
+ }
34
+ // --------------------------------------------------
35
+ // Restrict actions to INFO phase only
36
+ // --------------------------------------------------
37
+ const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('info'));
38
+ const actionsJson = JSON.stringify(effectiveActions, null, 2);
39
+ console.log('Actions: ', actionsJson);
40
+ const intentText = context.analysis.intent?.normalizedQuery ??
41
+ context.initContext?.userQuery ??
42
+ '';
43
+ const intentCategory = context.analysis.intent?.intentCategory ?? '';
44
+ const prompt = `
45
+ You are an autonomous coding agent.
46
+ Based on the allowed actions you task is to produce a structured plan describing what information should be gathered
47
+ in order to understand and solve the task.
48
+
49
+ Allowed actions (information acquisition only):
50
+ ${actionsJson}
51
+
52
+ Intent / task description:
53
+ ${intentText}
54
+
55
+ Below is gathered information about the repository:
56
+
57
+ Task category:
58
+ ${intentCategory}
59
+
60
+ Folder structure:
61
+ ${context.analysis.folderCapsulesHuman ?? ''}
62
+
63
+ Existing relevant files:
64
+ ${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)}
65
+
66
+ Missing files:
67
+ ${JSON.stringify(missingFiles, null, 2)}
68
+
69
+ Return a structured JSON plan describing which information to gather.
70
+
71
+ JSON schema:
72
+ {
73
+ "steps": [
74
+ {
75
+ "action": "stepName",
76
+ "targetFile": "optional/path.ts",
77
+ "description": "explanation",
78
+ "metadata": {}
79
+ }
80
+ ]
81
+ }
82
+ `.trim();
83
+ try {
84
+ const genInput = {
85
+ query: intentText,
86
+ content: prompt
87
+ };
88
+ const genOutput = await generate(genInput);
89
+ const raw = typeof genOutput.data === 'string'
90
+ ? genOutput.data
91
+ : JSON.stringify(genOutput.data ?? '{}');
92
+ const cleaned = await cleanupModule.run({
93
+ query: intentText,
94
+ content: raw
95
+ });
96
+ const jsonString = typeof cleaned.content === 'string'
97
+ ? cleaned.content
98
+ : JSON.stringify(cleaned.content ?? '{}');
99
+ let plan = JSON.parse(jsonString);
100
+ if (!plan || !Array.isArray(plan.steps)) {
101
+ throw new Error('Invalid info plan structure');
102
+ }
103
+ if (plan.steps.length > MAX_STEPS) {
104
+ plan.steps = plan.steps.slice(0, MAX_STEPS);
105
+ }
106
+ plan.steps = plan.steps.map((step) => {
107
+ const actionDef = PLAN_ACTIONS.find(a => a.action === step.action);
108
+ return {
109
+ ...step,
110
+ metadata: {
111
+ ...step.metadata,
112
+ routingConfidence: context.analysis?.routingDecision?.confidence ?? 0
113
+ },
114
+ groups: actionDef?.groups ?? []
115
+ };
116
+ });
117
+ context.analysis.planSuggestion = { plan };
118
+ logInputOutput('infoPlanGen', 'output', plan);
119
+ }
120
+ catch (err) {
121
+ console.warn('⚠️ Failed to generate info plan:', err);
122
+ context.analysis.planSuggestion = { plan: { steps: [] } };
123
+ logInputOutput('infoPlanGen', 'output', { steps: [] });
124
+ }
125
+ }
126
+ };