task-o-matic 0.0.12 → 0.0.14
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/init.js +41 -8
- package/dist/commands/tasks/create.d.ts.map +1 -1
- package/dist/commands/tasks/create.js +6 -13
- package/dist/commands/tasks/document/add.d.ts +3 -0
- package/dist/commands/tasks/document/add.d.ts.map +1 -0
- package/dist/commands/tasks/document/add.js +35 -0
- package/dist/commands/tasks/document/analyze.d.ts +3 -0
- package/dist/commands/tasks/document/analyze.d.ts.map +1 -0
- package/dist/commands/tasks/document/analyze.js +49 -0
- package/dist/commands/tasks/document/get.d.ts +3 -0
- package/dist/commands/tasks/document/get.d.ts.map +1 -0
- package/dist/commands/tasks/document/get.js +29 -0
- package/dist/commands/tasks/document/index.d.ts +8 -0
- package/dist/commands/tasks/document/index.d.ts.map +1 -0
- package/dist/commands/tasks/document/index.js +13 -0
- package/dist/commands/tasks/enhance.d.ts.map +1 -1
- package/dist/commands/tasks/enhance.js +64 -61
- package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
- package/dist/commands/tasks/execute-loop.js +64 -90
- package/dist/commands/tasks/execute.d.ts.map +1 -1
- package/dist/commands/tasks/execute.js +52 -16
- package/dist/commands/tasks/list.js +2 -2
- package/dist/commands/tasks/next.js +4 -4
- package/dist/commands/tasks/plan/create.d.ts +3 -0
- package/dist/commands/tasks/plan/create.d.ts.map +1 -0
- package/dist/commands/tasks/plan/create.js +37 -0
- package/dist/commands/tasks/plan/delete.d.ts +3 -0
- package/dist/commands/tasks/plan/delete.d.ts.map +1 -0
- package/dist/commands/tasks/plan/delete.js +14 -0
- package/dist/commands/tasks/plan/get.d.ts +3 -0
- package/dist/commands/tasks/plan/get.d.ts.map +1 -0
- package/dist/commands/tasks/plan/get.js +24 -0
- package/dist/commands/tasks/plan/index.d.ts +10 -0
- package/dist/commands/tasks/plan/index.d.ts.map +1 -0
- package/dist/commands/tasks/plan/index.js +17 -0
- package/dist/commands/tasks/plan/list.d.ts +3 -0
- package/dist/commands/tasks/plan/list.d.ts.map +1 -0
- package/dist/commands/tasks/plan/list.js +21 -0
- package/dist/commands/tasks/plan/set.d.ts +3 -0
- package/dist/commands/tasks/plan/set.d.ts.map +1 -0
- package/dist/commands/tasks/plan/set.js +33 -0
- package/dist/commands/tasks/split.d.ts.map +1 -1
- package/dist/commands/tasks/split.js +65 -60
- package/dist/commands/tasks/status.js +2 -2
- package/dist/lib/ai-service/ai-operations.d.ts +1 -1
- package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/base-operations.d.ts +22 -0
- package/dist/lib/ai-service/base-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/base-operations.js +29 -1
- package/dist/lib/ai-service/task-operations.d.ts +1 -1
- package/dist/lib/ai-service/task-operations.d.ts.map +1 -1
- package/dist/lib/better-t-stack-cli.d.ts +36 -21
- package/dist/lib/better-t-stack-cli.d.ts.map +1 -1
- package/dist/lib/better-t-stack-cli.js +212 -33
- package/dist/lib/bootstrap/cli-bootstrap.d.ts +14 -0
- package/dist/lib/bootstrap/cli-bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap/cli-bootstrap.js +325 -0
- package/dist/lib/bootstrap/index.d.ts +4 -0
- package/dist/lib/bootstrap/index.d.ts.map +1 -0
- package/dist/lib/bootstrap/index.js +19 -0
- package/dist/lib/bootstrap/medusa-bootstrap.d.ts +14 -0
- package/dist/lib/bootstrap/medusa-bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap/medusa-bootstrap.js +218 -0
- package/dist/lib/bootstrap/opentui-bootstrap.d.ts +11 -0
- package/dist/lib/bootstrap/opentui-bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap/opentui-bootstrap.js +342 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +18 -0
- package/dist/lib/git-utils.d.ts +45 -0
- package/dist/lib/git-utils.d.ts.map +1 -0
- package/dist/lib/git-utils.js +160 -0
- package/dist/lib/task-execution-core.d.ts +7 -0
- package/dist/lib/task-execution-core.d.ts.map +1 -0
- package/dist/lib/task-execution-core.js +360 -0
- package/dist/lib/task-execution.d.ts +4 -0
- package/dist/lib/task-execution.d.ts.map +1 -1
- package/dist/lib/task-execution.js +31 -149
- package/dist/lib/task-loop-execution.d.ts +1 -19
- package/dist/lib/task-loop-execution.d.ts.map +1 -1
- package/dist/lib/task-loop-execution.js +50 -585
- package/dist/lib/task-planning.d.ts +28 -0
- package/dist/lib/task-planning.d.ts.map +1 -0
- package/dist/lib/task-planning.js +109 -0
- package/dist/lib/task-review.d.ts +27 -0
- package/dist/lib/task-review.d.ts.map +1 -0
- package/dist/lib/task-review.js +106 -0
- package/dist/lib/validation.d.ts +20 -3
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +39 -10
- package/dist/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +18 -45
- package/dist/services/tasks.d.ts +2 -2
- package/dist/services/tasks.d.ts.map +1 -1
- package/dist/services/tasks.js +55 -85
- package/dist/test/task-loop-git.test.js +6 -6
- package/dist/types/cli-options.d.ts +138 -0
- package/dist/types/cli-options.d.ts.map +1 -0
- package/dist/types/cli-options.js +6 -0
- package/dist/types/index.d.ts +74 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/results.d.ts +60 -6
- package/dist/types/results.d.ts.map +1 -1
- package/dist/utils/bulk-operations.d.ts +51 -0
- package/dist/utils/bulk-operations.d.ts.map +1 -0
- package/dist/utils/bulk-operations.js +68 -0
- package/dist/utils/cli-validators.d.ts +54 -0
- package/dist/utils/cli-validators.d.ts.map +1 -0
- package/dist/utils/cli-validators.js +75 -0
- package/dist/utils/command-error-handler.d.ts +32 -0
- package/dist/utils/command-error-handler.d.ts.map +1 -0
- package/dist/utils/command-error-handler.js +52 -0
- package/dist/utils/confirmation.d.ts +19 -0
- package/dist/utils/confirmation.d.ts.map +1 -0
- package/dist/utils/confirmation.js +39 -0
- package/dist/utils/display-helpers.d.ts +81 -0
- package/dist/utils/display-helpers.d.ts.map +1 -0
- package/dist/utils/display-helpers.js +109 -0
- package/dist/utils/error-utils.d.ts +70 -0
- package/dist/utils/error-utils.d.ts.map +1 -0
- package/dist/utils/error-utils.js +103 -0
- package/dist/utils/file-utils.d.ts +49 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +77 -0
- package/dist/utils/id-generator.d.ts +92 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +140 -0
- package/dist/utils/model-executor-parser.d.ts +38 -0
- package/dist/utils/model-executor-parser.d.ts.map +1 -0
- package/dist/utils/model-executor-parser.js +67 -0
- package/dist/utils/progress-tracking.d.ts +28 -0
- package/dist/utils/progress-tracking.d.ts.map +1 -0
- package/dist/utils/progress-tracking.js +43 -0
- package/dist/utils/stack-formatter.d.ts +2 -1
- package/dist/utils/stack-formatter.d.ts.map +1 -1
- package/dist/utils/stack-formatter.js +8 -2
- package/dist/utils/storage-utils.d.ts +49 -0
- package/dist/utils/storage-utils.d.ts.map +1 -0
- package/dist/utils/storage-utils.js +79 -0
- package/dist/utils/streaming-utils.d.ts +38 -0
- package/dist/utils/streaming-utils.d.ts.map +1 -0
- package/dist/utils/streaming-utils.js +56 -0
- package/docs/agents/cli.md +58 -149
- package/package.json +1 -1
- package/dist/commands/tasks/document.d.ts +0 -5
- package/dist/commands/tasks/document.d.ts.map +0 -1
- package/dist/commands/tasks/document.js +0 -118
- package/dist/commands/tasks/plan.d.ts +0 -7
- package/dist/commands/tasks/plan.d.ts.map +0 -1
- package/dist/commands/tasks/plan.js +0 -131
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.executeTaskCore = executeTaskCore;
|
|
7
|
+
const tasks_1 = require("../services/tasks");
|
|
8
|
+
const executor_factory_1 = require("./executors/executor-factory");
|
|
9
|
+
const validation_1 = require("./validation");
|
|
10
|
+
const ai_service_factory_1 = require("../utils/ai-service-factory");
|
|
11
|
+
const hooks_1 = require("./hooks");
|
|
12
|
+
const prompt_builder_1 = require("./prompt-builder");
|
|
13
|
+
const task_planning_1 = require("./task-planning");
|
|
14
|
+
const task_review_1 = require("./task-review");
|
|
15
|
+
const git_utils_1 = require("./git-utils");
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
/**
|
|
18
|
+
* Execute a single task with all features (retry, planning, review, etc.)
|
|
19
|
+
* This is the core unified execution logic used by both execute and execute-loop commands
|
|
20
|
+
*/
|
|
21
|
+
async function executeTaskCore(taskId, config) {
|
|
22
|
+
const { tool, executorConfig, customMessage, verificationCommands = [], enableRetry = false, maxRetries = 3, tryModels, enablePlanPhase = false, planModel, reviewPlan = false, enableReviewPhase = false, reviewModel, autoCommit: enableAutoCommit = false, executeSubtasks = true, dry = false, } = config;
|
|
23
|
+
// Load task
|
|
24
|
+
const task = await tasks_1.taskService.getTask(taskId);
|
|
25
|
+
if (!task) {
|
|
26
|
+
throw new Error(`Task with ID ${taskId} not found`);
|
|
27
|
+
}
|
|
28
|
+
// Check if task has subtasks and should execute them recursively
|
|
29
|
+
if (executeSubtasks && !customMessage) {
|
|
30
|
+
const subtasks = await tasks_1.taskService.getSubtasks(taskId);
|
|
31
|
+
if (subtasks.length > 0) {
|
|
32
|
+
return await executeTaskWithSubtasks(task, subtasks, config);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// ----------------------------------------------------------------------
|
|
36
|
+
// PLANNING PHASE
|
|
37
|
+
// ----------------------------------------------------------------------
|
|
38
|
+
let planContent;
|
|
39
|
+
if (enablePlanPhase) {
|
|
40
|
+
const planningResult = await (0, task_planning_1.executePlanningPhase)(task, tool, {
|
|
41
|
+
planModel,
|
|
42
|
+
reviewPlan,
|
|
43
|
+
autoCommit: enableAutoCommit,
|
|
44
|
+
dry,
|
|
45
|
+
});
|
|
46
|
+
if (planningResult.success && planningResult.planContent) {
|
|
47
|
+
planContent = planningResult.planContent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ----------------------------------------------------------------------
|
|
51
|
+
// EXECUTION PHASE (with optional retry logic)
|
|
52
|
+
// ----------------------------------------------------------------------
|
|
53
|
+
const attempts = [];
|
|
54
|
+
if (enableRetry) {
|
|
55
|
+
// Execute with retry logic
|
|
56
|
+
return await executeTaskWithRetry(task, config, attempts, planContent);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Execute once without retry (simpler path for execute command)
|
|
60
|
+
return await executeSingleAttempt(task, config, attempts, planContent, 1, maxRetries);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Execute task with subtasks recursively
|
|
65
|
+
*/
|
|
66
|
+
async function executeTaskWithSubtasks(task, subtasks, config) {
|
|
67
|
+
const { dry } = config;
|
|
68
|
+
console.log(chalk_1.default.blue(`📋 Task has ${subtasks.length} subtasks, executing recursively...`));
|
|
69
|
+
const subtaskResults = [];
|
|
70
|
+
let allSuccess = true;
|
|
71
|
+
// Execute subtasks one by one
|
|
72
|
+
for (let i = 0; i < subtasks.length; i++) {
|
|
73
|
+
const subtask = subtasks[i];
|
|
74
|
+
console.log(chalk_1.default.cyan(`\n[${i + 1}/${subtasks.length}] Executing subtask: ${subtask.title} (${subtask.id})`));
|
|
75
|
+
try {
|
|
76
|
+
const result = await executeTaskCore(subtask.id, config);
|
|
77
|
+
subtaskResults.push(result);
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
allSuccess = false;
|
|
80
|
+
console.error(chalk_1.default.red(`❌ Failed to execute subtask ${subtask.id}: ${subtask.title}`));
|
|
81
|
+
break; // Stop on first failure
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
allSuccess = false;
|
|
86
|
+
console.error(chalk_1.default.red(`❌ Failed to execute subtask ${subtask.id}: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// After all subtasks are done (or on failure), update the main task status
|
|
91
|
+
if (!dry) {
|
|
92
|
+
if (allSuccess) {
|
|
93
|
+
await tasks_1.taskService.setTaskStatus(task.id, "completed");
|
|
94
|
+
console.log(chalk_1.default.green(`✅ Main task ${task.title} completed after all subtasks`));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
98
|
+
console.log(chalk_1.default.red(`❌ Main task ${task.title} failed due to subtask failure, status reset to todo`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
success: allSuccess,
|
|
103
|
+
attempts: [], // Parent task doesn't have its own attempts
|
|
104
|
+
subtaskResults,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Execute task with retry logic
|
|
109
|
+
*/
|
|
110
|
+
async function executeTaskWithRetry(task, config, attempts, planContent) {
|
|
111
|
+
const { tool, maxRetries = 3, tryModels, verificationCommands = [], enableReviewPhase = false, reviewModel, autoCommit: enableAutoCommit = false, dry = false, } = config;
|
|
112
|
+
let currentAttempt = 1;
|
|
113
|
+
let lastError;
|
|
114
|
+
while (currentAttempt <= maxRetries) {
|
|
115
|
+
// Determine which executor and model to use for this attempt
|
|
116
|
+
let currentExecutor = tool;
|
|
117
|
+
let currentModel;
|
|
118
|
+
if (tryModels && tryModels.length > 0) {
|
|
119
|
+
const modelConfigIndex = Math.min(currentAttempt - 1, tryModels.length - 1);
|
|
120
|
+
const modelConfig = tryModels[modelConfigIndex];
|
|
121
|
+
if (modelConfig.executor) {
|
|
122
|
+
currentExecutor = modelConfig.executor;
|
|
123
|
+
}
|
|
124
|
+
if (modelConfig.model) {
|
|
125
|
+
currentModel = modelConfig.model;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log(chalk_1.default.blue(`\n🎯 Attempt ${currentAttempt}/${maxRetries} for task: ${task.title} (${task.id})`));
|
|
129
|
+
if (currentModel) {
|
|
130
|
+
console.log(chalk_1.default.cyan(` Using executor: ${currentExecutor} with model: ${currentModel}`));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log(chalk_1.default.cyan(` Using executor: ${currentExecutor}`));
|
|
134
|
+
}
|
|
135
|
+
// Build retry context if this is a retry attempt
|
|
136
|
+
let retryContext = "";
|
|
137
|
+
if (currentAttempt > 1 && lastError) {
|
|
138
|
+
const retryParts = [];
|
|
139
|
+
retryParts.push(`# RETRY ATTEMPT ${currentAttempt}/${maxRetries}\n\n`);
|
|
140
|
+
if (currentModel) {
|
|
141
|
+
retryParts.push(`**Note**: You are ${currentExecutor} using the ${currentModel} model. This is a more capable model than the previous attempt.\n\n`);
|
|
142
|
+
}
|
|
143
|
+
retryParts.push(`## Previous Attempt Failed With Error:\n\n${lastError}\n\n`);
|
|
144
|
+
retryParts.push(`Please analyze the error carefully and fix it. The error might be due to:\n`);
|
|
145
|
+
retryParts.push(`- Syntax errors\n`);
|
|
146
|
+
retryParts.push(`- Logic errors\n`);
|
|
147
|
+
retryParts.push(`- Missing dependencies or imports\n`);
|
|
148
|
+
retryParts.push(`- Incorrect configuration\n`);
|
|
149
|
+
retryParts.push(`- Build or test failures\n\n`);
|
|
150
|
+
retryParts.push(`Please fix the error above and complete the task successfully.\n\n`);
|
|
151
|
+
retryContext = retryParts.join("");
|
|
152
|
+
}
|
|
153
|
+
// Update executor config for this attempt
|
|
154
|
+
const attemptConfig = {
|
|
155
|
+
...config,
|
|
156
|
+
tool: currentExecutor,
|
|
157
|
+
executorConfig: {
|
|
158
|
+
...config.executorConfig,
|
|
159
|
+
model: currentModel,
|
|
160
|
+
continueLastSession: currentAttempt > 1, // Resume session on retries
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
try {
|
|
164
|
+
const result = await executeSingleAttempt(task, attemptConfig, attempts, planContent, currentAttempt, maxRetries, retryContext);
|
|
165
|
+
// Check if all verifications passed
|
|
166
|
+
const allVerificationsPassed = result.attempts[result.attempts.length - 1]
|
|
167
|
+
?.verificationResults?.every((r) => r.success) ?? true;
|
|
168
|
+
if (!allVerificationsPassed) {
|
|
169
|
+
const failedVerification = result.attempts[result.attempts.length - 1]
|
|
170
|
+
?.verificationResults?.find((r) => !r.success);
|
|
171
|
+
lastError = `Verification command "${failedVerification?.command}" failed:\n${failedVerification?.error}`;
|
|
172
|
+
currentAttempt++;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// ----------------------------------------------------------------------
|
|
176
|
+
// AI REVIEW PHASE
|
|
177
|
+
// ----------------------------------------------------------------------
|
|
178
|
+
if (enableReviewPhase && !dry) {
|
|
179
|
+
const reviewResult = await (0, task_review_1.executeReviewPhase)(task, {
|
|
180
|
+
reviewModel,
|
|
181
|
+
planContent,
|
|
182
|
+
dry,
|
|
183
|
+
});
|
|
184
|
+
if (!reviewResult.approved) {
|
|
185
|
+
lastError = `AI Review Failed:\n${reviewResult.feedback}`;
|
|
186
|
+
console.log(chalk_1.default.red(`❌ AI Review Rejected Changes: ${reviewResult.feedback}`));
|
|
187
|
+
currentAttempt++;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
console.log(chalk_1.default.green(`✅ AI Review Approved: ${reviewResult.feedback}`));
|
|
192
|
+
result.reviewFeedback = reviewResult.feedback;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Success! Return the result
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
200
|
+
console.log(chalk_1.default.red(`❌ Task execution failed on attempt ${currentAttempt}: ${lastError}`));
|
|
201
|
+
if (!dry && currentAttempt < maxRetries) {
|
|
202
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
203
|
+
console.log(chalk_1.default.yellow("⏸ Task status reset to todo for retry"));
|
|
204
|
+
}
|
|
205
|
+
currentAttempt++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// All retries exhausted
|
|
209
|
+
if (!dry) {
|
|
210
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
211
|
+
console.log(chalk_1.default.red("❌ All retry attempts exhausted, task status reset to todo"));
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
attempts,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Execute a single attempt of the task
|
|
220
|
+
*/
|
|
221
|
+
async function executeSingleAttempt(task, config, attempts, planContent, attemptNumber, maxRetries, retryContext) {
|
|
222
|
+
const { tool, executorConfig, customMessage, verificationCommands = [], autoCommit: enableAutoCommit = false, dry = false, } = config;
|
|
223
|
+
const attemptStartTime = Date.now();
|
|
224
|
+
console.log(chalk_1.default.blue(`🎯 ${dry ? "DRY RUN" : "Executing"} task: ${task.title} (${task.id})`));
|
|
225
|
+
// Capture git state before execution
|
|
226
|
+
const gitStateBefore = await (0, git_utils_1.captureGitState)();
|
|
227
|
+
// Build execution message
|
|
228
|
+
let executionMessage;
|
|
229
|
+
if (customMessage) {
|
|
230
|
+
// Use custom message override
|
|
231
|
+
executionMessage = customMessage;
|
|
232
|
+
console.log(chalk_1.default.cyan("📝 Using custom execution message"));
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Build comprehensive execution message with full context
|
|
236
|
+
const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
|
|
237
|
+
const taskContext = await contextBuilder.buildContext(task.id);
|
|
238
|
+
// Get task plan if available
|
|
239
|
+
const storedPlanData = await tasks_1.taskService.getTaskPlan(task.id);
|
|
240
|
+
let finalPlan;
|
|
241
|
+
if (planContent) {
|
|
242
|
+
finalPlan = `${planContent}\n\nPlease follow this plan to implement the task.`;
|
|
243
|
+
}
|
|
244
|
+
else if (storedPlanData) {
|
|
245
|
+
finalPlan = storedPlanData.plan;
|
|
246
|
+
}
|
|
247
|
+
// Build execution prompt using PromptBuilder
|
|
248
|
+
const promptResult = prompt_builder_1.PromptBuilder.buildExecutionPrompt({
|
|
249
|
+
taskTitle: task.title,
|
|
250
|
+
taskDescription: task.description,
|
|
251
|
+
taskPlan: finalPlan,
|
|
252
|
+
stack: taskContext.stack,
|
|
253
|
+
documentation: taskContext.documentation,
|
|
254
|
+
retryContext,
|
|
255
|
+
});
|
|
256
|
+
if (!promptResult.success) {
|
|
257
|
+
throw new Error(`Failed to build execution prompt: ${promptResult.error}`);
|
|
258
|
+
}
|
|
259
|
+
executionMessage = promptResult.prompt;
|
|
260
|
+
}
|
|
261
|
+
// Update task status to in-progress
|
|
262
|
+
if (!dry) {
|
|
263
|
+
await tasks_1.taskService.setTaskStatus(task.id, "in-progress");
|
|
264
|
+
console.log(chalk_1.default.yellow("⏳ Task status updated to in-progress"));
|
|
265
|
+
}
|
|
266
|
+
// Emit execution:start event
|
|
267
|
+
await hooks_1.hooks.emit("execution:start", { taskId: task.id, tool });
|
|
268
|
+
try {
|
|
269
|
+
// Create executor and run
|
|
270
|
+
const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
|
|
271
|
+
// Log session resumption if applicable
|
|
272
|
+
if (executorConfig?.continueLastSession && attemptNumber > 1) {
|
|
273
|
+
console.log(chalk_1.default.cyan("🔄 Resuming previous session to provide error feedback to AI"));
|
|
274
|
+
}
|
|
275
|
+
await executor.execute(executionMessage, dry, executorConfig);
|
|
276
|
+
// Run verification commands
|
|
277
|
+
const verificationResults = await (0, validation_1.runValidations)(verificationCommands, dry);
|
|
278
|
+
const allVerificationsPassed = verificationResults.every((r) => r.success);
|
|
279
|
+
if (!allVerificationsPassed) {
|
|
280
|
+
const failedVerification = verificationResults.find((r) => !r.success);
|
|
281
|
+
const error = `Verification command "${failedVerification?.command}" failed:\n${failedVerification?.error}`;
|
|
282
|
+
attempts.push({
|
|
283
|
+
attemptNumber,
|
|
284
|
+
success: false,
|
|
285
|
+
error,
|
|
286
|
+
executor: tool,
|
|
287
|
+
model: executorConfig?.model,
|
|
288
|
+
verificationResults,
|
|
289
|
+
timestamp: Date.now() - attemptStartTime,
|
|
290
|
+
});
|
|
291
|
+
console.log(chalk_1.default.red(`❌ Task execution failed verification on attempt ${attemptNumber}`));
|
|
292
|
+
throw new Error(error);
|
|
293
|
+
}
|
|
294
|
+
// Extract commit info if enabled
|
|
295
|
+
let commitInfo;
|
|
296
|
+
if (enableAutoCommit && !dry) {
|
|
297
|
+
console.log(chalk_1.default.blue("📝 Extracting commit information..."));
|
|
298
|
+
const gitStateAfter = await (0, git_utils_1.captureGitState)();
|
|
299
|
+
const gitState = {
|
|
300
|
+
beforeHead: gitStateBefore.beforeHead || "",
|
|
301
|
+
afterHead: gitStateAfter.afterHead || "",
|
|
302
|
+
hasUncommittedChanges: gitStateAfter.hasUncommittedChanges || false,
|
|
303
|
+
};
|
|
304
|
+
commitInfo = await (0, git_utils_1.extractCommitInfo)(task.id, task.title, executionMessage, gitState);
|
|
305
|
+
console.log(chalk_1.default.green(`✅ Commit message: ${commitInfo.message}`));
|
|
306
|
+
if (commitInfo.files.length > 0) {
|
|
307
|
+
console.log(chalk_1.default.green(`📁 Changed files: ${commitInfo.files.join(", ")}`));
|
|
308
|
+
}
|
|
309
|
+
// Auto-commit the changes
|
|
310
|
+
await (0, git_utils_1.autoCommit)(commitInfo);
|
|
311
|
+
}
|
|
312
|
+
// Update task status to completed
|
|
313
|
+
if (!dry) {
|
|
314
|
+
await tasks_1.taskService.setTaskStatus(task.id, "completed");
|
|
315
|
+
console.log(chalk_1.default.green("✅ Task execution completed successfully"));
|
|
316
|
+
}
|
|
317
|
+
// Record successful attempt
|
|
318
|
+
attempts.push({
|
|
319
|
+
attemptNumber,
|
|
320
|
+
success: true,
|
|
321
|
+
executor: tool,
|
|
322
|
+
model: executorConfig?.model,
|
|
323
|
+
verificationResults,
|
|
324
|
+
commitInfo,
|
|
325
|
+
timestamp: Date.now() - attemptStartTime,
|
|
326
|
+
});
|
|
327
|
+
// Emit execution:end event
|
|
328
|
+
await hooks_1.hooks.emit("execution:end", { taskId: task.id, success: true });
|
|
329
|
+
return {
|
|
330
|
+
success: true,
|
|
331
|
+
attempts,
|
|
332
|
+
commitInfo,
|
|
333
|
+
planContent,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
338
|
+
// Record failed attempt if not already recorded
|
|
339
|
+
if (attempts.length === 0 || attempts[attempts.length - 1].attemptNumber !== attemptNumber) {
|
|
340
|
+
attempts.push({
|
|
341
|
+
attemptNumber,
|
|
342
|
+
success: false,
|
|
343
|
+
error: errorMessage,
|
|
344
|
+
executor: tool,
|
|
345
|
+
model: executorConfig?.model,
|
|
346
|
+
timestamp: Date.now() - attemptStartTime,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
// Emit execution:error event
|
|
350
|
+
await hooks_1.hooks.emit("execution:error", {
|
|
351
|
+
taskId: task.id,
|
|
352
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
353
|
+
});
|
|
354
|
+
if (!dry) {
|
|
355
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
356
|
+
console.log(chalk_1.default.red("❌ Task execution failed, status reset to todo"));
|
|
357
|
+
}
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { ExecuteTaskOptions } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Execute a task using an external coding assistant
|
|
4
|
+
* This is the simplified entry point that delegates to the unified core
|
|
5
|
+
*/
|
|
2
6
|
export declare function executeTask(options: ExecuteTaskOptions): Promise<void>;
|
|
3
7
|
//# sourceMappingURL=task-execution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAEnB,MAAM,UAAU,CAAC;AAGlB;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiD5E"}
|
|
@@ -1,157 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.executeTask = executeTask;
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const prompt_builder_1 = require("./prompt-builder");
|
|
13
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
-
async function executeSingleTask(taskId, tool, dry, executorConfig) {
|
|
15
|
-
// Load task
|
|
16
|
-
const task = await tasks_1.taskService.getTask(taskId);
|
|
17
|
-
if (!task) {
|
|
18
|
-
throw new Error(`Task with ID ${taskId} not found`);
|
|
19
|
-
}
|
|
20
|
-
console.log(chalk_1.default.blue(`🎯 ${dry ? "DRY RUN" : "Executing"} task: ${task.title} (${taskId})`));
|
|
21
|
-
// Build comprehensive execution message with full context
|
|
22
|
-
const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
|
|
23
|
-
const taskContext = await contextBuilder.buildContext(taskId);
|
|
24
|
-
// Get task plan if available
|
|
25
|
-
const planData = await tasks_1.taskService.getTaskPlan(taskId);
|
|
26
|
-
// Build execution prompt using PromptBuilder
|
|
27
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildExecutionPrompt({
|
|
28
|
-
taskTitle: task.title,
|
|
29
|
-
taskDescription: task.description,
|
|
30
|
-
taskPlan: planData?.plan,
|
|
31
|
-
stack: taskContext.stack,
|
|
32
|
-
documentation: taskContext.documentation,
|
|
33
|
-
});
|
|
34
|
-
if (!promptResult.success) {
|
|
35
|
-
throw new Error(`Failed to build execution prompt: ${promptResult.error}`);
|
|
36
|
-
}
|
|
37
|
-
const executionMessage = promptResult.prompt;
|
|
38
|
-
if (!dry) {
|
|
39
|
-
// Update task status to in-progress
|
|
40
|
-
await tasks_1.taskService.setTaskStatus(taskId, "in-progress");
|
|
41
|
-
console.log(chalk_1.default.yellow("⏳ Task status updated to in-progress"));
|
|
42
|
-
}
|
|
43
|
-
// Emit execution:start event
|
|
44
|
-
await hooks_1.hooks.emit("execution:start", { taskId, tool });
|
|
45
|
-
try {
|
|
46
|
-
// Create executor and run
|
|
47
|
-
const executor = executor_factory_1.ExecutorFactory.create(tool, executorConfig);
|
|
48
|
-
await executor.execute(executionMessage, dry, executorConfig);
|
|
49
|
-
if (!dry) {
|
|
50
|
-
// Update task status to completed
|
|
51
|
-
await tasks_1.taskService.setTaskStatus(taskId, "completed");
|
|
52
|
-
console.log(chalk_1.default.green("✅ Task execution completed successfully"));
|
|
53
|
-
}
|
|
54
|
-
// Emit execution:end event
|
|
55
|
-
await hooks_1.hooks.emit("execution:end", { taskId, success: true });
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
// Emit execution:error event
|
|
59
|
-
await hooks_1.hooks.emit("execution:error", {
|
|
60
|
-
taskId,
|
|
61
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
62
|
-
});
|
|
63
|
-
if (!dry) {
|
|
64
|
-
// Update task status back to todo on failure
|
|
65
|
-
await tasks_1.taskService.setTaskStatus(taskId, "todo");
|
|
66
|
-
console.log(chalk_1.default.red("❌ Task execution failed, status reset to todo"));
|
|
67
|
-
}
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async function executeTaskWithSubtasks(taskId, tool, dry, executorConfig) {
|
|
72
|
-
const task = await tasks_1.taskService.getTask(taskId);
|
|
73
|
-
if (!task) {
|
|
74
|
-
throw new Error(`Task with ID ${taskId} not found`);
|
|
75
|
-
}
|
|
76
|
-
// Get subtasks
|
|
77
|
-
const subtasks = await tasks_1.taskService.getSubtasks(taskId);
|
|
78
|
-
if (subtasks.length === 0) {
|
|
79
|
-
// No subtasks, execute this task directly
|
|
80
|
-
await executeSingleTask(taskId, tool, dry);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
// Has subtasks - execute them one by one
|
|
84
|
-
console.log(chalk_1.default.blue(`📋 Task has ${subtasks.length} subtasks, executing recursively...`));
|
|
85
|
-
for (let i = 0; i < subtasks.length; i++) {
|
|
86
|
-
const subtask = subtasks[i];
|
|
87
|
-
console.log(chalk_1.default.cyan(`\n[${i + 1}/${subtasks.length}] Executing subtask: ${subtask.title} (${subtask.id})`));
|
|
88
|
-
try {
|
|
89
|
-
await executeTaskWithSubtasks(subtask.id, tool, dry, executorConfig);
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
console.error(chalk_1.default.red(`❌ Failed to execute subtask ${subtask.id}: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
// After all subtasks are done, mark the main task as completed
|
|
97
|
-
if (!dry) {
|
|
98
|
-
await tasks_1.taskService.setTaskStatus(taskId, "completed");
|
|
99
|
-
console.log(chalk_1.default.green(`✅ Main task ${task.title} completed after all subtasks`));
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
console.log(chalk_1.default.cyan(`🔍 DRY RUN - Main task ${task.title} would be completed after all subtasks`));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
4
|
+
const task_execution_core_1 = require("./task-execution-core");
|
|
5
|
+
/**
|
|
6
|
+
* Execute a task using an external coding assistant
|
|
7
|
+
* This is the simplified entry point that delegates to the unified core
|
|
8
|
+
*/
|
|
105
9
|
async function executeTask(options) {
|
|
106
|
-
const { taskId, tool = "opencode", message, model, continueSession, dry = false, validate = [], } = options;
|
|
107
|
-
// Build
|
|
108
|
-
const
|
|
109
|
-
|
|
10
|
+
const { taskId, tool = "opencode", message, model, continueSession, dry = false, validate = [], maxRetries, tryModels, plan, planModel, reviewPlan, review, reviewModel, autoCommit, } = options;
|
|
11
|
+
// Build unified task execution config
|
|
12
|
+
const config = {
|
|
13
|
+
tool,
|
|
14
|
+
executorConfig: {
|
|
110
15
|
model,
|
|
111
16
|
continueLastSession: continueSession,
|
|
112
|
-
}
|
|
113
|
-
:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
// Emit execution:end event
|
|
135
|
-
await hooks_1.hooks.emit("execution:end", { taskId, success: true });
|
|
136
|
-
// Run validations after task completion
|
|
137
|
-
await (0, validation_1.runValidations)(validate, dry);
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
if (!dry) {
|
|
141
|
-
await tasks_1.taskService.setTaskStatus(taskId, "todo");
|
|
142
|
-
console.log(chalk_1.default.red("❌ Task execution failed, status reset to todo"));
|
|
143
|
-
}
|
|
144
|
-
// Emit execution:error event
|
|
145
|
-
await hooks_1.hooks.emit("execution:error", {
|
|
146
|
-
taskId,
|
|
147
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
148
|
-
});
|
|
149
|
-
throw error;
|
|
150
|
-
}
|
|
151
|
-
return;
|
|
17
|
+
},
|
|
18
|
+
customMessage: message,
|
|
19
|
+
verificationCommands: validate,
|
|
20
|
+
enableRetry: maxRetries !== undefined && maxRetries > 0, // Opt-in retry
|
|
21
|
+
maxRetries: maxRetries || 3,
|
|
22
|
+
tryModels,
|
|
23
|
+
enablePlanPhase: plan,
|
|
24
|
+
planModel,
|
|
25
|
+
reviewPlan,
|
|
26
|
+
enableReviewPhase: review,
|
|
27
|
+
reviewModel,
|
|
28
|
+
autoCommit,
|
|
29
|
+
executeSubtasks: true, // Always execute subtasks in execute command
|
|
30
|
+
dry,
|
|
31
|
+
};
|
|
32
|
+
// Delegate to unified core execution
|
|
33
|
+
const result = await (0, task_execution_core_1.executeTaskCore)(taskId, config);
|
|
34
|
+
// Throw error if execution failed (maintains backward compatibility)
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
const lastAttempt = result.attempts[result.attempts.length - 1];
|
|
37
|
+
throw new Error(lastAttempt?.error || "Task execution failed");
|
|
152
38
|
}
|
|
153
|
-
// No custom message - execute recursively with subtasks
|
|
154
|
-
await executeTaskWithSubtasks(taskId, tool, dry, executorConfig);
|
|
155
|
-
// Run validations after all subtasks complete
|
|
156
|
-
await (0, validation_1.runValidations)(validate, dry);
|
|
157
39
|
}
|
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
import { ExecuteLoopOptions, ExecuteLoopResult } from "../types";
|
|
2
|
-
/**
|
|
3
|
-
* Extract commit message and file list from AI conversation/output
|
|
4
|
-
* This function analyzes the executor's work and generates appropriate commit info
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Extract commit message and file list from git state
|
|
8
|
-
* This function analyzes the actual git state to generate appropriate commit info
|
|
9
|
-
*/
|
|
10
|
-
export declare function extractCommitInfo(taskId: string, taskTitle: string, executionMessage: string, gitState: {
|
|
11
|
-
beforeHead: string;
|
|
12
|
-
afterHead: string;
|
|
13
|
-
hasUncommittedChanges: boolean;
|
|
14
|
-
}, execFn?: (command: string) => Promise<{
|
|
15
|
-
stdout: string;
|
|
16
|
-
stderr: string;
|
|
17
|
-
}>, aiOps?: any): Promise<{
|
|
18
|
-
message: string;
|
|
19
|
-
files: string[];
|
|
20
|
-
}>;
|
|
21
2
|
/**
|
|
22
3
|
* Execute multiple tasks in a loop with retry and verification
|
|
4
|
+
* This delegates to the unified executeTaskCore for each task
|
|
23
5
|
*/
|
|
24
6
|
export declare function executeTaskLoop(options: ExecuteLoopOptions): Promise<ExecuteLoopResult>;
|
|
25
7
|
//# sourceMappingURL=task-loop-execution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"task-loop-execution.d.ts","sourceRoot":"","sources":["../../src/lib/task-loop-execution.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAIlB,MAAM,UAAU,CAAC;AAIlB;;;GAGG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAgL5B"}
|