agent-state-machine 2.5.0 → 2.6.0
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/lib/llm.js +14 -3
- package/lib/runtime/prompt.js +1 -1
- package/lib/runtime/runtime.js +14 -2
- package/lib/runtime/track-changes.js +84 -0
- package/package.json +1 -1
- package/templates/project-builder/agents/{code-writer.md → code-write.md} +18 -12
- package/templates/project-builder/agents/{assumptions-clarifier.md → intake-assumptions.md} +1 -0
- package/templates/project-builder/agents/{requirements-clarifier.md → intake-requirements.md} +1 -0
- package/templates/project-builder/agents/{scope-clarifier.md → intake-scope.md} +1 -0
- package/templates/project-builder/agents/{security-clarifier.md → intake-security.md} +1 -0
- package/templates/project-builder/agents/{roadmap-generator.md → plan-roadmap.md} +1 -0
- package/templates/project-builder/agents/{task-planner.md → plan-tasks.md} +1 -0
- package/templates/project-builder/agents/post-code-fix.md +59 -0
- package/templates/project-builder/agents/{code-reviewer.md → post-code-review.md} +10 -0
- package/templates/project-builder/agents/post-code-security.md +55 -0
- package/templates/project-builder/agents/{security-reviewer.md → pre-code-security.md} +8 -11
- package/templates/project-builder/agents/{test-planner.md → pre-code-tests.md} +1 -0
- package/templates/project-builder/agents/response-interpreter.md +1 -0
- package/templates/project-builder/agents/verify-commit-msg.md +64 -0
- package/templates/project-builder/agents/{sanity-checker.md → verify-sanity.md} +1 -12
- package/templates/project-builder/config.js +15 -4
- package/templates/project-builder/scripts/safeguard-recovery.js +40 -0
- package/templates/project-builder/scripts/validate-changes.js +61 -0
- package/templates/project-builder/scripts/workflow-helpers.js +87 -35
- package/templates/project-builder/workflow.js +231 -93
- package/vercel-server/public/remote/assets/{index-BSL55rdk.js → index-BnuR91vD.js} +1 -1
- package/vercel-server/public/remote/index.html +1 -1
- package/vercel-server/ui/src/components/ContentCard.jsx +7 -7
- package/vercel-server/ui/src/components/SettingsModal.jsx +19 -4
- package/templates/project-builder/agents/code-fixer.md +0 -50
- /package/templates/project-builder/{agents → scripts}/sanity-runner.js +0 -0
|
@@ -13,7 +13,6 @@ import path from 'path';
|
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import {
|
|
15
15
|
writeMarkdownFile,
|
|
16
|
-
writeImplementationFiles,
|
|
17
16
|
isApproval,
|
|
18
17
|
renderRoadmapMarkdown,
|
|
19
18
|
renderTasksMarkdown,
|
|
@@ -26,13 +25,19 @@ import {
|
|
|
26
25
|
getQuickFixAttempts,
|
|
27
26
|
incrementQuickFixAttempts,
|
|
28
27
|
resetQuickFixAttempts,
|
|
29
|
-
detectTestFramework
|
|
28
|
+
detectTestFramework,
|
|
29
|
+
getProjectFiles,
|
|
30
|
+
createGitCommit,
|
|
31
|
+
ensureGitRepo
|
|
30
32
|
} from './scripts/workflow-helpers.js';
|
|
31
33
|
import {
|
|
32
34
|
createInteraction,
|
|
33
35
|
parseResponse,
|
|
34
36
|
formatInteractionPrompt as formatPrompt
|
|
35
37
|
} from './scripts/interaction-helpers.js';
|
|
38
|
+
import { validateProjectChanges } from './scripts/validate-changes.js';
|
|
39
|
+
import { autoFixValidationIssues, rollbackFile } from './scripts/safeguard-recovery.js';
|
|
40
|
+
import runSanityChecks from './scripts/sanity-runner.js';
|
|
36
41
|
|
|
37
42
|
// Derive workflow directory dynamically
|
|
38
43
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -49,48 +54,6 @@ const C = {
|
|
|
49
54
|
yellow: '\x1b[33m'
|
|
50
55
|
};
|
|
51
56
|
|
|
52
|
-
function applyFixesToImplementation(originalImplementation, fixes) {
|
|
53
|
-
if (!originalImplementation || !Array.isArray(fixes) || fixes.length === 0) {
|
|
54
|
-
return originalImplementation;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const updated = { ...originalImplementation };
|
|
58
|
-
const container = updated.implementation ? { ...updated.implementation } : updated;
|
|
59
|
-
const files = Array.isArray(container.files) ? [...container.files] : [];
|
|
60
|
-
|
|
61
|
-
for (const fix of fixes) {
|
|
62
|
-
if (!fix?.path || !fix?.code) {
|
|
63
|
-
console.warn(` [Fix] Skipping invalid fix entry: ${JSON.stringify(fix)}`);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (fix.operation && fix.operation !== 'replace') {
|
|
67
|
-
console.warn(` [Fix] Unsupported operation "${fix.operation}" for ${fix.path}`);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const existingIndex = files.findIndex((file) => file.path === fix.path);
|
|
72
|
-
const nextFile = {
|
|
73
|
-
...(existingIndex >= 0 ? files[existingIndex] : {}),
|
|
74
|
-
path: fix.path,
|
|
75
|
-
code: fix.code,
|
|
76
|
-
purpose: fix.purpose || (existingIndex >= 0 ? files[existingIndex].purpose : 'Updated by code-fixer')
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
if (existingIndex >= 0) {
|
|
80
|
-
files[existingIndex] = nextFile;
|
|
81
|
-
} else {
|
|
82
|
-
files.push(nextFile);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (updated.implementation) {
|
|
87
|
-
updated.implementation = { ...container, files };
|
|
88
|
-
return updated;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return { ...updated, files };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
57
|
// ============================================
|
|
95
58
|
// MAIN WORKFLOW
|
|
96
59
|
// ============================================
|
|
@@ -98,6 +61,13 @@ function applyFixesToImplementation(originalImplementation, fixes) {
|
|
|
98
61
|
export default async function () {
|
|
99
62
|
console.log('Starting Project Builder Workflow...\n');
|
|
100
63
|
|
|
64
|
+
// Ensure git repo exists for commit tracking
|
|
65
|
+
const runtime = getCurrentRuntime();
|
|
66
|
+
const gitResult = ensureGitRepo(runtime.workflowConfig.projectRoot);
|
|
67
|
+
if (gitResult.initialized) {
|
|
68
|
+
console.log(' [Git] Initialized new repository\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
101
71
|
// ============================================
|
|
102
72
|
// PHASE 1: PROJECT INTAKE
|
|
103
73
|
// ============================================
|
|
@@ -128,7 +98,7 @@ export default async function () {
|
|
|
128
98
|
// 1. Scope Clarification
|
|
129
99
|
if (!memory.scopeClarified) {
|
|
130
100
|
console.log('--- Scope Clarification ---');
|
|
131
|
-
const scopeResult = await agent('scope
|
|
101
|
+
const scopeResult = await agent('intake-scope', {
|
|
132
102
|
projectDescription: memory.projectDescription
|
|
133
103
|
});
|
|
134
104
|
memory.scope = scopeResult;
|
|
@@ -138,7 +108,7 @@ export default async function () {
|
|
|
138
108
|
// 2. Requirements Clarification
|
|
139
109
|
if (!memory.requirementsClarified) {
|
|
140
110
|
console.log('--- Requirements Clarification ---');
|
|
141
|
-
const reqResult = await agent('requirements
|
|
111
|
+
const reqResult = await agent('intake-requirements', {
|
|
142
112
|
projectDescription: memory.projectDescription,
|
|
143
113
|
scope: memory.scope
|
|
144
114
|
});
|
|
@@ -149,7 +119,7 @@ export default async function () {
|
|
|
149
119
|
// 3. Assumptions Clarification
|
|
150
120
|
if (!memory.assumptionsClarified) {
|
|
151
121
|
console.log('--- Assumptions Clarification ---');
|
|
152
|
-
const assumeResult = await agent('assumptions
|
|
122
|
+
const assumeResult = await agent('intake-assumptions', {
|
|
153
123
|
projectDescription: memory.projectDescription,
|
|
154
124
|
scope: memory.scope,
|
|
155
125
|
requirements: memory.requirements
|
|
@@ -161,7 +131,7 @@ export default async function () {
|
|
|
161
131
|
// 4. Security Clarification
|
|
162
132
|
if (!memory.securityClarified) {
|
|
163
133
|
console.log('--- Security Clarification ---');
|
|
164
|
-
const secResult = await agent('security
|
|
134
|
+
const secResult = await agent('intake-security', {
|
|
165
135
|
projectDescription: memory.projectDescription,
|
|
166
136
|
scope: memory.scope,
|
|
167
137
|
requirements: memory.requirements,
|
|
@@ -181,7 +151,7 @@ export default async function () {
|
|
|
181
151
|
if (!memory.roadmapApproved) {
|
|
182
152
|
// Generate roadmap as JSON
|
|
183
153
|
if (!memory.roadmap) {
|
|
184
|
-
const roadmapResult = await agent('roadmap
|
|
154
|
+
const roadmapResult = await agent('plan-roadmap', {
|
|
185
155
|
projectDescription: memory.projectDescription,
|
|
186
156
|
scope: memory.scope,
|
|
187
157
|
requirements: memory.requirements,
|
|
@@ -218,7 +188,7 @@ export default async function () {
|
|
|
218
188
|
} else {
|
|
219
189
|
const feedback = reviewResponse.customText || reviewResponse.text || reviewResponse.raw || reviewRaw;
|
|
220
190
|
// Regenerate roadmap with feedback
|
|
221
|
-
const updatedRoadmap = await agent('roadmap
|
|
191
|
+
const updatedRoadmap = await agent('plan-roadmap', {
|
|
222
192
|
projectDescription: memory.projectDescription,
|
|
223
193
|
scope: memory.scope,
|
|
224
194
|
requirements: memory.requirements,
|
|
@@ -256,7 +226,7 @@ export default async function () {
|
|
|
256
226
|
// Generate task list for this phase (as JSON)
|
|
257
227
|
if (!memory[tasksApprovedKey]) {
|
|
258
228
|
if (!memory[tasksKey]) {
|
|
259
|
-
const taskResult = await agent('
|
|
229
|
+
const taskResult = await agent('plan-tasks', {
|
|
260
230
|
projectDescription: memory.projectDescription,
|
|
261
231
|
scope: memory.scope,
|
|
262
232
|
requirements: memory.requirements,
|
|
@@ -292,7 +262,7 @@ export default async function () {
|
|
|
292
262
|
console.log(`Phase ${i + 1} task list approved!\n`);
|
|
293
263
|
} else {
|
|
294
264
|
const feedback = taskReview.customText || taskReview.text || taskReview.raw || taskReviewRaw;
|
|
295
|
-
const updatedTasks = await agent('
|
|
265
|
+
const updatedTasks = await agent('plan-tasks', {
|
|
296
266
|
projectDescription: memory.projectDescription,
|
|
297
267
|
scope: memory.scope,
|
|
298
268
|
requirements: memory.requirements,
|
|
@@ -347,11 +317,10 @@ export default async function () {
|
|
|
347
317
|
if (stage === TASK_STAGES.PENDING || stage === TASK_STAGES.SECURITY_PRE) {
|
|
348
318
|
if (!getTaskData(i, taskId, 'security_pre')) {
|
|
349
319
|
console.log(' > Security pre-review...');
|
|
350
|
-
const securityPreReview = await agent('security
|
|
320
|
+
const securityPreReview = await agent('pre-code-security', {
|
|
351
321
|
task: task,
|
|
352
322
|
phase: phase,
|
|
353
323
|
scope: memory.scope,
|
|
354
|
-
stage: 'pre-implementation',
|
|
355
324
|
feedback: feedback
|
|
356
325
|
});
|
|
357
326
|
setTaskData(i, taskId, 'security_pre', securityPreReview);
|
|
@@ -364,7 +333,7 @@ export default async function () {
|
|
|
364
333
|
if (stage === TASK_STAGES.TEST_PLANNING) {
|
|
365
334
|
if (!getTaskData(i, taskId, 'tests')) {
|
|
366
335
|
console.log(' > Test planning...');
|
|
367
|
-
const testPlan = await agent('
|
|
336
|
+
const testPlan = await agent('pre-code-tests', {
|
|
368
337
|
task: task,
|
|
369
338
|
phase: phase,
|
|
370
339
|
requirements: memory.requirements,
|
|
@@ -381,22 +350,51 @@ export default async function () {
|
|
|
381
350
|
if (stage === TASK_STAGES.IMPLEMENTING) {
|
|
382
351
|
if (!getTaskData(i, taskId, 'code')) {
|
|
383
352
|
console.log(' > Code implementation...');
|
|
384
|
-
const
|
|
353
|
+
const projectRoot = getCurrentRuntime().workflowConfig.projectRoot;
|
|
354
|
+
const implementation = await agent('code-write', {
|
|
385
355
|
task: task,
|
|
386
356
|
phase: phase,
|
|
387
357
|
requirements: memory.requirements,
|
|
388
358
|
testPlan: getTaskData(i, taskId, 'tests'),
|
|
389
359
|
securityConsiderations: getTaskData(i, taskId, 'security_pre'),
|
|
360
|
+
projectFiles: getProjectFiles(projectRoot),
|
|
390
361
|
feedback: feedback
|
|
391
362
|
});
|
|
392
363
|
setTaskData(i, taskId, 'code', implementation);
|
|
364
|
+
|
|
365
|
+
// Validate project changes after code-write
|
|
366
|
+
const filesWritten = implementation?.implementation?.filesWritten || implementation?.filesWritten || [];
|
|
367
|
+
const modifiedPaths = filesWritten.map(f => f.path);
|
|
368
|
+
const validation = validateProjectChanges(projectRoot, { modified: modifiedPaths });
|
|
369
|
+
|
|
370
|
+
if (!validation.valid) {
|
|
371
|
+
console.log(` > Validation issues: ${validation.violations.join(', ')}`);
|
|
372
|
+
|
|
373
|
+
// Try auto-fix
|
|
374
|
+
const fixed = autoFixValidationIssues(projectRoot, validation.violations);
|
|
375
|
+
if (fixed.length > 0) {
|
|
376
|
+
console.log(` > Auto-fixed: ${fixed.join(', ')}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Re-validate
|
|
380
|
+
const recheck = validateProjectChanges(projectRoot, { modified: modifiedPaths });
|
|
381
|
+
if (!recheck.valid) {
|
|
382
|
+
console.log(' > Auto-fix failed, rolling back package.json...');
|
|
383
|
+
const rollbackResult = rollbackFile(projectRoot, 'package.json');
|
|
384
|
+
if (rollbackResult.success) {
|
|
385
|
+
console.log(' > Rolled back package.json');
|
|
386
|
+
} else {
|
|
387
|
+
console.log(` > Rollback failed: ${rollbackResult.error}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
393
391
|
}
|
|
394
392
|
|
|
395
|
-
//
|
|
393
|
+
// Files are written directly by the agent using native file tools
|
|
396
394
|
const implementation = getTaskData(i, taskId, 'code');
|
|
397
395
|
if (implementation) {
|
|
398
|
-
|
|
399
|
-
|
|
396
|
+
const filesWrittenCount = (implementation?.implementation?.filesWritten || implementation?.filesWritten || []).length;
|
|
397
|
+
console.log(` > Agent wrote ${filesWrittenCount} file(s) to disk`);
|
|
400
398
|
}
|
|
401
399
|
|
|
402
400
|
setTaskStage(i, taskId, TASK_STAGES.CODE_REVIEW);
|
|
@@ -407,14 +405,83 @@ export default async function () {
|
|
|
407
405
|
if (stage === TASK_STAGES.CODE_REVIEW) {
|
|
408
406
|
if (!getTaskData(i, taskId, 'review')) {
|
|
409
407
|
console.log(' > Code review...');
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
408
|
+
const impl = getTaskData(i, taskId, 'code');
|
|
409
|
+
const filesWritten = impl?.implementation?.filesWritten || impl?.filesWritten || [];
|
|
410
|
+
const codeReview = await agent('post-code-review', {
|
|
411
|
+
task: { title: task.title, description: task.description },
|
|
412
|
+
filesToReview: filesWritten.map(f => f.path),
|
|
413
|
+
implementationSummary: impl?.implementation?.summary || impl?.summary || 'See files on disk',
|
|
414
414
|
feedback: feedback
|
|
415
415
|
});
|
|
416
416
|
setTaskData(i, taskId, 'review', codeReview);
|
|
417
417
|
}
|
|
418
|
+
|
|
419
|
+
// Check for code review issues and let user decide
|
|
420
|
+
const review = getTaskData(i, taskId, 'review');
|
|
421
|
+
const hasIssues = review?.issues?.length > 0 || review?.requiredChanges?.length > 0;
|
|
422
|
+
|
|
423
|
+
if (hasIssues && !review.approved && !getTaskData(i, taskId, 'review_decision')) {
|
|
424
|
+
const issuesList = [
|
|
425
|
+
...(review.requiredChanges || []).map(c => `[REQUIRED] ${c}`),
|
|
426
|
+
...(review.issues || [])
|
|
427
|
+
.filter(iss => iss.severity === 'critical' || iss.severity === 'major')
|
|
428
|
+
.map(iss => `[${iss.severity?.toUpperCase() || 'ISSUE'}] ${iss.location || 'general'}: ${iss.description}`)
|
|
429
|
+
];
|
|
430
|
+
|
|
431
|
+
if (issuesList.length > 0) {
|
|
432
|
+
const reviewChoice = createInteraction('choice', `phase-${i + 1}-task-${taskId}-review-issues`, {
|
|
433
|
+
prompt: `Code review issues for "${task.title}":\n\n${issuesList.join('\n')}\n\nHow would you like to proceed?`,
|
|
434
|
+
options: [
|
|
435
|
+
{ key: 'fix', label: 'Fix issues', description: 'Run code-fixer to address these issues' },
|
|
436
|
+
{ key: 'ignore', label: 'Ignore and continue', description: 'Proceed to security review despite issues' },
|
|
437
|
+
{ key: 'reimplement', label: 'Reimplement', description: 'Start implementation from scratch' }
|
|
438
|
+
],
|
|
439
|
+
allowCustom: true
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
const reviewRaw = await askHuman(formatPrompt(reviewChoice), {
|
|
443
|
+
slug: reviewChoice.slug,
|
|
444
|
+
interaction: reviewChoice
|
|
445
|
+
});
|
|
446
|
+
const reviewDecision = await parseResponse(reviewChoice, reviewRaw);
|
|
447
|
+
setTaskData(i, taskId, 'review_decision', reviewDecision.selectedKey);
|
|
448
|
+
|
|
449
|
+
if (reviewDecision.selectedKey === 'fix') {
|
|
450
|
+
console.log(' > Fixing code review issues...');
|
|
451
|
+
const implForFix = getTaskData(i, taskId, 'code');
|
|
452
|
+
const filesForFix = implForFix?.implementation?.filesWritten || implForFix?.filesWritten || [];
|
|
453
|
+
const fixerResult = await agent('post-code-fix', {
|
|
454
|
+
task: { title: task.title },
|
|
455
|
+
reviewIssues: review.issues,
|
|
456
|
+
requiredChanges: review.requiredChanges,
|
|
457
|
+
filePaths: filesForFix.map(f => f.path)
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Agent writes files directly, just update metadata if fixes were applied
|
|
461
|
+
if (fixerResult?.fixesApplied?.length > 0) {
|
|
462
|
+
console.log(` > Fixed ${fixerResult.fixesApplied.length} file(s)`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Clear review to re-run it
|
|
466
|
+
setTaskData(i, taskId, 'review', null);
|
|
467
|
+
setTaskData(i, taskId, 'review_decision', null);
|
|
468
|
+
t--;
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (reviewDecision.selectedKey === 'reimplement') {
|
|
473
|
+
clearPartialTaskData(i, taskId, ['security_pre', 'tests']);
|
|
474
|
+
setTaskData(i, taskId, 'review', null);
|
|
475
|
+
setTaskData(i, taskId, 'review_decision', null);
|
|
476
|
+
setTaskStage(i, taskId, TASK_STAGES.IMPLEMENTING);
|
|
477
|
+
t--;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// 'ignore' falls through to next stage
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
418
485
|
setTaskStage(i, taskId, TASK_STAGES.SECURITY_POST);
|
|
419
486
|
stage = TASK_STAGES.SECURITY_POST;
|
|
420
487
|
}
|
|
@@ -423,11 +490,11 @@ export default async function () {
|
|
|
423
490
|
if (stage === TASK_STAGES.SECURITY_POST) {
|
|
424
491
|
if (!getTaskData(i, taskId, 'security_post')) {
|
|
425
492
|
console.log(' > Final security check...');
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
493
|
+
const implForSecurity = getTaskData(i, taskId, 'code');
|
|
494
|
+
const filesForSecurity = implForSecurity?.implementation?.filesWritten || implForSecurity?.filesWritten || [];
|
|
495
|
+
const securityPostReview = await agent('post-code-security', {
|
|
496
|
+
task: { title: task.title, description: task.description },
|
|
497
|
+
filePaths: filesForSecurity.map(f => f.path),
|
|
431
498
|
feedback: feedback
|
|
432
499
|
});
|
|
433
500
|
setTaskData(i, taskId, 'security_post', securityPostReview);
|
|
@@ -439,10 +506,11 @@ export default async function () {
|
|
|
439
506
|
// 6. Sanity check generation & execution
|
|
440
507
|
if (stage === TASK_STAGES.SANITY_CHECK) {
|
|
441
508
|
const testFramework = detectTestFramework();
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
509
|
+
const implForSanity = getTaskData(i, taskId, 'code');
|
|
510
|
+
const filesForSanity = implForSanity?.implementation?.filesWritten || implForSanity?.filesWritten || [];
|
|
511
|
+
const executableChecks = await agent('verify-sanity', {
|
|
512
|
+
task: { title: task.title, sanityCheck: task.sanityCheck },
|
|
513
|
+
filePaths: filesForSanity.map(f => f.path),
|
|
446
514
|
testFramework
|
|
447
515
|
});
|
|
448
516
|
setTaskData(i, taskId, 'sanity_checks', executableChecks);
|
|
@@ -478,10 +546,11 @@ export default async function () {
|
|
|
478
546
|
const action = sanityResponse.selectedKey;
|
|
479
547
|
|
|
480
548
|
if (action === 'auto') {
|
|
481
|
-
const results = await
|
|
549
|
+
const results = await runSanityChecks({
|
|
482
550
|
checks: executableChecks.checks,
|
|
483
551
|
setup: executableChecks.setup,
|
|
484
|
-
teardown: executableChecks.teardown
|
|
552
|
+
teardown: executableChecks.teardown,
|
|
553
|
+
_config: { projectRoot: getCurrentRuntime().workflowConfig.projectRoot }
|
|
485
554
|
});
|
|
486
555
|
setTaskData(i, taskId, 'sanity_results', results);
|
|
487
556
|
|
|
@@ -533,30 +602,21 @@ export default async function () {
|
|
|
533
602
|
|
|
534
603
|
if (failResponse.selectedKey === 'quickfix') {
|
|
535
604
|
console.log(' > Running quick fix...');
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
},
|
|
544
|
-
testPlan: getTaskData(i, taskId, 'tests'),
|
|
605
|
+
const implForQuickFix = getTaskData(i, taskId, 'code');
|
|
606
|
+
const filesForQuickFix = implForQuickFix?.implementation?.filesWritten || implForQuickFix?.filesWritten || [];
|
|
607
|
+
const failedChecksList = results.results.filter(r => r.status === 'failed');
|
|
608
|
+
const fixerResult = await agent('post-code-fix', {
|
|
609
|
+
task: { title: task.title },
|
|
610
|
+
failedChecks: failedChecksList.map(c => ({ id: c.id, error: c.error, output: c.output })),
|
|
611
|
+
filePaths: filesForQuickFix.map(f => f.path),
|
|
545
612
|
previousAttempts: quickFixAttempts
|
|
546
613
|
});
|
|
547
614
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
.
|
|
551
|
-
.map((fix) => ({ path: fix.path, code: fix.code }));
|
|
552
|
-
|
|
553
|
-
if (fixFiles.length > 0) {
|
|
554
|
-
console.log(' > Applying fixes to disk...');
|
|
555
|
-
writeImplementationFiles({ files: fixFiles });
|
|
615
|
+
// Agent writes files directly
|
|
616
|
+
if (fixerResult?.fixesApplied?.length > 0) {
|
|
617
|
+
console.log(` > Fixed ${fixerResult.fixesApplied.length} file(s)`);
|
|
556
618
|
}
|
|
557
619
|
|
|
558
|
-
const updatedImplementation = applyFixesToImplementation(getTaskData(i, taskId, 'code'), fixes);
|
|
559
|
-
setTaskData(i, taskId, 'code', updatedImplementation);
|
|
560
620
|
incrementQuickFixAttempts(i, taskId);
|
|
561
621
|
setTaskData(i, taskId, 'sanity_checks', null);
|
|
562
622
|
setTaskData(i, taskId, 'sanity_results', null);
|
|
@@ -590,6 +650,32 @@ export default async function () {
|
|
|
590
650
|
task.stage = 'completed';
|
|
591
651
|
memory[tasksKey] = tasks;
|
|
592
652
|
writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, tasks));
|
|
653
|
+
|
|
654
|
+
// Git commit for completed task
|
|
655
|
+
const implForCommit = getTaskData(i, taskId, 'code');
|
|
656
|
+
const filesForCommit = implForCommit?.implementation?.filesWritten || implForCommit?.filesWritten || [];
|
|
657
|
+
if (filesForCommit.length > 0) {
|
|
658
|
+
console.log(' > Generating commit message...');
|
|
659
|
+
const commitMsg = await agent('verify-commit-msg', {
|
|
660
|
+
task: { title: task.title, description: task.description },
|
|
661
|
+
filesWritten: filesForCommit
|
|
662
|
+
});
|
|
663
|
+
const runtime = getCurrentRuntime();
|
|
664
|
+
const fullMessage = commitMsg.scope
|
|
665
|
+
? `${commitMsg.type}(${commitMsg.scope}): ${commitMsg.message}\n\n${commitMsg.body || ''}`
|
|
666
|
+
: `${commitMsg.type}: ${commitMsg.message}\n\n${commitMsg.body || ''}`;
|
|
667
|
+
const commitResult = createGitCommit(
|
|
668
|
+
runtime.workflowConfig.projectRoot,
|
|
669
|
+
fullMessage.trim(),
|
|
670
|
+
filesForCommit.map(f => f.path)
|
|
671
|
+
);
|
|
672
|
+
if (commitResult.success) {
|
|
673
|
+
console.log(` > Committed: ${commitResult.hash}`);
|
|
674
|
+
} else {
|
|
675
|
+
console.log(` > Commit skipped: ${commitResult.error}`);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
593
679
|
console.log(` Task ${t + 1} confirmed complete!\n`);
|
|
594
680
|
} else if (action === 'skip') {
|
|
595
681
|
resetQuickFixAttempts(i, taskId);
|
|
@@ -598,6 +684,32 @@ export default async function () {
|
|
|
598
684
|
task.stage = 'completed';
|
|
599
685
|
memory[tasksKey] = tasks;
|
|
600
686
|
writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, tasks));
|
|
687
|
+
|
|
688
|
+
// Git commit for completed task (skipped verification)
|
|
689
|
+
const implForCommitSkip = getTaskData(i, taskId, 'code');
|
|
690
|
+
const filesForCommitSkip = implForCommitSkip?.implementation?.filesWritten || implForCommitSkip?.filesWritten || [];
|
|
691
|
+
if (filesForCommitSkip.length > 0) {
|
|
692
|
+
console.log(' > Generating commit message...');
|
|
693
|
+
const commitMsgSkip = await agent('verify-commit-msg', {
|
|
694
|
+
task: { title: task.title, description: task.description },
|
|
695
|
+
filesWritten: filesForCommitSkip
|
|
696
|
+
});
|
|
697
|
+
const runtimeSkip = getCurrentRuntime();
|
|
698
|
+
const fullMessageSkip = commitMsgSkip.scope
|
|
699
|
+
? `${commitMsgSkip.type}(${commitMsgSkip.scope}): ${commitMsgSkip.message}\n\n${commitMsgSkip.body || ''}`
|
|
700
|
+
: `${commitMsgSkip.type}: ${commitMsgSkip.message}\n\n${commitMsgSkip.body || ''}`;
|
|
701
|
+
const commitResultSkip = createGitCommit(
|
|
702
|
+
runtimeSkip.workflowConfig.projectRoot,
|
|
703
|
+
fullMessageSkip.trim(),
|
|
704
|
+
filesForCommitSkip.map(f => f.path)
|
|
705
|
+
);
|
|
706
|
+
if (commitResultSkip.success) {
|
|
707
|
+
console.log(` > Committed: ${commitResultSkip.hash}`);
|
|
708
|
+
} else {
|
|
709
|
+
console.log(` > Commit skipped: ${commitResultSkip.error}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
601
713
|
console.log(` Task ${t + 1} confirmed complete!\n`);
|
|
602
714
|
} else {
|
|
603
715
|
setTaskStage(i, taskId, TASK_STAGES.AWAITING_APPROVAL);
|
|
@@ -628,6 +740,32 @@ export default async function () {
|
|
|
628
740
|
task.stage = 'completed';
|
|
629
741
|
memory[tasksKey] = tasks;
|
|
630
742
|
writeMarkdownFile(STATE_DIR, `phase-${i + 1}-tasks.md`, renderTasksMarkdown(i + 1, phase.title, tasks));
|
|
743
|
+
|
|
744
|
+
// Git commit for manually approved task
|
|
745
|
+
const implForCommitManual = getTaskData(i, taskId, 'code');
|
|
746
|
+
const filesForCommitManual = implForCommitManual?.implementation?.filesWritten || implForCommitManual?.filesWritten || [];
|
|
747
|
+
if (filesForCommitManual.length > 0) {
|
|
748
|
+
console.log(' > Generating commit message...');
|
|
749
|
+
const commitMsgManual = await agent('verify-commit-msg', {
|
|
750
|
+
task: { title: task.title, description: task.description },
|
|
751
|
+
filesWritten: filesForCommitManual
|
|
752
|
+
});
|
|
753
|
+
const runtimeManual = getCurrentRuntime();
|
|
754
|
+
const fullMessageManual = commitMsgManual.scope
|
|
755
|
+
? `${commitMsgManual.type}(${commitMsgManual.scope}): ${commitMsgManual.message}\n\n${commitMsgManual.body || ''}`
|
|
756
|
+
: `${commitMsgManual.type}: ${commitMsgManual.message}\n\n${commitMsgManual.body || ''}`;
|
|
757
|
+
const commitResultManual = createGitCommit(
|
|
758
|
+
runtimeManual.workflowConfig.projectRoot,
|
|
759
|
+
fullMessageManual.trim(),
|
|
760
|
+
filesForCommitManual.map(f => f.path)
|
|
761
|
+
);
|
|
762
|
+
if (commitResultManual.success) {
|
|
763
|
+
console.log(` > Committed: ${commitResultManual.hash}`);
|
|
764
|
+
} else {
|
|
765
|
+
console.log(` > Commit skipped: ${commitResultManual.error}`);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
631
769
|
console.log(` Task ${t + 1} confirmed complete!\n`);
|
|
632
770
|
} else {
|
|
633
771
|
console.log(' > Issue flagged, reprocessing task with feedback...');
|