task-o-matic 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/commands/tasks/create.d.ts.map +1 -1
  2. package/dist/commands/tasks/create.js +6 -13
  3. package/dist/commands/tasks/document/add.d.ts +3 -0
  4. package/dist/commands/tasks/document/add.d.ts.map +1 -0
  5. package/dist/commands/tasks/document/add.js +35 -0
  6. package/dist/commands/tasks/document/analyze.d.ts +3 -0
  7. package/dist/commands/tasks/document/analyze.d.ts.map +1 -0
  8. package/dist/commands/tasks/document/analyze.js +49 -0
  9. package/dist/commands/tasks/document/get.d.ts +3 -0
  10. package/dist/commands/tasks/document/get.d.ts.map +1 -0
  11. package/dist/commands/tasks/document/get.js +29 -0
  12. package/dist/commands/tasks/document/index.d.ts +8 -0
  13. package/dist/commands/tasks/document/index.d.ts.map +1 -0
  14. package/dist/commands/tasks/document/index.js +13 -0
  15. package/dist/commands/tasks/enhance.d.ts.map +1 -1
  16. package/dist/commands/tasks/enhance.js +64 -61
  17. package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
  18. package/dist/commands/tasks/execute-loop.js +64 -90
  19. package/dist/commands/tasks/execute.d.ts.map +1 -1
  20. package/dist/commands/tasks/execute.js +52 -16
  21. package/dist/commands/tasks/plan/create.d.ts +3 -0
  22. package/dist/commands/tasks/plan/create.d.ts.map +1 -0
  23. package/dist/commands/tasks/plan/create.js +37 -0
  24. package/dist/commands/tasks/plan/delete.d.ts +3 -0
  25. package/dist/commands/tasks/plan/delete.d.ts.map +1 -0
  26. package/dist/commands/tasks/plan/delete.js +14 -0
  27. package/dist/commands/tasks/plan/get.d.ts +3 -0
  28. package/dist/commands/tasks/plan/get.d.ts.map +1 -0
  29. package/dist/commands/tasks/plan/get.js +24 -0
  30. package/dist/commands/tasks/plan/index.d.ts +10 -0
  31. package/dist/commands/tasks/plan/index.d.ts.map +1 -0
  32. package/dist/commands/tasks/plan/index.js +17 -0
  33. package/dist/commands/tasks/plan/list.d.ts +3 -0
  34. package/dist/commands/tasks/plan/list.d.ts.map +1 -0
  35. package/dist/commands/tasks/plan/list.js +21 -0
  36. package/dist/commands/tasks/plan/set.d.ts +3 -0
  37. package/dist/commands/tasks/plan/set.d.ts.map +1 -0
  38. package/dist/commands/tasks/plan/set.js +33 -0
  39. package/dist/commands/tasks/split.d.ts.map +1 -1
  40. package/dist/commands/tasks/split.js +65 -60
  41. package/dist/lib/git-utils.d.ts +45 -0
  42. package/dist/lib/git-utils.d.ts.map +1 -0
  43. package/dist/lib/git-utils.js +160 -0
  44. package/dist/lib/task-execution-core.d.ts +7 -0
  45. package/dist/lib/task-execution-core.d.ts.map +1 -0
  46. package/dist/lib/task-execution-core.js +360 -0
  47. package/dist/lib/task-execution.d.ts +4 -0
  48. package/dist/lib/task-execution.d.ts.map +1 -1
  49. package/dist/lib/task-execution.js +31 -149
  50. package/dist/lib/task-loop-execution.d.ts +1 -19
  51. package/dist/lib/task-loop-execution.d.ts.map +1 -1
  52. package/dist/lib/task-loop-execution.js +50 -585
  53. package/dist/lib/task-planning.d.ts +28 -0
  54. package/dist/lib/task-planning.d.ts.map +1 -0
  55. package/dist/lib/task-planning.js +109 -0
  56. package/dist/lib/task-review.d.ts +27 -0
  57. package/dist/lib/task-review.d.ts.map +1 -0
  58. package/dist/lib/task-review.js +106 -0
  59. package/dist/lib/validation.d.ts +20 -3
  60. package/dist/lib/validation.d.ts.map +1 -1
  61. package/dist/lib/validation.js +39 -10
  62. package/dist/test/task-loop-git.test.js +6 -6
  63. package/dist/types/cli-options.d.ts +138 -0
  64. package/dist/types/cli-options.d.ts.map +1 -0
  65. package/dist/types/cli-options.js +6 -0
  66. package/dist/types/index.d.ts +38 -0
  67. package/dist/types/index.d.ts.map +1 -1
  68. package/dist/utils/bulk-operations.d.ts +51 -0
  69. package/dist/utils/bulk-operations.d.ts.map +1 -0
  70. package/dist/utils/bulk-operations.js +68 -0
  71. package/dist/utils/cli-validators.d.ts +54 -0
  72. package/dist/utils/cli-validators.d.ts.map +1 -0
  73. package/dist/utils/cli-validators.js +75 -0
  74. package/dist/utils/command-error-handler.d.ts +32 -0
  75. package/dist/utils/command-error-handler.d.ts.map +1 -0
  76. package/dist/utils/command-error-handler.js +52 -0
  77. package/dist/utils/confirmation.d.ts +19 -0
  78. package/dist/utils/confirmation.d.ts.map +1 -0
  79. package/dist/utils/confirmation.js +39 -0
  80. package/dist/utils/display-helpers.d.ts +81 -0
  81. package/dist/utils/display-helpers.d.ts.map +1 -0
  82. package/dist/utils/display-helpers.js +109 -0
  83. package/dist/utils/model-executor-parser.d.ts +38 -0
  84. package/dist/utils/model-executor-parser.d.ts.map +1 -0
  85. package/dist/utils/model-executor-parser.js +67 -0
  86. package/dist/utils/progress-tracking.d.ts +28 -0
  87. package/dist/utils/progress-tracking.d.ts.map +1 -0
  88. package/dist/utils/progress-tracking.js +43 -0
  89. package/package.json +1 -1
  90. package/dist/commands/tasks/document.d.ts +0 -5
  91. package/dist/commands/tasks/document.d.ts.map +0 -1
  92. package/dist/commands/tasks/document.js +0 -118
  93. package/dist/commands/tasks/plan.d.ts +0 -7
  94. package/dist/commands/tasks/plan.d.ts.map +0 -1
  95. package/dist/commands/tasks/plan.js +0 -131
@@ -0,0 +1,160 @@
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.captureGitState = captureGitState;
7
+ exports.extractCommitInfo = extractCommitInfo;
8
+ exports.autoCommit = autoCommit;
9
+ exports.commitFile = commitFile;
10
+ const child_process_1 = require("child_process");
11
+ const util_1 = require("util");
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ const ai_service_factory_1 = require("../utils/ai-service-factory");
14
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
15
+ /**
16
+ * Capture git state (HEAD commit and uncommitted changes)
17
+ */
18
+ async function captureGitState(execFn = execAsync) {
19
+ try {
20
+ const { stdout: headStdout } = await execFn("git rev-parse HEAD");
21
+ const { stdout: statusStdout } = await execFn("git status --porcelain");
22
+ return {
23
+ beforeHead: headStdout.trim(),
24
+ afterHead: headStdout.trim(),
25
+ hasUncommittedChanges: statusStdout.trim().length > 0,
26
+ };
27
+ }
28
+ catch (e) {
29
+ // Git might not be initialized or no commits yet
30
+ return {};
31
+ }
32
+ }
33
+ /**
34
+ * Extract commit message and file list from git state
35
+ * This function analyzes the actual git state to generate appropriate commit info
36
+ */
37
+ async function extractCommitInfo(taskId, taskTitle, executionMessage, gitState, execFn = execAsync, aiOps = (0, ai_service_factory_1.getAIOperations)()) {
38
+ try {
39
+ // Case 1: Executor created a commit
40
+ if (gitState.beforeHead !== gitState.afterHead) {
41
+ console.log(chalk_1.default.blue("📝 Executor created a commit, extracting info..."));
42
+ const { stdout } = await execFn(`git show --stat --format="%s%n%b" ${gitState.afterHead}`);
43
+ const lines = stdout.trim().split("\n");
44
+ const message = lines[0].trim();
45
+ // Parse files from stat output (e.g. " src/file.ts | 10 +")
46
+ const files = lines
47
+ .slice(1)
48
+ .filter((line) => line.includes("|"))
49
+ .map((line) => line.split("|")[0].trim());
50
+ return {
51
+ message,
52
+ files,
53
+ };
54
+ }
55
+ // Case 2: Executor left uncommitted changes
56
+ if (gitState.hasUncommittedChanges) {
57
+ console.log(chalk_1.default.blue("📝 Uncommitted changes detected, generating commit message..."));
58
+ // Get the diff to send to AI
59
+ const { stdout: diff } = await execFn("git diff HEAD");
60
+ // Get list of changed files
61
+ const { stdout: status } = await execFn("git status --porcelain");
62
+ const files = status
63
+ .split("\n")
64
+ .filter((line) => line.length > 0)
65
+ .map((line) => line.substring(3).trim())
66
+ .filter((file) => file.length > 0);
67
+ // Use AI to generate commit message based on the diff
68
+ const prompt = `Based on the following git diff, generate a concise git commit message.
69
+
70
+ Task: ${taskTitle}
71
+
72
+ Git Diff:
73
+ ${diff.substring(0, 10000)} // Limit diff size
74
+
75
+ Please respond in JSON format:
76
+ {
77
+ "message": "concise commit message following conventional commits format"
78
+ }
79
+
80
+ The commit message should:
81
+ - Follow conventional commits format (feat:, fix:, refactor:, etc.)
82
+ - Be concise and descriptive
83
+ - Focus on what changed
84
+ `;
85
+ const response = await aiOps.streamText(prompt, undefined, "You are a helpful assistant that generates git commit messages.");
86
+ // Try to parse JSON from response
87
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
88
+ let message = `feat: complete task ${taskTitle}`;
89
+ if (jsonMatch) {
90
+ try {
91
+ const parsed = JSON.parse(jsonMatch[0]);
92
+ if (parsed.message) {
93
+ message = parsed.message;
94
+ }
95
+ }
96
+ catch (e) {
97
+ // Ignore parse error
98
+ }
99
+ }
100
+ return {
101
+ message,
102
+ files,
103
+ };
104
+ }
105
+ // Case 3: No changes detected
106
+ return {
107
+ message: `feat: complete task ${taskTitle}`,
108
+ files: [],
109
+ };
110
+ }
111
+ catch (error) {
112
+ console.warn(chalk_1.default.yellow(`⚠️ Failed to extract commit info: ${error instanceof Error ? error.message : "Unknown error"}`));
113
+ // Fallback commit info
114
+ return {
115
+ message: `feat: complete task ${taskTitle}`,
116
+ files: [],
117
+ };
118
+ }
119
+ }
120
+ /**
121
+ * Auto-commit changes using the provided commit info
122
+ */
123
+ async function autoCommit(commitInfo, execFn = execAsync) {
124
+ try {
125
+ const { message, files } = commitInfo;
126
+ if (files.length > 0) {
127
+ // Stage specific files
128
+ const gitAdd = `git add ${files.join(" ")}`;
129
+ console.log(chalk_1.default.blue(`📦 Staging files: ${gitAdd}`));
130
+ await execFn(gitAdd);
131
+ }
132
+ else {
133
+ // Stage all changes
134
+ console.log(chalk_1.default.blue("📦 Staging all changes"));
135
+ await execFn("git add .");
136
+ }
137
+ // Commit
138
+ const gitCommit = `git commit -m "${message}"`;
139
+ console.log(chalk_1.default.blue(`💾 Committing: ${message}`));
140
+ await execFn(gitCommit);
141
+ console.log(chalk_1.default.green("✅ Changes committed successfully\n"));
142
+ }
143
+ catch (error) {
144
+ console.warn(chalk_1.default.yellow(`⚠️ Auto-commit failed: ${error instanceof Error ? error.message : "Unknown error"}\n`));
145
+ }
146
+ }
147
+ /**
148
+ * Commit a specific file with a custom message
149
+ */
150
+ async function commitFile(filePath, message, execFn = execAsync) {
151
+ try {
152
+ console.log(chalk_1.default.blue(`📦 Staging file: ${filePath}`));
153
+ await execFn(`git add ${filePath}`);
154
+ await execFn(`git commit -m "${message}"`);
155
+ console.log(chalk_1.default.green("✅ File committed successfully"));
156
+ }
157
+ catch (e) {
158
+ console.warn(chalk_1.default.yellow(`⚠️ Failed to commit file: ${e instanceof Error ? e.message : "Unknown error"}`));
159
+ }
160
+ }
@@ -0,0 +1,7 @@
1
+ import { TaskExecutionConfig, TaskExecutionResult } from "../types";
2
+ /**
3
+ * Execute a single task with all features (retry, planning, review, etc.)
4
+ * This is the core unified execution logic used by both execute and execute-loop commands
5
+ */
6
+ export declare function executeTaskCore(taskId: string, config: TaskExecutionConfig): Promise<TaskExecutionResult>;
7
+ //# sourceMappingURL=task-execution-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-execution-core.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution-core.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EAKpB,MAAM,UAAU,CAAC;AAWlB;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA+D9B"}
@@ -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":"AACA,OAAO,EAAE,kBAAkB,EAAgC,MAAM,UAAU,CAAC;AA0J5E,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgF5E"}
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"}