edsger 0.22.4 → 0.23.1
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/commands/workflow/config/phase-configs.js +5 -0
- package/dist/commands/workflow/core/workflow-logger.d.ts +3 -0
- package/dist/commands/workflow/core/workflow-logger.js +12 -0
- package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
- package/dist/commands/workflow/executors/phase-executor.js +2 -2
- package/dist/commands/workflow/feature-coordinator.js +2 -1
- package/dist/phases/autonomous/index.d.ts +24 -0
- package/dist/phases/autonomous/index.js +404 -0
- package/dist/phases/autonomous/prompts.d.ts +6 -0
- package/dist/phases/autonomous/prompts.js +74 -0
- package/dist/phases/feature-analysis/index.js +0 -7
- package/dist/services/audit-logs.d.ts +2 -2
- package/dist/types/features.d.ts +1 -0
- package/dist/types/pipeline.d.ts +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import { writeCodeTests } from '../../../phases/code-testing/analyzer.js';
|
|
|
10
10
|
import { refineCodeFromPRFeedback } from '../../../phases/code-refine/index.js';
|
|
11
11
|
import { verifyAndResolveComments } from '../../../phases/code-refine-verification/index.js';
|
|
12
12
|
import { reviewPullRequest } from '../../../phases/code-review/index.js';
|
|
13
|
+
import { runAutonomousDevelopment } from '../../../phases/autonomous/index.js';
|
|
13
14
|
import { getGitHubConfig } from '../../../api/github.js';
|
|
14
15
|
/**
|
|
15
16
|
* Wrapper for code-refine phase to inject GitHub token
|
|
@@ -104,4 +105,8 @@ export const phaseConfigs = [
|
|
|
104
105
|
name: 'code-review',
|
|
105
106
|
execute: executeCodeReview,
|
|
106
107
|
},
|
|
108
|
+
{
|
|
109
|
+
name: 'autonomous',
|
|
110
|
+
execute: runAutonomousDevelopment,
|
|
111
|
+
},
|
|
107
112
|
];
|
|
@@ -18,3 +18,6 @@ export declare const logAllFeaturesProcessed: () => void;
|
|
|
18
18
|
export declare const logSkippingProcessing: (processingCount: number) => void;
|
|
19
19
|
export declare const logPollingError: (error: unknown) => void;
|
|
20
20
|
export declare const logProcessNextFeatureError: (error: unknown) => void;
|
|
21
|
+
export declare const logAutonomousIterationStart: (iteration: number, remainingHours: number) => void;
|
|
22
|
+
export declare const logAutonomousIterationComplete: (iteration: number, summary: string, prUrl?: string) => void;
|
|
23
|
+
export declare const logAutonomousPhaseComplete: (totalIterations: number, elapsedHours: number) => void;
|
|
@@ -63,3 +63,15 @@ export const logPollingError = (error) => {
|
|
|
63
63
|
export const logProcessNextFeatureError = (error) => {
|
|
64
64
|
logError(`Error in processNextFeature: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
65
|
};
|
|
66
|
+
export const logAutonomousIterationStart = (iteration, remainingHours) => {
|
|
67
|
+
logInfo(`🔄 Autonomous iteration ${iteration} (${remainingHours.toFixed(1)}h remaining)`);
|
|
68
|
+
};
|
|
69
|
+
export const logAutonomousIterationComplete = (iteration, summary, prUrl) => {
|
|
70
|
+
logSuccess(`✅ Iteration ${iteration}: ${summary}`);
|
|
71
|
+
if (prUrl) {
|
|
72
|
+
logInfo(` PR: ${prUrl}`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
export const logAutonomousPhaseComplete = (totalIterations, elapsedHours) => {
|
|
76
|
+
logSuccess(`🏁 Autonomous development completed: ${totalIterations} iterations in ${elapsedHours.toFixed(2)} hours`);
|
|
77
|
+
};
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
import { EdsgerConfig } from '../../../types/index.js';
|
|
5
5
|
import { PipelinePhaseOptions, PipelineResult, PhaseConfig } from '../../../types/pipeline.js';
|
|
6
6
|
export declare const createPhaseRunner: (phaseConfig: PhaseConfig) => (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
7
|
-
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, };
|
|
7
|
+
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runAutonomousPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
+
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -262,6 +262,6 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
262
262
|
}
|
|
263
263
|
};
|
|
264
264
|
// Create phase runners using the configuration
|
|
265
|
-
const [runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
265
|
+
const [runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
266
266
|
// Export individual phase runners for granular control
|
|
267
|
-
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, };
|
|
267
|
+
export { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -13,7 +13,7 @@ import { getFeature } from '../../api/features/get-feature.js';
|
|
|
13
13
|
import { markWorkflowPhaseCompleted } from '../../api/features/update-feature.js';
|
|
14
14
|
import { logError, logInfo, logWarning } from '../../utils/logger.js';
|
|
15
15
|
import { logPhaseResult } from '../../utils/pipeline-logger.js';
|
|
16
|
-
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
16
|
+
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeRefinePhase, runCodeReviewPhase, runAutonomousPhase, } from './executors/phase-executor.js';
|
|
17
17
|
/**
|
|
18
18
|
* Map workflow phase names (underscore format) to phase runner functions
|
|
19
19
|
* Note: code_refine includes built-in verification loop (similar to technical_design)
|
|
@@ -26,6 +26,7 @@ const PHASE_RUNNERS = {
|
|
|
26
26
|
functional_testing: runFunctionalTestingPhase,
|
|
27
27
|
code_review: runCodeReviewPhase,
|
|
28
28
|
code_refine: runCodeRefinePhase,
|
|
29
|
+
autonomous: runAutonomousPhase,
|
|
29
30
|
};
|
|
30
31
|
/**
|
|
31
32
|
* Get all pending phases from workflow in order
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous Development Phase
|
|
3
|
+
*
|
|
4
|
+
* Runs a time-limited loop where Claude Code SDK analyzes the codebase,
|
|
5
|
+
* decides the next actionable step toward the feature's objective (description),
|
|
6
|
+
* implements it, and commits. All iterations build on the same branch.
|
|
7
|
+
* A PR is created after the first iteration; subsequent pushes update it.
|
|
8
|
+
*/
|
|
9
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
10
|
+
import { ChecklistPhaseContext } from '../../services/checklist.js';
|
|
11
|
+
import { PipelinePhaseOptions } from '../../types/pipeline.js';
|
|
12
|
+
export interface AutonomousResult {
|
|
13
|
+
featureId: string;
|
|
14
|
+
status: 'success' | 'error';
|
|
15
|
+
message: string;
|
|
16
|
+
branchName: string;
|
|
17
|
+
totalIterations: number;
|
|
18
|
+
elapsedHours: number;
|
|
19
|
+
prUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Main entry point for autonomous development phase
|
|
23
|
+
*/
|
|
24
|
+
export declare function runAutonomousDevelopment(options: PipelinePhaseOptions, config: EdsgerConfig, _checklistContext?: ChecklistPhaseContext | null): Promise<any>;
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous Development Phase
|
|
3
|
+
*
|
|
4
|
+
* Runs a time-limited loop where Claude Code SDK analyzes the codebase,
|
|
5
|
+
* decides the next actionable step toward the feature's objective (description),
|
|
6
|
+
* implements it, and commits. All iterations build on the same branch.
|
|
7
|
+
* A PR is created after the first iteration; subsequent pushes update it.
|
|
8
|
+
*/
|
|
9
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
10
|
+
import { DEFAULT_MODEL } from '../../constants.js';
|
|
11
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
12
|
+
import { getFeature } from '../../api/features/get-feature.js';
|
|
13
|
+
import { createBranches, getCurrentBranch, updateBranch, } from '../../services/branches.js';
|
|
14
|
+
import { prepareCustomBranchGitEnvironmentAsync, resetUncommittedChanges, } from '../../utils/git-branch-manager.js';
|
|
15
|
+
import { createBranchPullRequest, } from '../code-implementation/branch-pr-creator.js';
|
|
16
|
+
import { getGitHubConfig } from '../../api/github.js';
|
|
17
|
+
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
18
|
+
import { createAutonomousSystemPrompt, createAutonomousUserPrompt, } from './prompts.js';
|
|
19
|
+
/**
|
|
20
|
+
* Truncate text to a maximum length, adding ellipsis if truncated
|
|
21
|
+
*/
|
|
22
|
+
function truncateText(text, maxLength) {
|
|
23
|
+
if (text.length <= maxLength) {
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
return text.slice(0, maxLength - 3) + '...';
|
|
27
|
+
}
|
|
28
|
+
function userMessage(content) {
|
|
29
|
+
return {
|
|
30
|
+
type: 'user',
|
|
31
|
+
message: { role: 'user', content: content },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function* prompt(userPrompt) {
|
|
35
|
+
yield userMessage(userPrompt);
|
|
36
|
+
await new Promise((res) => setTimeout(res, 10000));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Push branch to remote repository
|
|
40
|
+
*/
|
|
41
|
+
async function pushToRemote(branchName, verbose) {
|
|
42
|
+
try {
|
|
43
|
+
const { execSync } = await import('child_process');
|
|
44
|
+
if (verbose) {
|
|
45
|
+
logInfo(`📤 Pushing branch ${branchName} to remote...`);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
execSync(`git push -u origin ${branchName}`, {
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
51
|
+
});
|
|
52
|
+
return { success: true };
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
try {
|
|
56
|
+
execSync(`git push origin ${branchName}`, {
|
|
57
|
+
encoding: 'utf-8',
|
|
58
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
59
|
+
});
|
|
60
|
+
return { success: true };
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
if (verbose) {
|
|
64
|
+
logInfo(`⚠️ Push rejected, attempting force push with lease...`);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
execSync(`git push --force-with-lease origin ${branchName}`, {
|
|
68
|
+
encoding: 'utf-8',
|
|
69
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
70
|
+
});
|
|
71
|
+
return { success: true };
|
|
72
|
+
}
|
|
73
|
+
catch (forceError) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: forceError instanceof Error
|
|
77
|
+
? forceError.message
|
|
78
|
+
: String(forceError),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: error instanceof Error ? error.message : String(error),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Run a single autonomous iteration using Claude Code SDK
|
|
93
|
+
*/
|
|
94
|
+
async function runIteration(featureId, featureDescription, config, verbose) {
|
|
95
|
+
const systemPrompt = createAutonomousSystemPrompt(config, featureId);
|
|
96
|
+
const userPromptText = createAutonomousUserPrompt(featureDescription);
|
|
97
|
+
let lastAssistantResponse = '';
|
|
98
|
+
for await (const message of query({
|
|
99
|
+
prompt: prompt(userPromptText),
|
|
100
|
+
options: {
|
|
101
|
+
systemPrompt: {
|
|
102
|
+
type: 'preset',
|
|
103
|
+
preset: 'claude_code',
|
|
104
|
+
append: systemPrompt,
|
|
105
|
+
},
|
|
106
|
+
model: DEFAULT_MODEL,
|
|
107
|
+
maxTurns: 3000,
|
|
108
|
+
permissionMode: 'bypassPermissions',
|
|
109
|
+
},
|
|
110
|
+
})) {
|
|
111
|
+
if (verbose) {
|
|
112
|
+
logInfo(`Received message type: ${message.type}`);
|
|
113
|
+
}
|
|
114
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
115
|
+
for (const content of message.message.content) {
|
|
116
|
+
if (content.type === 'text') {
|
|
117
|
+
lastAssistantResponse += content.text + '\n';
|
|
118
|
+
if (verbose) {
|
|
119
|
+
console.log(`\n🤖 ${content.text}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (content.type === 'tool_use') {
|
|
123
|
+
if (verbose) {
|
|
124
|
+
console.log(`\n🔧 ${content.name}: ${content.input.description || 'Running...'}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (message.type === 'result') {
|
|
130
|
+
if (message.subtype === 'success') {
|
|
131
|
+
try {
|
|
132
|
+
const responseText = message.result || lastAssistantResponse;
|
|
133
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
134
|
+
let jsonResult = null;
|
|
135
|
+
if (jsonBlockMatch) {
|
|
136
|
+
jsonResult = JSON.parse(jsonBlockMatch[1]);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
jsonResult = JSON.parse(responseText);
|
|
140
|
+
}
|
|
141
|
+
if (jsonResult?.autonomous_result) {
|
|
142
|
+
const result = jsonResult.autonomous_result;
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
summary: result.summary,
|
|
146
|
+
taskType: result.task_type,
|
|
147
|
+
filesModified: result.files_modified,
|
|
148
|
+
commitHash: result.commit_hash,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// JSON parsed but missing autonomous_result key
|
|
152
|
+
logInfo('⚠️ JSON result missing autonomous_result key, treating as success');
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
summary: 'Autonomous iteration completed (missing result key)',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// JSON parsing failed - still consider it a success if Claude did work
|
|
160
|
+
logInfo('⚠️ Could not parse structured result, treating as success');
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
summary: 'Autonomous iteration completed (unstructured result)',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
logError(`⚠️ Autonomous iteration incomplete: ${message.subtype}`);
|
|
169
|
+
return { success: false };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { success: false };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Main entry point for autonomous development phase
|
|
177
|
+
*/
|
|
178
|
+
export async function runAutonomousDevelopment(options, config, _checklistContext) {
|
|
179
|
+
const { featureId, verbose } = options;
|
|
180
|
+
const startTime = Date.now();
|
|
181
|
+
logInfo(`\n🤖 Starting autonomous development for feature: ${featureId}`);
|
|
182
|
+
// 1. Fetch feature info to get description (= objective) and autonomous_hours
|
|
183
|
+
const feature = await getFeature(featureId, verbose);
|
|
184
|
+
const featureDescription = feature.description || '';
|
|
185
|
+
const autonomousHours = feature.autonomous_hours || 4;
|
|
186
|
+
if (!featureDescription) {
|
|
187
|
+
return {
|
|
188
|
+
featureId,
|
|
189
|
+
status: 'error',
|
|
190
|
+
message: 'Feature description is required for autonomous mode (it serves as the objective)',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const endTime = startTime + autonomousHours * 3600000;
|
|
194
|
+
logInfo(`⏰ Autonomous mode will run for ${autonomousHours} hour(s)`);
|
|
195
|
+
logInfo(`🎯 Objective: ${truncateText(featureDescription.replace(/<[^>]*>/g, ''), 200)}`);
|
|
196
|
+
// 2. Get or create branch
|
|
197
|
+
let currentBranch = null;
|
|
198
|
+
try {
|
|
199
|
+
currentBranch = await getCurrentBranch({ featureId, verbose });
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
if (verbose) {
|
|
203
|
+
logInfo('No existing branch found, will create one');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!currentBranch) {
|
|
207
|
+
const featureName = truncateText(feature.name || 'Autonomous Development', 100);
|
|
208
|
+
const defaultBranchName = `dev/${featureId}`;
|
|
209
|
+
const createdBranches = await createBranches({ featureId, verbose }, [
|
|
210
|
+
{
|
|
211
|
+
name: featureName,
|
|
212
|
+
description: `Autonomous development branch for: ${featureName}`,
|
|
213
|
+
branch_name: defaultBranchName,
|
|
214
|
+
status: 'pending',
|
|
215
|
+
},
|
|
216
|
+
]);
|
|
217
|
+
if (createdBranches.length > 0) {
|
|
218
|
+
currentBranch = createdBranches[0];
|
|
219
|
+
if (verbose) {
|
|
220
|
+
logInfo(`✅ Created branch: ${currentBranch.branch_name}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (!currentBranch) {
|
|
225
|
+
return {
|
|
226
|
+
featureId,
|
|
227
|
+
status: 'error',
|
|
228
|
+
message: 'Failed to create or find a branch for autonomous development',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const devBranchName = currentBranch.branch_name || `dev/${featureId}`;
|
|
232
|
+
// 3. Update branch status to in_progress
|
|
233
|
+
if (currentBranch.status === 'pending') {
|
|
234
|
+
try {
|
|
235
|
+
await updateBranch(currentBranch.id, { status: 'in_progress' }, verbose);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
if (verbose) {
|
|
239
|
+
logError(`Failed to update branch status: ${error}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// 4. Prepare git environment (checkout branch + rebase with main)
|
|
244
|
+
const { cleanup: cleanupGit } = await prepareCustomBranchGitEnvironmentAsync({
|
|
245
|
+
featureBranch: devBranchName,
|
|
246
|
+
baseBranch: 'main',
|
|
247
|
+
verbose,
|
|
248
|
+
resolveConflicts: true,
|
|
249
|
+
conflictResolverConfig: {
|
|
250
|
+
model: DEFAULT_MODEL,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
let totalIterations = 0;
|
|
254
|
+
let prUrl;
|
|
255
|
+
let prNumber;
|
|
256
|
+
try {
|
|
257
|
+
// 5. Main autonomous loop
|
|
258
|
+
while (Date.now() < endTime) {
|
|
259
|
+
totalIterations++;
|
|
260
|
+
const iterationStart = Date.now();
|
|
261
|
+
logInfo(`\n${'='.repeat(60)}\n🔄 Autonomous iteration ${totalIterations}\n${'='.repeat(60)}`);
|
|
262
|
+
// Run iteration
|
|
263
|
+
const iterationResult = await runIteration(featureId, featureDescription, config, verbose);
|
|
264
|
+
const iterationDuration = Date.now() - iterationStart;
|
|
265
|
+
logInfo(`⏱️ Iteration ${totalIterations} took ${(iterationDuration / 1000).toFixed(0)}s`);
|
|
266
|
+
if (iterationResult.success) {
|
|
267
|
+
logInfo(`✅ Iteration ${totalIterations}: ${iterationResult.summary || 'completed'}`);
|
|
268
|
+
// Push to remote
|
|
269
|
+
const pushResult = await pushToRemote(devBranchName, verbose);
|
|
270
|
+
if (!pushResult.success && verbose) {
|
|
271
|
+
logError(`⚠️ Failed to push: ${pushResult.error}`);
|
|
272
|
+
}
|
|
273
|
+
// Create PR after first successful iteration
|
|
274
|
+
if (!prUrl) {
|
|
275
|
+
const githubConfig = await getGitHubConfig(featureId, verbose);
|
|
276
|
+
if (githubConfig.configured &&
|
|
277
|
+
githubConfig.token &&
|
|
278
|
+
githubConfig.owner &&
|
|
279
|
+
githubConfig.repo) {
|
|
280
|
+
const prConfig = {
|
|
281
|
+
githubToken: githubConfig.token,
|
|
282
|
+
owner: githubConfig.owner,
|
|
283
|
+
repo: githubConfig.repo,
|
|
284
|
+
verbose,
|
|
285
|
+
};
|
|
286
|
+
logInfo(`📝 Creating pull request for autonomous work...`);
|
|
287
|
+
const prResult = await createBranchPullRequest(prConfig, devBranchName, currentBranch.name, currentBranch.description ||
|
|
288
|
+
'Autonomous development implementation', 'main');
|
|
289
|
+
if (prResult.success) {
|
|
290
|
+
prUrl = prResult.pullRequestUrl;
|
|
291
|
+
prNumber = prResult.pullRequestNumber;
|
|
292
|
+
logInfo(`✅ Pull request created: ${prUrl}`);
|
|
293
|
+
}
|
|
294
|
+
else if (verbose) {
|
|
295
|
+
logError(`⚠️ Failed to create pull request: ${prResult.error}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if (verbose) {
|
|
299
|
+
logInfo('⚠️ GitHub not configured, skipping PR creation');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Log iteration completion to audit logs
|
|
303
|
+
await logFeaturePhaseEvent({
|
|
304
|
+
featureId,
|
|
305
|
+
eventType: 'autonomous_iteration_completed',
|
|
306
|
+
phase: 'autonomous',
|
|
307
|
+
result: 'success',
|
|
308
|
+
metadata: {
|
|
309
|
+
iteration: totalIterations,
|
|
310
|
+
task_type: iterationResult.taskType,
|
|
311
|
+
summary: iterationResult.summary,
|
|
312
|
+
files_modified: iterationResult.filesModified || [],
|
|
313
|
+
commit_hash: iterationResult.commitHash || '',
|
|
314
|
+
duration_ms: iterationDuration,
|
|
315
|
+
pr_url: prUrl || null,
|
|
316
|
+
elapsed_hours: ((Date.now() - startTime) / 3600000).toFixed(2),
|
|
317
|
+
remaining_hours: ((endTime - Date.now()) / 3600000).toFixed(2),
|
|
318
|
+
timestamp: new Date().toISOString(),
|
|
319
|
+
},
|
|
320
|
+
}, verbose);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
logError(`❌ Iteration ${totalIterations} failed`);
|
|
324
|
+
// Reset any uncommitted changes before next iteration
|
|
325
|
+
try {
|
|
326
|
+
resetUncommittedChanges(verbose);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Ignore cleanup errors
|
|
330
|
+
}
|
|
331
|
+
// Log iteration failure to audit logs
|
|
332
|
+
await logFeaturePhaseEvent({
|
|
333
|
+
featureId,
|
|
334
|
+
eventType: 'autonomous_iteration_failed',
|
|
335
|
+
phase: 'autonomous',
|
|
336
|
+
result: 'error',
|
|
337
|
+
metadata: {
|
|
338
|
+
iteration: totalIterations,
|
|
339
|
+
duration_ms: iterationDuration,
|
|
340
|
+
elapsed_hours: ((Date.now() - startTime) / 3600000).toFixed(2),
|
|
341
|
+
remaining_hours: ((endTime - Date.now()) / 3600000).toFixed(2),
|
|
342
|
+
timestamp: new Date().toISOString(),
|
|
343
|
+
},
|
|
344
|
+
errorMessage: 'Autonomous iteration failed',
|
|
345
|
+
}, verbose);
|
|
346
|
+
}
|
|
347
|
+
// Check time before starting next iteration
|
|
348
|
+
if (Date.now() >= endTime) {
|
|
349
|
+
logInfo(`\n⏰ Time limit reached after ${totalIterations} iterations`);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// 6. Update branch status to ready_for_review
|
|
354
|
+
try {
|
|
355
|
+
await updateBranch(currentBranch.id, {
|
|
356
|
+
status: 'ready_for_review',
|
|
357
|
+
pull_request_url: prUrl || null,
|
|
358
|
+
pull_request_number: prNumber || null,
|
|
359
|
+
}, verbose);
|
|
360
|
+
if (verbose) {
|
|
361
|
+
logInfo(`✅ Updated branch status to ready_for_review`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (verbose) {
|
|
366
|
+
logError(`Failed to update branch status: ${error}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const elapsedHours = (Date.now() - startTime) / 3600000;
|
|
370
|
+
logInfo(`\n${'='.repeat(60)}`);
|
|
371
|
+
logInfo(`🏁 Autonomous development completed`);
|
|
372
|
+
logInfo(` Total iterations: ${totalIterations}`);
|
|
373
|
+
logInfo(` Elapsed time: ${elapsedHours.toFixed(2)} hours`);
|
|
374
|
+
logInfo(` Branch: ${devBranchName}`);
|
|
375
|
+
if (prUrl) {
|
|
376
|
+
logInfo(` PR: ${prUrl}`);
|
|
377
|
+
}
|
|
378
|
+
logInfo(`${'='.repeat(60)}\n`);
|
|
379
|
+
return {
|
|
380
|
+
featureId,
|
|
381
|
+
status: 'success',
|
|
382
|
+
message: `Autonomous development completed: ${totalIterations} iterations in ${elapsedHours.toFixed(2)} hours`,
|
|
383
|
+
branchName: devBranchName,
|
|
384
|
+
totalIterations,
|
|
385
|
+
elapsedHours,
|
|
386
|
+
prUrl,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
const elapsedHours = (Date.now() - startTime) / 3600000;
|
|
391
|
+
logError(`Autonomous development failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
392
|
+
return {
|
|
393
|
+
featureId,
|
|
394
|
+
status: 'error',
|
|
395
|
+
message: `Autonomous development failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
396
|
+
branchName: devBranchName,
|
|
397
|
+
totalIterations,
|
|
398
|
+
elapsedHours,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
finally {
|
|
402
|
+
cleanupGit();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts for the autonomous development phase
|
|
3
|
+
*/
|
|
4
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
5
|
+
export declare function createAutonomousSystemPrompt(_config: EdsgerConfig, featureId: string): string;
|
|
6
|
+
export declare function createAutonomousUserPrompt(featureDescription: string): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts for the autonomous development phase
|
|
3
|
+
*/
|
|
4
|
+
export function createAutonomousSystemPrompt(_config, featureId) {
|
|
5
|
+
return `You are an autonomous developer working toward a specific objective. Your role is to analyze the current state of the codebase, decide the next most valuable step toward the objective, implement it, and commit your changes.
|
|
6
|
+
|
|
7
|
+
**Your Role**: Analyze the codebase, identify the next actionable improvement, implement it, and commit.
|
|
8
|
+
|
|
9
|
+
**Available Tools**:
|
|
10
|
+
- Bash: Git operations, running commands, tests, and builds
|
|
11
|
+
- Glob: Find files and understand project structure
|
|
12
|
+
- Read: Examine existing code, configuration, and documentation files
|
|
13
|
+
- Edit: Modify existing files
|
|
14
|
+
- Write: Create new files
|
|
15
|
+
- TodoWrite: Track implementation tasks
|
|
16
|
+
|
|
17
|
+
**WORKFLOW - Follow these steps**:
|
|
18
|
+
|
|
19
|
+
1. **ANALYZE**: Use Glob and Read to understand the current state of the code
|
|
20
|
+
2. **DECIDE**: Based on the objective, determine the single most impactful next step
|
|
21
|
+
3. **IMPLEMENT**: Make the changes using Edit/Write tools
|
|
22
|
+
4. **TEST**: Run relevant tests or builds to verify your changes work
|
|
23
|
+
5. **COMMIT**: Commit your changes with a conventional commit message (feat:, fix:, refactor:, docs:, test:, chore:)
|
|
24
|
+
|
|
25
|
+
**Types of work you may do**:
|
|
26
|
+
- Implement new feature code toward the objective
|
|
27
|
+
- Fix bugs discovered during analysis
|
|
28
|
+
- Refactor code to improve quality or maintainability
|
|
29
|
+
- Add or improve tests
|
|
30
|
+
- Update documentation
|
|
31
|
+
- Improve error handling or validation
|
|
32
|
+
|
|
33
|
+
**Important Rules**:
|
|
34
|
+
1. You are already on the correct branch - do NOT create or switch branches
|
|
35
|
+
2. Focus on ONE coherent change per iteration
|
|
36
|
+
3. Use conventional commit messages that describe what and why
|
|
37
|
+
4. Follow existing code patterns and conventions in the repository
|
|
38
|
+
5. Ensure proper TypeScript types and interfaces
|
|
39
|
+
6. Add appropriate error handling and validation
|
|
40
|
+
7. Handle pre-commit hooks properly:
|
|
41
|
+
- If lint-staged fails: Fix formatting and linting issues, then retry commit
|
|
42
|
+
- If AI code review fails: Use SKIP_REVIEW=1 git commit -m "message"
|
|
43
|
+
- Last resort: git commit --no-verify -m "message"
|
|
44
|
+
|
|
45
|
+
**CRITICAL - Result Format**:
|
|
46
|
+
You MUST end your response with a JSON object in this EXACT format:
|
|
47
|
+
|
|
48
|
+
\`\`\`json
|
|
49
|
+
{
|
|
50
|
+
"autonomous_result": {
|
|
51
|
+
"feature_id": "${featureId}",
|
|
52
|
+
"task_type": "feature|bugfix|refactor|test|docs|chore",
|
|
53
|
+
"summary": "Brief description of what was done",
|
|
54
|
+
"files_modified": ["file1.ts", "file2.tsx"],
|
|
55
|
+
"commit_hash": "abc123..."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
Remember: Focus on making real, meaningful progress toward the objective. Quality over quantity.`;
|
|
61
|
+
}
|
|
62
|
+
export function createAutonomousUserPrompt(featureDescription) {
|
|
63
|
+
return `## Objective
|
|
64
|
+
|
|
65
|
+
${featureDescription}
|
|
66
|
+
|
|
67
|
+
## Instructions
|
|
68
|
+
|
|
69
|
+
Analyze the current state of the codebase. Based on the objective above, decide the next most valuable step to move toward the goal. Implement the change, verify it works, and commit it.
|
|
70
|
+
|
|
71
|
+
Focus on ONE coherent, well-scoped change. Make real progress toward the objective.
|
|
72
|
+
|
|
73
|
+
Begin by analyzing the codebase to understand the current state and decide your next step.`;
|
|
74
|
+
}
|
|
@@ -5,14 +5,11 @@ import { executeAnalysisQuery } from './agent.js';
|
|
|
5
5
|
import { performVerificationCycle } from '../feature-analysis-verification/index.js';
|
|
6
6
|
import { deleteArtifacts, deleteSpecificArtifacts, updateArtifactsToReady, saveAnalysisArtifactsAsDraft, buildAnalysisResult, } from './outcome.js';
|
|
7
7
|
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
8
|
-
import { preparePhaseGitEnvironment } from '../../utils/git-branch-manager.js';
|
|
9
8
|
export const analyseFeature = async (options, config, checklistContext) => {
|
|
10
9
|
const { featureId, verbose } = options;
|
|
11
10
|
if (verbose) {
|
|
12
11
|
logInfo(`Starting feature analysis for feature ID: ${featureId}`);
|
|
13
12
|
}
|
|
14
|
-
// Prepare git environment: switch to feature branch and rebase with main
|
|
15
|
-
const cleanupGit = preparePhaseGitEnvironment(featureId, 'main', verbose);
|
|
16
13
|
try {
|
|
17
14
|
const context = await prepareAnalysisContext(featureId, checklistContext, verbose);
|
|
18
15
|
const systemPrompt = createFeatureAnalysisSystemPrompt();
|
|
@@ -157,8 +154,4 @@ export const analyseFeature = async (options, config, checklistContext) => {
|
|
|
157
154
|
status: 'error',
|
|
158
155
|
};
|
|
159
156
|
}
|
|
160
|
-
finally {
|
|
161
|
-
// Always return to main branch after phase completion
|
|
162
|
-
cleanupGit();
|
|
163
|
-
}
|
|
164
157
|
};
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export interface LogPhaseEventParams {
|
|
6
6
|
featureId: string;
|
|
7
|
-
eventType: 'phase_started' | 'phase_completed' | 'phase_failed';
|
|
8
|
-
phase: 'feature_analysis' | 'technical_design' | 'branch_planning' | 'code_implementation' | 'functional_testing' | 'pull_request' | 'code_review' | 'code_refine' | 'code_refine_verification';
|
|
7
|
+
eventType: 'phase_started' | 'phase_completed' | 'phase_failed' | 'autonomous_iteration_completed' | 'autonomous_iteration_failed';
|
|
8
|
+
phase: 'feature_analysis' | 'technical_design' | 'branch_planning' | 'code_implementation' | 'functional_testing' | 'pull_request' | 'code_review' | 'code_refine' | 'code_refine_verification' | 'autonomous';
|
|
9
9
|
result?: 'success' | 'error' | 'warning' | 'info';
|
|
10
10
|
metadata?: Record<string, any>;
|
|
11
11
|
errorMessage?: string;
|
package/dist/types/features.d.ts
CHANGED
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { EdsgerConfig } from './index.js';
|
|
5
5
|
import { ChecklistPhaseContext } from '../services/checklist.js';
|
|
6
|
-
export type ExecutionMode = 'full_pipeline' | 'only_feature_analysis' | 'only_technical_design' | 'only_branch_planning' | 'only_code_implementation' | 'only_functional_testing' | 'only_code_refine' | 'only_code_review' | 'from_feature_analysis' | 'from_technical_design' | 'from_branch_planning' | 'from_code_implementation' | 'from_functional_testing' | 'from_code_review' | 'custom';
|
|
6
|
+
export type ExecutionMode = 'full_pipeline' | 'only_feature_analysis' | 'only_technical_design' | 'only_branch_planning' | 'only_code_implementation' | 'only_functional_testing' | 'only_code_refine' | 'only_code_review' | 'from_feature_analysis' | 'from_technical_design' | 'from_branch_planning' | 'from_code_implementation' | 'from_functional_testing' | 'from_code_review' | 'custom' | 'autonomous';
|
|
7
7
|
export type WorkflowPhaseStatus = 'pending' | 'completed';
|
|
8
8
|
export interface WorkflowPhase {
|
|
9
9
|
phase: string;
|