scai 0.1.117 ā 0.1.119
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/README.md +88 -503
- 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 +1 -11
- package/dist/pipeline/modules/changeLogModule.js +16 -19
- package/dist/pipeline/modules/chunkManagerModule.js +24 -0
- package/dist/pipeline/modules/cleanupModule.js +95 -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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// File: src/agents/transformPlanGenStep.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
|
+
* TRANSFORM PLAN GENERATOR
|
|
9
|
+
* Generates steps that perform concrete transformations or code changes.
|
|
10
|
+
*/
|
|
11
|
+
export const transformPlanGenStep = {
|
|
12
|
+
name: 'transformPlanGen',
|
|
13
|
+
description: 'Generates code transformation / modification steps.',
|
|
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 TRANSFORM group only
|
|
22
|
+
const effectiveActions = PLAN_ACTIONS.filter(a => a.groups?.includes('transform'));
|
|
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 how to implement actual code transformations
|
|
29
|
+
or modifications in the system to achieve the intended task.
|
|
30
|
+
|
|
31
|
+
Intent / task description:
|
|
32
|
+
${intentText}
|
|
33
|
+
|
|
34
|
+
If the intent indicates that this is NOT a coding, refactoring, or inline commenting task,
|
|
35
|
+
then return an empty plan object with an empty "steps" array:
|
|
36
|
+
{ "steps": [] }
|
|
37
|
+
|
|
38
|
+
Allowed actions (transformation only):
|
|
39
|
+
${actionsJson}
|
|
40
|
+
|
|
41
|
+
Task category:
|
|
42
|
+
${intentCategory}
|
|
43
|
+
|
|
44
|
+
Folder structure:
|
|
45
|
+
${context.analysis.folderCapsulesHuman ?? ''}
|
|
46
|
+
|
|
47
|
+
Existing relevant files:
|
|
48
|
+
${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)}
|
|
49
|
+
|
|
50
|
+
Only perform transformations that are safe based on the existing analysis.
|
|
51
|
+
|
|
52
|
+
ā” Phase guidance:
|
|
53
|
+
- Actions are grouped into phases: info, transform, finalize.
|
|
54
|
+
- Only include transform steps in this phase.
|
|
55
|
+
- Include a 'writeFile' step for each 'codeTransform' to persist changes to disk.
|
|
56
|
+
- Each step must include: "action", "targetFile" (optional), "description", "metadata"
|
|
57
|
+
|
|
58
|
+
ā IMPORTANT: Do NOT include "info" steps here. Only plan actual code transformations.
|
|
59
|
+
|
|
60
|
+
Return a strictly valid JSON plan:
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
"steps": [
|
|
64
|
+
{ "action": "stepName", "targetFile": "optional/path.ts", "description": "explanation", "metadata": {} }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
`.trim();
|
|
68
|
+
try {
|
|
69
|
+
console.log('TransformPlanGenStep prompt', prompt);
|
|
70
|
+
const genInput = { query: intentText, content: prompt };
|
|
71
|
+
const genOutput = await generate(genInput);
|
|
72
|
+
const raw = typeof genOutput.data === 'string'
|
|
73
|
+
? genOutput.data
|
|
74
|
+
: JSON.stringify(genOutput.data ?? '{}');
|
|
75
|
+
const cleaned = await cleanupModule.run({ query: intentText, content: raw });
|
|
76
|
+
const jsonString = typeof cleaned.content === 'string'
|
|
77
|
+
? cleaned.content
|
|
78
|
+
: JSON.stringify(cleaned.content ?? '{}');
|
|
79
|
+
let plan = JSON.parse(jsonString);
|
|
80
|
+
if (!plan || !Array.isArray(plan.steps))
|
|
81
|
+
throw new Error('Invalid transform plan structure');
|
|
82
|
+
if (plan.steps.length > MAX_STEPS)
|
|
83
|
+
plan.steps = plan.steps.slice(0, MAX_STEPS);
|
|
84
|
+
// Map groups & metadata
|
|
85
|
+
plan.steps = plan.steps.map(step => {
|
|
86
|
+
const actionDef = PLAN_ACTIONS.find(a => a.action === step.action);
|
|
87
|
+
return {
|
|
88
|
+
...step,
|
|
89
|
+
metadata: {
|
|
90
|
+
...step.metadata,
|
|
91
|
+
routingConfidence: context.analysis?.routingDecision?.confidence ?? 0
|
|
92
|
+
},
|
|
93
|
+
groups: actionDef?.groups ?? ['transform']
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
// Replace existing transform steps in planSuggestion
|
|
97
|
+
context.analysis.planSuggestion.plan.steps = [
|
|
98
|
+
...context.analysis.planSuggestion.plan.steps.filter(s => !s.groups?.includes('transform')),
|
|
99
|
+
...plan.steps
|
|
100
|
+
];
|
|
101
|
+
logInputOutput('transformPlanGen', 'output', plan);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.warn('ā ļø Failed to generate transform plan:', err);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// File: src/agents/steps/understandIntentStep.ts
|
|
2
|
+
import { generate } from "../lib/generate.js";
|
|
3
|
+
import { logInputOutput } from "../utils/promptLogHelper.js";
|
|
4
|
+
export const understandIntentStep = {
|
|
5
|
+
name: "understandIntent",
|
|
6
|
+
description: "Analyze the user query and determine its intent, type, and the appropriate task category.",
|
|
7
|
+
/**
|
|
8
|
+
* Run the step
|
|
9
|
+
*/
|
|
10
|
+
run: async (input) => {
|
|
11
|
+
const { context } = input;
|
|
12
|
+
const prompt = `
|
|
13
|
+
You are an AI assistant whose job is to determine the user's intent.
|
|
14
|
+
|
|
15
|
+
User Query:
|
|
16
|
+
${context.initContext?.userQuery}
|
|
17
|
+
|
|
18
|
+
Return a STRICT JSON object with the following fields:
|
|
19
|
+
{
|
|
20
|
+
"intent": "short sentence summarizing the user's intent",
|
|
21
|
+
"intentCategory": "one of: question, request, codingTask, refactorTask, explanation, debugging, planning, writing, other",
|
|
22
|
+
"normalizedQuery": "a cleaned and direct restatement of the user query",
|
|
23
|
+
"confidence": 0-1 // float
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Do not include commentary. Emit ONLY valid JSON.
|
|
27
|
+
`.trim();
|
|
28
|
+
try {
|
|
29
|
+
const genInput = {
|
|
30
|
+
query: context.initContext?.userQuery ?? '',
|
|
31
|
+
content: prompt
|
|
32
|
+
};
|
|
33
|
+
const genOutput = await generate(genInput);
|
|
34
|
+
let raw = genOutput.data;
|
|
35
|
+
if (typeof raw !== "string")
|
|
36
|
+
raw = JSON.stringify(raw ?? "{}");
|
|
37
|
+
logInputOutput("understandIntent", "output", raw);
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
parsed = {
|
|
44
|
+
intent: "unknown",
|
|
45
|
+
intentCategory: "other",
|
|
46
|
+
normalizedQuery: context.initContext?.userQuery,
|
|
47
|
+
confidence: 0.3
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Ensure the analysis object exists
|
|
51
|
+
context.analysis ?? (context.analysis = {});
|
|
52
|
+
// Store intent inside a dedicated object
|
|
53
|
+
context.analysis.intent = {
|
|
54
|
+
intent: parsed.intent,
|
|
55
|
+
intentCategory: parsed.intentCategory,
|
|
56
|
+
normalizedQuery: parsed.normalizedQuery,
|
|
57
|
+
confidence: parsed.confidence
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
console.error("understandIntent error:", err);
|
|
62
|
+
// Ensure the analysis object exists
|
|
63
|
+
context.analysis ?? (context.analysis = {});
|
|
64
|
+
context.analysis.intent = {
|
|
65
|
+
intent: "unknown",
|
|
66
|
+
intentCategory: "other",
|
|
67
|
+
normalizedQuery: context.initContext?.userQuery ?? '',
|
|
68
|
+
confidence: 0.0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { logInputOutput } from "../utils/promptLogHelper.js";
|
|
2
|
+
export const validationAnalysisStep = {
|
|
3
|
+
name: "validationAnalysis",
|
|
4
|
+
description: "Validate semantic analysis results and verify that focused working files were correctly synced into plan.targetFiles.",
|
|
5
|
+
groups: ["analysis"],
|
|
6
|
+
run: async (input) => {
|
|
7
|
+
const ctx = input.context;
|
|
8
|
+
if (!ctx) {
|
|
9
|
+
throw new Error("[validationAnalysisStep] StructuredContext is required but was not provided.");
|
|
10
|
+
}
|
|
11
|
+
const notes = [];
|
|
12
|
+
const warnings = [];
|
|
13
|
+
// -----------------------------
|
|
14
|
+
// Focus validation
|
|
15
|
+
// -----------------------------
|
|
16
|
+
const focusFiles = ctx.analysis?.focus?.relevantFiles ?? [];
|
|
17
|
+
if (!focusFiles.length) {
|
|
18
|
+
warnings.push("No relevant files found in analysis.focus.");
|
|
19
|
+
}
|
|
20
|
+
// -----------------------------
|
|
21
|
+
// Semantic understanding validation
|
|
22
|
+
// -----------------------------
|
|
23
|
+
const understanding = ctx.analysis?.understanding;
|
|
24
|
+
if (!understanding) {
|
|
25
|
+
notes.push("No semantic understanding produced by analysis.");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (!understanding.risks?.length) {
|
|
29
|
+
notes.push("No risks recorded in analysis.understanding.");
|
|
30
|
+
}
|
|
31
|
+
if (!understanding.assumptions?.length) {
|
|
32
|
+
notes.push("No assumptions recorded in analysis.understanding.");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// -----------------------------
|
|
36
|
+
// Intent presence & shape validation
|
|
37
|
+
// (Produced by understandIntent step)
|
|
38
|
+
// -----------------------------
|
|
39
|
+
const intent = ctx.analysis?.intent;
|
|
40
|
+
if (!intent) {
|
|
41
|
+
warnings.push("No intent found in context (expected from understandIntent step).");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
if (!intent.intent || !intent.intentCategory) {
|
|
45
|
+
warnings.push("Intent is missing required fields (intent/intentCategory).");
|
|
46
|
+
}
|
|
47
|
+
if (typeof intent.confidence !== "number") {
|
|
48
|
+
warnings.push("Intent confidence not set or invalid.");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// -----------------------------
|
|
52
|
+
// Plan.targetFiles consistency validation
|
|
53
|
+
// -----------------------------
|
|
54
|
+
const plan = ctx.plan;
|
|
55
|
+
const workingFiles = new Set(ctx.workingFiles?.map(f => f.path) ?? []);
|
|
56
|
+
const targetFiles = new Set(plan?.targetFiles ?? []);
|
|
57
|
+
if (!plan) {
|
|
58
|
+
warnings.push("No plan found in context; target files were not validated.");
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Only focused files that are also working files are expected to be in the plan
|
|
62
|
+
const expectedTargetFiles = focusFiles.filter(f => workingFiles.has(f));
|
|
63
|
+
const missing = expectedTargetFiles.filter(f => !targetFiles.has(f));
|
|
64
|
+
if (missing.length > 0) {
|
|
65
|
+
warnings.push(`Some focused working files were not added to plan.targetFiles: ${missing.join(", ")}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const output = {
|
|
69
|
+
query: input.query,
|
|
70
|
+
data: {
|
|
71
|
+
notes,
|
|
72
|
+
warnings,
|
|
73
|
+
summary: {
|
|
74
|
+
focusValid: focusFiles.length > 0,
|
|
75
|
+
semanticValid: !!understanding,
|
|
76
|
+
intentValid: !!intent,
|
|
77
|
+
planFilesValid: !!plan &&
|
|
78
|
+
focusFiles
|
|
79
|
+
.filter(f => workingFiles.has(f))
|
|
80
|
+
.every(f => targetFiles.has(f)),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
logInputOutput("validationAnalysis", "output", output.data);
|
|
85
|
+
return output;
|
|
86
|
+
},
|
|
87
|
+
};
|
package/dist/commands/AskCmd.js
CHANGED
|
@@ -1,145 +1,76 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
2
|
import readline from 'readline';
|
|
4
|
-
import { searchFiles, queryFiles } from '../db/fileIndex.js';
|
|
5
|
-
import { sanitizeQueryForFts } from '../utils/sanitizeQuery.js';
|
|
6
|
-
import { generate } from '../lib/generate.js';
|
|
7
|
-
import { buildContextualPrompt } from '../utils/buildContextualPrompt.js';
|
|
8
|
-
import { log } from '../utils/log.js';
|
|
9
|
-
import { PROMPT_LOG_PATH, SCAI_HOME, RELATED_FILES_LIMIT, getIndexDir } from '../constants.js';
|
|
10
3
|
import chalk from 'chalk';
|
|
4
|
+
import { searchFiles } from '../db/fileIndex.js';
|
|
5
|
+
import { sanitizeQueryForFts } from '../utils/sanitizeQuery.js';
|
|
6
|
+
import { NUM_TOPFILES, RELATED_FILES_LIMIT, getIndexDir } from '../constants.js';
|
|
7
|
+
import { MainAgent } from '../agents/MainAgent.js';
|
|
8
|
+
import { buildLightContext } from '../utils/buildContextualPrompt.js';
|
|
11
9
|
export async function runAskCommand(query) {
|
|
12
|
-
|
|
10
|
+
// STEP 0: Get user query
|
|
11
|
+
if (!query)
|
|
13
12
|
query = await promptOnce('š¬ Ask your question:\n');
|
|
14
|
-
}
|
|
15
13
|
query = query.trim();
|
|
16
14
|
if (!query) {
|
|
17
15
|
console.error('ā No question provided.\nš Usage: scai ask "your question"');
|
|
18
16
|
return;
|
|
19
17
|
}
|
|
20
18
|
console.log(`š Using index root: ${getIndexDir()}`);
|
|
21
|
-
//
|
|
22
|
-
const start = Date.now();
|
|
23
|
-
const semanticResults = await searchFiles(query, RELATED_FILES_LIMIT);
|
|
24
|
-
const duration = Date.now() - start;
|
|
25
|
-
console.log(`ā±ļø searchFiles took ${duration}ms and returned ${semanticResults.length} result(s)`);
|
|
26
|
-
semanticResults.forEach((file, i) => {
|
|
27
|
-
console.log(` ${i + 1}. š Path: ${file.path} | Score: ${file.score?.toFixed(3) ?? 'n/a'}`);
|
|
28
|
-
});
|
|
29
|
-
// Fallback FTS search
|
|
19
|
+
// STEP 1: FTS Search
|
|
30
20
|
const safeQuery = sanitizeQueryForFts(query);
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const combinedResults = [];
|
|
38
|
-
for (const file of semanticResults) {
|
|
39
|
-
const resolved = path.resolve(file.path);
|
|
40
|
-
seen.add(resolved);
|
|
41
|
-
combinedResults.push(file);
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const results = await searchFiles(safeQuery, RELATED_FILES_LIMIT);
|
|
23
|
+
console.log(`ā±ļø searchFiles took ${Date.now() - start}ms and returned ${results.length} result(s)`);
|
|
24
|
+
if (results.length === 0) {
|
|
25
|
+
console.log(chalk.redBright('ā No results found.'));
|
|
26
|
+
return;
|
|
42
27
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
path: file.path,
|
|
50
|
-
summary: file.summary || '',
|
|
51
|
-
score: 0.0,
|
|
52
|
-
sim: 0,
|
|
53
|
-
bm25: 0
|
|
54
|
-
});
|
|
28
|
+
// STEP 2: Build topFiles (with code)
|
|
29
|
+
const topResults = results.slice(0, NUM_TOPFILES);
|
|
30
|
+
const topFiles = topResults.map(r => {
|
|
31
|
+
let code = '';
|
|
32
|
+
try {
|
|
33
|
+
code = fs.readFileSync(r.path, 'utf-8');
|
|
55
34
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const queryFilenameRaw = path.basename(query).toLowerCase();
|
|
59
|
-
const queryFilenameNoExt = queryFilenameRaw.replace(/\.[^/.]+$/, '');
|
|
60
|
-
const exactMatchIndex = combinedResults.findIndex(f => {
|
|
61
|
-
const base = path.basename(f.path).toLowerCase();
|
|
62
|
-
const baseNoExt = base.replace(/\.[^/.]+$/, '');
|
|
63
|
-
return base === queryFilenameRaw || baseNoExt === queryFilenameNoExt;
|
|
64
|
-
});
|
|
65
|
-
if (exactMatchIndex !== -1) {
|
|
66
|
-
const [exactMatch] = combinedResults.splice(exactMatchIndex, 1);
|
|
67
|
-
combinedResults.unshift(exactMatch);
|
|
68
|
-
console.log(`šÆ Exact match prioritized: ${exactMatch.path}`);
|
|
69
|
-
}
|
|
70
|
-
// Log combined results
|
|
71
|
-
if (combinedResults.length) {
|
|
72
|
-
console.log('\nš Final Related Files:');
|
|
73
|
-
combinedResults.forEach((f, i) => {
|
|
74
|
-
console.log(` ${i + 1}. ${f.path} (${f.score?.toFixed(3) ?? 'fallback'})`);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
console.log('ā ļø No similar files found. Using query only.');
|
|
79
|
-
}
|
|
80
|
-
// STEP 4+: Build contextual prompt using topFile + combinedResults
|
|
81
|
-
if (combinedResults.length === 0) {
|
|
82
|
-
throw new Error('ā No search results found. Cannot build contextual prompt.');
|
|
83
|
-
}
|
|
84
|
-
const file = combinedResults[0];
|
|
85
|
-
let code = "";
|
|
86
|
-
// STEP 4++: Add code to params
|
|
87
|
-
try {
|
|
88
|
-
code = fs.readFileSync(file.path, 'utf-8');
|
|
89
|
-
if (!code) {
|
|
90
|
-
console.warn(`ā ļø No code loaded for top file: ${file.path}`);
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.warn(`ā ļø Failed to read file: ${r.path}`);
|
|
91
37
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
38
|
+
return {
|
|
39
|
+
path: r.path,
|
|
40
|
+
summary: r.summary ?? undefined,
|
|
41
|
+
code,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
console.log(chalk.greenBright(`šÆ Selected top ${topFiles.length} file(s):`));
|
|
45
|
+
topFiles.forEach(f => console.log(` ā ${f.path}`));
|
|
46
|
+
// STEP 3: Related files (all results except topFiles)
|
|
47
|
+
const relatedFiles = results.slice(NUM_TOPFILES).map(f => ({
|
|
48
|
+
id: f.id,
|
|
49
|
+
path: f.path,
|
|
50
|
+
summary: f.summary ?? undefined,
|
|
51
|
+
}));
|
|
52
|
+
// STEP 4: Build context prompt
|
|
102
53
|
const promptArgs = {
|
|
103
|
-
|
|
104
|
-
relatedFiles
|
|
54
|
+
topFiles,
|
|
55
|
+
relatedFiles,
|
|
105
56
|
query,
|
|
106
57
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
console.log(chalk.greenBright('ā
Prompt built successfully.'));
|
|
110
|
-
console.log(chalk.cyan(`[runAskCommand] Prompt token estimate: ~${Math.round(promptContent.length / 4)} tokens`));
|
|
111
|
-
// STEP 5: Save prompt
|
|
58
|
+
const context = await buildLightContext(promptArgs);
|
|
59
|
+
// STEP 5: Run agent
|
|
112
60
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
log(`š Prompt saved to ${PROMPT_LOG_PATH}`);
|
|
61
|
+
console.log('\nš¤ Launching autonomous agent...');
|
|
62
|
+
const agent = new MainAgent(context);
|
|
63
|
+
await agent.run();
|
|
117
64
|
}
|
|
118
65
|
catch (err) {
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
// STEP 6: Ask model
|
|
122
|
-
try {
|
|
123
|
-
console.log('\nš¤ Asking the model...');
|
|
124
|
-
const input = {
|
|
125
|
-
content: promptContent,
|
|
126
|
-
filepath: topFile.path,
|
|
127
|
-
};
|
|
128
|
-
const modelResponse = await generate(input);
|
|
129
|
-
console.log(`\nš§ Model Response:\n${modelResponse.content}`);
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
console.error('ā Model request failed:', err);
|
|
66
|
+
console.error('ā Autonomous agent run failed:', err);
|
|
133
67
|
}
|
|
134
68
|
}
|
|
135
|
-
//
|
|
69
|
+
// helper
|
|
136
70
|
function promptOnce(promptText) {
|
|
137
71
|
return new Promise(resolve => {
|
|
138
72
|
console.log(promptText);
|
|
139
|
-
const rl = readline.createInterface({
|
|
140
|
-
input: process.stdin,
|
|
141
|
-
output: process.stdout,
|
|
142
|
-
});
|
|
73
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
143
74
|
rl.question('> ', answer => {
|
|
144
75
|
rl.close();
|
|
145
76
|
resolve(answer.trim());
|
|
@@ -7,7 +7,6 @@ import { changelogModule } from '../pipeline/modules/changeLogModule.js';
|
|
|
7
7
|
import { askChangelogApproval } from '../utils/changeLogPrompt.js';
|
|
8
8
|
import { openTextEditor } from '../utils/editor.js';
|
|
9
9
|
export async function handleStandaloneChangelogUpdate() {
|
|
10
|
-
// Don't bother with diffs at all here
|
|
11
10
|
let entry = await generateChangelogEntry();
|
|
12
11
|
if (!entry) {
|
|
13
12
|
console.log('ā ļø No significant changes found.');
|
|
@@ -51,8 +50,16 @@ export async function generateChangelogEntry(currentCommitMsg) {
|
|
|
51
50
|
console.log("ā ļø No commits found since last release.");
|
|
52
51
|
return null;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
53
|
+
// --- ModuleIO usage ---
|
|
54
|
+
const result = await runModulePipeline([changelogModule], {
|
|
55
|
+
query: 'generate changelog entry',
|
|
56
|
+
content: commits
|
|
57
|
+
});
|
|
58
|
+
const output = result?.data
|
|
59
|
+
? typeof result.data === 'string'
|
|
60
|
+
? result.data.trim()
|
|
61
|
+
: JSON.stringify(result.data, null, 2)
|
|
62
|
+
: null;
|
|
56
63
|
if (!output || output === 'NO UPDATE') {
|
|
57
64
|
console.log('ā ļø No significant changes detected for changelog.');
|
|
58
65
|
return null;
|
|
@@ -69,11 +76,10 @@ function getLastGitTag() {
|
|
|
69
76
|
return execSync('git describe --tags --abbrev=0', { encoding: 'utf-8' }).trim();
|
|
70
77
|
}
|
|
71
78
|
catch {
|
|
72
|
-
return null;
|
|
79
|
+
return null;
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
function getCommitsSinceTag(tag) {
|
|
76
|
-
// Get commit messages in a simple format, e.g. only commit messages
|
|
77
83
|
return execSync(`git log ${tag}..HEAD --pretty=format:%s`, { encoding: 'utf-8' }).trim();
|
|
78
84
|
}
|
|
79
85
|
export async function updateChangelogFile(entry) {
|