task-o-matic 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +286 -23
- package/dist/commands/benchmark.d.ts +3 -0
- package/dist/commands/benchmark.d.ts.map +1 -0
- package/dist/commands/benchmark.js +569 -0
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +203 -9
- package/dist/commands/tasks/execute-loop.d.ts +3 -0
- package/dist/commands/tasks/execute-loop.d.ts.map +1 -0
- package/dist/commands/tasks/execute-loop.js +118 -0
- package/dist/commands/tasks/index.d.ts +1 -0
- package/dist/commands/tasks/index.d.ts.map +1 -1
- package/dist/commands/tasks/index.js +1 -0
- package/dist/commands/tasks.d.ts.map +1 -1
- package/dist/commands/tasks.js +1 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +491 -331
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/ai-service/ai-operations.d.ts +5 -0
- package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/ai-operations.js +167 -0
- package/dist/lib/benchmark/registry.d.ts +11 -0
- package/dist/lib/benchmark/registry.d.ts.map +1 -0
- package/dist/lib/benchmark/registry.js +89 -0
- package/dist/lib/benchmark/runner.d.ts +6 -0
- package/dist/lib/benchmark/runner.d.ts.map +1 -0
- package/dist/lib/benchmark/runner.js +150 -0
- package/dist/lib/benchmark/storage.d.ts +13 -0
- package/dist/lib/benchmark/storage.d.ts.map +1 -0
- package/dist/lib/benchmark/storage.js +99 -0
- package/dist/lib/benchmark/types.d.ts +104 -0
- package/dist/lib/benchmark/types.d.ts.map +1 -0
- package/dist/lib/benchmark/types.js +2 -0
- package/dist/lib/index.d.ts +9 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +7 -1
- package/dist/lib/prompt-registry.d.ts.map +1 -1
- package/dist/lib/prompt-registry.js +23 -0
- package/dist/lib/task-loop-execution.d.ts +25 -0
- package/dist/lib/task-loop-execution.d.ts.map +1 -0
- package/dist/lib/task-loop-execution.js +473 -0
- package/dist/prompts/index.d.ts +7 -6
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -0
- package/dist/prompts/prd-question.d.ts +3 -0
- package/dist/prompts/prd-question.d.ts.map +1 -0
- package/dist/prompts/prd-question.js +40 -0
- package/dist/services/benchmark.d.ts +12 -0
- package/dist/services/benchmark.d.ts.map +1 -0
- package/dist/services/benchmark.js +18 -0
- package/dist/services/prd.d.ts +25 -0
- package/dist/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +224 -29
- package/dist/services/tasks.d.ts.map +1 -1
- package/dist/services/tasks.js +90 -3
- package/dist/services/workflow-benchmark.d.ts +34 -0
- package/dist/services/workflow-benchmark.d.ts.map +1 -0
- package/dist/services/workflow-benchmark.js +317 -0
- package/dist/services/workflow.d.ts +85 -0
- package/dist/services/workflow.d.ts.map +1 -0
- package/dist/services/workflow.js +476 -0
- package/dist/test/task-loop-git.test.d.ts +2 -0
- package/dist/test/task-loop-git.test.d.ts.map +1 -0
- package/dist/test/task-loop-git.test.js +62 -0
- package/dist/types/index.d.ts +53 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/options.d.ts +2 -1
- package/dist/types/options.d.ts.map +1 -1
- package/dist/types/options.js +16 -0
- package/dist/types/results.d.ts +29 -1
- package/dist/types/results.d.ts.map +1 -1
- package/dist/types/workflow-options.d.ts +45 -0
- package/dist/types/workflow-options.d.ts.map +1 -0
- package/dist/types/workflow-options.js +2 -0
- package/dist/types/workflow-results.d.ts +82 -0
- package/dist/types/workflow-results.d.ts.map +1 -0
- package/dist/types/workflow-results.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,473 @@
|
|
|
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.extractCommitInfo = extractCommitInfo;
|
|
7
|
+
exports.executeTaskLoop = executeTaskLoop;
|
|
8
|
+
const tasks_1 = require("../services/tasks");
|
|
9
|
+
const executor_factory_1 = require("./executors/executor-factory");
|
|
10
|
+
const ai_service_factory_1 = require("../utils/ai-service-factory");
|
|
11
|
+
const hooks_1 = require("./hooks");
|
|
12
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const util_1 = require("util");
|
|
15
|
+
const ai_service_factory_2 = require("../utils/ai-service-factory");
|
|
16
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
17
|
+
/**
|
|
18
|
+
* Extract commit message and file list from AI conversation/output
|
|
19
|
+
* This function analyzes the executor's work and generates appropriate commit info
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Extract commit message and file list from git state
|
|
23
|
+
* This function analyzes the actual git state to generate appropriate commit info
|
|
24
|
+
*/
|
|
25
|
+
async function extractCommitInfo(taskId, taskTitle, executionMessage, gitState, execFn = execAsync, aiOps = (0, ai_service_factory_2.getAIOperations)()) {
|
|
26
|
+
try {
|
|
27
|
+
// Case 1: Executor created a commit
|
|
28
|
+
if (gitState.beforeHead !== gitState.afterHead) {
|
|
29
|
+
console.log(chalk_1.default.blue("📝 Executor created a commit, extracting info..."));
|
|
30
|
+
const { stdout } = await execFn(`git show --stat --format="%s%n%b" ${gitState.afterHead}`);
|
|
31
|
+
const lines = stdout.trim().split("\n");
|
|
32
|
+
const message = lines[0].trim();
|
|
33
|
+
// Parse files from stat output (e.g. " src/file.ts | 10 +")
|
|
34
|
+
const files = lines
|
|
35
|
+
.slice(1)
|
|
36
|
+
.filter((line) => line.includes("|"))
|
|
37
|
+
.map((line) => line.split("|")[0].trim());
|
|
38
|
+
return {
|
|
39
|
+
message,
|
|
40
|
+
files,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Case 2: Executor left uncommitted changes
|
|
44
|
+
if (gitState.hasUncommittedChanges) {
|
|
45
|
+
console.log(chalk_1.default.blue("📝 Uncommitted changes detected, generating commit message..."));
|
|
46
|
+
// Get the diff to send to AI
|
|
47
|
+
const { stdout: diff } = await execFn("git diff HEAD");
|
|
48
|
+
// Get list of changed files
|
|
49
|
+
const { stdout: status } = await execFn("git status --porcelain");
|
|
50
|
+
const files = status
|
|
51
|
+
.split("\n")
|
|
52
|
+
.filter((line) => line.length > 0)
|
|
53
|
+
.map((line) => line.substring(3).trim())
|
|
54
|
+
.filter((file) => file.length > 0);
|
|
55
|
+
// Use AI to generate commit message based on the diff
|
|
56
|
+
// const aiOps = getAIOperations(); // Injected
|
|
57
|
+
const prompt = `Based on the following git diff, generate a concise git commit message.
|
|
58
|
+
|
|
59
|
+
Task: ${taskTitle}
|
|
60
|
+
|
|
61
|
+
Git Diff:
|
|
62
|
+
${diff.substring(0, 10000)} // Limit diff size
|
|
63
|
+
|
|
64
|
+
Please respond in JSON format:
|
|
65
|
+
{
|
|
66
|
+
"message": "concise commit message following conventional commits format"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
The commit message should:
|
|
70
|
+
- Follow conventional commits format (feat:, fix:, refactor:, etc.)
|
|
71
|
+
- Be concise and descriptive
|
|
72
|
+
- Focus on what changed
|
|
73
|
+
`;
|
|
74
|
+
const response = await aiOps.streamText(prompt, undefined, "You are a helpful assistant that generates git commit messages.");
|
|
75
|
+
// Try to parse JSON from response
|
|
76
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
77
|
+
let message = `feat: complete task ${taskTitle}`;
|
|
78
|
+
if (jsonMatch) {
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
81
|
+
if (parsed.message) {
|
|
82
|
+
message = parsed.message;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// Ignore parse error
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
message,
|
|
91
|
+
files,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Case 3: No changes detected
|
|
95
|
+
return {
|
|
96
|
+
message: `feat: complete task ${taskTitle}`,
|
|
97
|
+
files: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.warn(chalk_1.default.yellow(`⚠️ Failed to extract commit info: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
102
|
+
// Fallback commit info
|
|
103
|
+
return {
|
|
104
|
+
message: `feat: complete task ${taskTitle}`,
|
|
105
|
+
files: [],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Run verification commands and return results
|
|
111
|
+
*/
|
|
112
|
+
async function runVerificationCommands(commands, dry) {
|
|
113
|
+
const results = [];
|
|
114
|
+
if (dry) {
|
|
115
|
+
console.log(chalk_1.default.yellow("🔍 DRY RUN - Verification commands:"));
|
|
116
|
+
commands.forEach((cmd) => {
|
|
117
|
+
console.log(chalk_1.default.cyan(` ${cmd}`));
|
|
118
|
+
results.push({
|
|
119
|
+
command: cmd,
|
|
120
|
+
success: true,
|
|
121
|
+
output: "DRY RUN - not executed",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
return results;
|
|
125
|
+
}
|
|
126
|
+
for (const command of commands) {
|
|
127
|
+
console.log(chalk_1.default.blue(`🧪 Running verification: ${command}`));
|
|
128
|
+
try {
|
|
129
|
+
const { stdout, stderr } = await execAsync(command);
|
|
130
|
+
console.log(chalk_1.default.green(`✅ Verification passed: ${command}`));
|
|
131
|
+
results.push({
|
|
132
|
+
command,
|
|
133
|
+
success: true,
|
|
134
|
+
output: stdout.trim(),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error(chalk_1.default.red(`❌ Verification failed: ${command}`));
|
|
139
|
+
const errorOutput = error.stderr || error.stdout || error.message;
|
|
140
|
+
console.error(chalk_1.default.red(` Error: ${errorOutput}`));
|
|
141
|
+
results.push({
|
|
142
|
+
command,
|
|
143
|
+
success: false,
|
|
144
|
+
error: errorOutput,
|
|
145
|
+
});
|
|
146
|
+
// Return early on first failure
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Execute a single task with retry logic and error correction
|
|
154
|
+
*/
|
|
155
|
+
async function executeTaskWithRetry(task, tool, verificationCommands, maxRetries, dry, tryModels) {
|
|
156
|
+
const attempts = [];
|
|
157
|
+
let currentAttempt = 1;
|
|
158
|
+
let lastError;
|
|
159
|
+
while (currentAttempt <= maxRetries) {
|
|
160
|
+
// Determine which executor and model to use for this attempt
|
|
161
|
+
let currentExecutor = tool;
|
|
162
|
+
let currentModel;
|
|
163
|
+
if (tryModels && tryModels.length > 0) {
|
|
164
|
+
// Use the model config for this attempt (or last one if we've exceeded the list)
|
|
165
|
+
const modelConfigIndex = Math.min(currentAttempt - 1, tryModels.length - 1);
|
|
166
|
+
const modelConfig = tryModels[modelConfigIndex];
|
|
167
|
+
// Override executor if specified
|
|
168
|
+
if (modelConfig.executor) {
|
|
169
|
+
currentExecutor = modelConfig.executor;
|
|
170
|
+
}
|
|
171
|
+
// Store model name if specified
|
|
172
|
+
if (modelConfig.model) {
|
|
173
|
+
currentModel = modelConfig.model;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
console.log(chalk_1.default.blue(`\n🎯 Attempt ${currentAttempt}/${maxRetries} for task: ${task.title} (${task.id})`));
|
|
177
|
+
if (currentModel) {
|
|
178
|
+
console.log(chalk_1.default.cyan(` Using executor: ${currentExecutor} with model: ${currentModel}`));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
console.log(chalk_1.default.cyan(` Using executor: ${currentExecutor}`));
|
|
182
|
+
}
|
|
183
|
+
const attemptStartTime = Date.now();
|
|
184
|
+
// Capture git state before execution
|
|
185
|
+
let beforeHead = "";
|
|
186
|
+
try {
|
|
187
|
+
const { stdout } = await execAsync("git rev-parse HEAD");
|
|
188
|
+
beforeHead = stdout.trim();
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
// Git might not be initialized or no commits yet
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
// Build execution message with context
|
|
195
|
+
const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
|
|
196
|
+
const taskContext = await contextBuilder.buildContext(task.id);
|
|
197
|
+
const messageParts = [];
|
|
198
|
+
// Add retry context if this is a retry attempt
|
|
199
|
+
if (currentAttempt > 1 && lastError) {
|
|
200
|
+
messageParts.push(`# RETRY ATTEMPT ${currentAttempt}/${maxRetries}\n\n`);
|
|
201
|
+
// Add model escalation context
|
|
202
|
+
if (currentModel) {
|
|
203
|
+
messageParts.push(`**Note**: You are ${currentExecutor} using the ${currentModel} model. This is a more capable model than the previous attempt.\n\n`);
|
|
204
|
+
}
|
|
205
|
+
messageParts.push(`## Previous Attempt Failed With Error:\n\n${lastError}\n\n`);
|
|
206
|
+
messageParts.push(`Please analyze the error carefully and fix it. The error might be due to:\n`);
|
|
207
|
+
messageParts.push(`- Syntax errors\n`);
|
|
208
|
+
messageParts.push(`- Logic errors\n`);
|
|
209
|
+
messageParts.push(`- Missing dependencies or imports\n`);
|
|
210
|
+
messageParts.push(`- Incorrect configuration\n`);
|
|
211
|
+
messageParts.push(`- Build or test failures\n\n`);
|
|
212
|
+
messageParts.push(`Please fix the error above and complete the task successfully.\n\n`);
|
|
213
|
+
}
|
|
214
|
+
// Add task plan if available
|
|
215
|
+
const planData = await tasks_1.taskService.getTaskPlan(task.id);
|
|
216
|
+
if (planData) {
|
|
217
|
+
messageParts.push(`# Task Plan\n\n${planData.plan}\n`);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
messageParts.push(`# Task: ${task.title}\n\n${task.description || "No description"}\n`);
|
|
221
|
+
}
|
|
222
|
+
// Add PRD context if available
|
|
223
|
+
if (taskContext.prdContent) {
|
|
224
|
+
messageParts.push(`\n# Product Requirements Document\n\n${taskContext.prdContent}\n`);
|
|
225
|
+
}
|
|
226
|
+
// Add stack/technology context
|
|
227
|
+
if (taskContext.stack) {
|
|
228
|
+
messageParts.push(`\n# Technology Stack\n\n`);
|
|
229
|
+
messageParts.push(`- **Project**: ${taskContext.stack.projectName}\n`);
|
|
230
|
+
messageParts.push(`- **Frontend**: ${taskContext.stack.frontend}\n`);
|
|
231
|
+
messageParts.push(`- **Backend**: ${taskContext.stack.backend}\n`);
|
|
232
|
+
if (taskContext.stack.database !== "none") {
|
|
233
|
+
messageParts.push(`- **Database**: ${taskContext.stack.database}\n`);
|
|
234
|
+
}
|
|
235
|
+
messageParts.push(`- **Auth**: ${taskContext.stack.auth}\n`);
|
|
236
|
+
}
|
|
237
|
+
// Add documentation context if available
|
|
238
|
+
if (taskContext.documentation) {
|
|
239
|
+
messageParts.push(`\n# Documentation Context\n\n`);
|
|
240
|
+
messageParts.push(`${taskContext.documentation.recap}\n`);
|
|
241
|
+
}
|
|
242
|
+
const executionMessage = messageParts.join("");
|
|
243
|
+
// Update task status to in-progress
|
|
244
|
+
if (!dry) {
|
|
245
|
+
await tasks_1.taskService.setTaskStatus(task.id, "in-progress");
|
|
246
|
+
console.log(chalk_1.default.yellow("⏳ Task status updated to in-progress"));
|
|
247
|
+
}
|
|
248
|
+
// Emit execution:start event
|
|
249
|
+
await hooks_1.hooks.emit("execution:start", {
|
|
250
|
+
taskId: task.id,
|
|
251
|
+
tool: currentExecutor,
|
|
252
|
+
});
|
|
253
|
+
// Create executor and run
|
|
254
|
+
const executor = executor_factory_1.ExecutorFactory.create(currentExecutor);
|
|
255
|
+
// Add model info to execution message if specified
|
|
256
|
+
let finalExecutionMessage = executionMessage;
|
|
257
|
+
if (currentModel) {
|
|
258
|
+
finalExecutionMessage =
|
|
259
|
+
`**Model Configuration**: Using ${currentModel}\n\n` +
|
|
260
|
+
executionMessage;
|
|
261
|
+
}
|
|
262
|
+
await executor.execute(finalExecutionMessage, dry);
|
|
263
|
+
// Run verification commands
|
|
264
|
+
const verificationResults = await runVerificationCommands(verificationCommands, dry);
|
|
265
|
+
// Check if all verifications passed
|
|
266
|
+
const allVerificationsPassed = verificationResults.every((r) => r.success);
|
|
267
|
+
if (!allVerificationsPassed) {
|
|
268
|
+
// Verification failed - prepare error message for retry
|
|
269
|
+
const failedVerification = verificationResults.find((r) => !r.success);
|
|
270
|
+
lastError = `Verification command "${failedVerification?.command}" failed:\n${failedVerification?.error}`;
|
|
271
|
+
attempts.push({
|
|
272
|
+
attemptNumber: currentAttempt,
|
|
273
|
+
success: false,
|
|
274
|
+
error: lastError,
|
|
275
|
+
executor: currentExecutor,
|
|
276
|
+
model: currentModel,
|
|
277
|
+
verificationResults,
|
|
278
|
+
timestamp: Date.now() - attemptStartTime,
|
|
279
|
+
});
|
|
280
|
+
console.log(chalk_1.default.red(`❌ Task execution failed verification on attempt ${currentAttempt}`));
|
|
281
|
+
currentAttempt++;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
// Success! Extract commit info
|
|
285
|
+
let commitInfo;
|
|
286
|
+
if (!dry) {
|
|
287
|
+
console.log(chalk_1.default.blue("📝 Extracting commit information..."));
|
|
288
|
+
// Capture git state after execution
|
|
289
|
+
let afterHead = "";
|
|
290
|
+
let hasUncommittedChanges = false;
|
|
291
|
+
try {
|
|
292
|
+
const { stdout: headStdout } = await execAsync("git rev-parse HEAD");
|
|
293
|
+
afterHead = headStdout.trim();
|
|
294
|
+
const { stdout: statusStdout } = await execAsync("git status --porcelain");
|
|
295
|
+
hasUncommittedChanges = statusStdout.trim().length > 0;
|
|
296
|
+
}
|
|
297
|
+
catch (e) {
|
|
298
|
+
// Git issues
|
|
299
|
+
}
|
|
300
|
+
commitInfo = await extractCommitInfo(task.id, task.title, executionMessage, {
|
|
301
|
+
beforeHead,
|
|
302
|
+
afterHead,
|
|
303
|
+
hasUncommittedChanges,
|
|
304
|
+
});
|
|
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
|
+
}
|
|
310
|
+
// Update task status to completed
|
|
311
|
+
if (!dry) {
|
|
312
|
+
await tasks_1.taskService.setTaskStatus(task.id, "completed");
|
|
313
|
+
console.log(chalk_1.default.green("✅ Task execution completed successfully"));
|
|
314
|
+
}
|
|
315
|
+
// Record successful attempt
|
|
316
|
+
attempts.push({
|
|
317
|
+
attemptNumber: currentAttempt,
|
|
318
|
+
success: true,
|
|
319
|
+
executor: currentExecutor,
|
|
320
|
+
model: currentModel,
|
|
321
|
+
verificationResults,
|
|
322
|
+
commitInfo,
|
|
323
|
+
timestamp: Date.now() - attemptStartTime,
|
|
324
|
+
});
|
|
325
|
+
// Emit execution:end event
|
|
326
|
+
await hooks_1.hooks.emit("execution:end", { taskId: task.id, success: true });
|
|
327
|
+
return attempts; // Success - exit retry loop
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
331
|
+
attempts.push({
|
|
332
|
+
attemptNumber: currentAttempt,
|
|
333
|
+
success: false,
|
|
334
|
+
error: lastError,
|
|
335
|
+
executor: currentExecutor,
|
|
336
|
+
model: currentModel,
|
|
337
|
+
timestamp: Date.now() - attemptStartTime,
|
|
338
|
+
});
|
|
339
|
+
// Emit execution:error event
|
|
340
|
+
await hooks_1.hooks.emit("execution:error", {
|
|
341
|
+
taskId: task.id,
|
|
342
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
343
|
+
});
|
|
344
|
+
console.log(chalk_1.default.red(`❌ Task execution failed on attempt ${currentAttempt}: ${lastError}`));
|
|
345
|
+
if (!dry && currentAttempt < maxRetries) {
|
|
346
|
+
// Reset task status to todo for retry
|
|
347
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
348
|
+
console.log(chalk_1.default.yellow("⏸ Task status reset to todo for retry"));
|
|
349
|
+
}
|
|
350
|
+
currentAttempt++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// All retries exhausted
|
|
354
|
+
if (!dry) {
|
|
355
|
+
await tasks_1.taskService.setTaskStatus(task.id, "todo");
|
|
356
|
+
console.log(chalk_1.default.red("❌ All retry attempts exhausted, task status reset to todo"));
|
|
357
|
+
}
|
|
358
|
+
return attempts;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Execute multiple tasks in a loop with retry and verification
|
|
362
|
+
*/
|
|
363
|
+
async function executeTaskLoop(options) {
|
|
364
|
+
const startTime = Date.now();
|
|
365
|
+
const { filters = {}, tool = "opencode", config = {}, dry = false } = options;
|
|
366
|
+
const { maxRetries = 3, verificationCommands = [], autoCommit = false, } = config;
|
|
367
|
+
console.log(chalk_1.default.blue.bold("\n🔄 Starting Task Loop Execution\n"));
|
|
368
|
+
console.log(chalk_1.default.cyan(`Executor Tool: ${tool}`));
|
|
369
|
+
console.log(chalk_1.default.cyan(`Max Retries per Task: ${maxRetries}`));
|
|
370
|
+
console.log(chalk_1.default.cyan(`Verification Commands: ${verificationCommands.length > 0
|
|
371
|
+
? verificationCommands.join(", ")
|
|
372
|
+
: "None"}`));
|
|
373
|
+
console.log(chalk_1.default.cyan(`Auto Commit: ${autoCommit ? "Yes" : "No"}`));
|
|
374
|
+
console.log(chalk_1.default.cyan(`Dry Run: ${dry ? "Yes" : "No"}\n`));
|
|
375
|
+
// Get tasks to execute
|
|
376
|
+
let tasksToExecute = [];
|
|
377
|
+
if (filters.taskIds && filters.taskIds.length > 0) {
|
|
378
|
+
// Execute specific tasks by ID
|
|
379
|
+
for (const taskId of filters.taskIds) {
|
|
380
|
+
const task = await tasks_1.taskService.getTask(taskId);
|
|
381
|
+
if (task) {
|
|
382
|
+
tasksToExecute.push(task);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
console.warn(chalk_1.default.yellow(`⚠️ Task ${taskId} not found, skipping`));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// Get all tasks matching filters
|
|
391
|
+
tasksToExecute = await tasks_1.taskService.listTasks({
|
|
392
|
+
status: filters.status,
|
|
393
|
+
tag: filters.tag,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
if (tasksToExecute.length === 0) {
|
|
397
|
+
console.log(chalk_1.default.yellow("⚠️ No tasks found matching the filters"));
|
|
398
|
+
return {
|
|
399
|
+
totalTasks: 0,
|
|
400
|
+
completedTasks: 0,
|
|
401
|
+
failedTasks: 0,
|
|
402
|
+
taskResults: [],
|
|
403
|
+
duration: Date.now() - startTime,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
console.log(chalk_1.default.blue(`📋 Found ${tasksToExecute.length} task(s) to execute\n`));
|
|
407
|
+
const taskResults = [];
|
|
408
|
+
let completedTasks = 0;
|
|
409
|
+
let failedTasks = 0;
|
|
410
|
+
// Execute tasks sequentially
|
|
411
|
+
for (let i = 0; i < tasksToExecute.length; i++) {
|
|
412
|
+
const task = tasksToExecute[i];
|
|
413
|
+
console.log(chalk_1.default.blue.bold(`\n${"=".repeat(60)}\n📌 Task ${i + 1}/${tasksToExecute.length}: ${task.title} (${task.id})\n${"=".repeat(60)}\n`));
|
|
414
|
+
// Execute task with retry logic
|
|
415
|
+
const attempts = await executeTaskWithRetry(task, tool, verificationCommands, maxRetries, dry, config.tryModels);
|
|
416
|
+
// Check if task succeeded
|
|
417
|
+
const lastAttempt = attempts[attempts.length - 1];
|
|
418
|
+
const succeeded = lastAttempt.success;
|
|
419
|
+
if (succeeded) {
|
|
420
|
+
completedTasks++;
|
|
421
|
+
console.log(chalk_1.default.green.bold(`\n✅ Task ${task.title} completed successfully after ${attempts.length} attempt(s)\n`));
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
failedTasks++;
|
|
425
|
+
console.log(chalk_1.default.red.bold(`\n❌ Task ${task.title} failed after ${attempts.length} attempt(s)\n`));
|
|
426
|
+
}
|
|
427
|
+
taskResults.push({
|
|
428
|
+
taskId: task.id,
|
|
429
|
+
taskTitle: task.title,
|
|
430
|
+
attempts,
|
|
431
|
+
finalStatus: succeeded ? "completed" : "failed",
|
|
432
|
+
});
|
|
433
|
+
// Auto-commit if enabled and task succeeded
|
|
434
|
+
if (autoCommit && succeeded && !dry && lastAttempt.commitInfo) {
|
|
435
|
+
try {
|
|
436
|
+
const { message, files } = lastAttempt.commitInfo;
|
|
437
|
+
if (files.length > 0) {
|
|
438
|
+
// Stage specific files
|
|
439
|
+
const gitAdd = `git add ${files.join(" ")}`;
|
|
440
|
+
console.log(chalk_1.default.blue(`📦 Staging files: ${gitAdd}`));
|
|
441
|
+
await execAsync(gitAdd);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
// Stage all changes
|
|
445
|
+
console.log(chalk_1.default.blue("📦 Staging all changes"));
|
|
446
|
+
await execAsync("git add .");
|
|
447
|
+
}
|
|
448
|
+
// Commit
|
|
449
|
+
const gitCommit = `git commit -m "${message}"`;
|
|
450
|
+
console.log(chalk_1.default.blue(`💾 Committing: ${message}`));
|
|
451
|
+
await execAsync(gitCommit);
|
|
452
|
+
console.log(chalk_1.default.green("✅ Changes committed successfully\n"));
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
console.warn(chalk_1.default.yellow(`⚠️ Auto-commit failed: ${error instanceof Error ? error.message : "Unknown error"}\n`));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const duration = Date.now() - startTime;
|
|
460
|
+
// Print summary
|
|
461
|
+
console.log(chalk_1.default.blue.bold(`\n${"=".repeat(60)}\n📊 Execution Summary\n${"=".repeat(60)}`));
|
|
462
|
+
console.log(chalk_1.default.cyan(`Total Tasks: ${tasksToExecute.length}`));
|
|
463
|
+
console.log(chalk_1.default.green(`✅ Completed: ${completedTasks}`));
|
|
464
|
+
console.log(chalk_1.default.red(`❌ Failed: ${failedTasks}`));
|
|
465
|
+
console.log(chalk_1.default.cyan(`⏱ Duration: ${(duration / 1000).toFixed(2)} seconds\n`));
|
|
466
|
+
return {
|
|
467
|
+
totalTasks: tasksToExecute.length,
|
|
468
|
+
completedTasks,
|
|
469
|
+
failedTasks,
|
|
470
|
+
taskResults,
|
|
471
|
+
duration,
|
|
472
|
+
};
|
|
473
|
+
}
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
1
|
+
export * from "./prd-parsing";
|
|
2
|
+
export * from "./task-breakdown";
|
|
3
|
+
export * from "./task-enhancement";
|
|
4
|
+
export * from "./prd-rework";
|
|
5
|
+
export * from "./documentation-detection";
|
|
6
|
+
export * from "./task-planning";
|
|
7
|
+
export * from "./prd-question";
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
|
package/dist/prompts/index.js
CHANGED
|
@@ -20,3 +20,4 @@ __exportStar(require("./task-enhancement"), exports);
|
|
|
20
20
|
__exportStar(require("./prd-rework"), exports);
|
|
21
21
|
__exportStar(require("./documentation-detection"), exports);
|
|
22
22
|
__exportStar(require("./task-planning"), exports);
|
|
23
|
+
__exportStar(require("./prd-question"), exports);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const PRD_QUESTION_PROMPT = "\nAnalyze this PRD and generate meaningful clarifying questions to help improve its quality and completeness.\n\n## Current PRD:\n{PRD_CONTENT}\n\n## Project Technology Stack:\n{STACK_INFO}\n\nIdentify ambiguities, missing requirements, technical gaps, or potential conflicts.\nFocus on questions that will help:\n- Clarify user intent\n- Define edge cases\n- Specify technical implementation details aligned with the stack\n- Resolve potential architectural issues\n\nReturn a JSON object with a \"questions\" array, where each item is a string containing a question.\n";
|
|
2
|
+
export declare const PRD_QUESTION_SYSTEM_PROMPT = "\nYou are an expert Product Manager and Technical Architect. Your goal is to analyze Product Requirements Documents (PRDs) and ask insightful questions to clarify requirements and ensure technical feasibility.\n\nOutput Format:\nYou must return a valid JSON object with the following structure:\n{\n \"questions\": [\n \"Question 1?\",\n \"Question 2?\",\n ...\n ]\n}\n\nGuidelines:\n1. Ask 3-5 most critical questions. Do not overwhelm the user.\n2. Focus on \"what\" and \"why\" rather than \"how\" unless it affects feasibility.\n3. Be specific and reference parts of the PRD.\n4. Consider the technology stack constraints.\n";
|
|
3
|
+
//# sourceMappingURL=prd-question.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd-question.d.ts","sourceRoot":"","sources":["../../src/prompts/prd-question.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,+jBAiB/B,CAAC;AAEF,eAAO,MAAM,0BAA0B,ooBAkBtC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRD_QUESTION_SYSTEM_PROMPT = exports.PRD_QUESTION_PROMPT = void 0;
|
|
4
|
+
exports.PRD_QUESTION_PROMPT = `
|
|
5
|
+
Analyze this PRD and generate meaningful clarifying questions to help improve its quality and completeness.
|
|
6
|
+
|
|
7
|
+
## Current PRD:
|
|
8
|
+
{PRD_CONTENT}
|
|
9
|
+
|
|
10
|
+
## Project Technology Stack:
|
|
11
|
+
{STACK_INFO}
|
|
12
|
+
|
|
13
|
+
Identify ambiguities, missing requirements, technical gaps, or potential conflicts.
|
|
14
|
+
Focus on questions that will help:
|
|
15
|
+
- Clarify user intent
|
|
16
|
+
- Define edge cases
|
|
17
|
+
- Specify technical implementation details aligned with the stack
|
|
18
|
+
- Resolve potential architectural issues
|
|
19
|
+
|
|
20
|
+
Return a JSON object with a "questions" array, where each item is a string containing a question.
|
|
21
|
+
`;
|
|
22
|
+
exports.PRD_QUESTION_SYSTEM_PROMPT = `
|
|
23
|
+
You are an expert Product Manager and Technical Architect. Your goal is to analyze Product Requirements Documents (PRDs) and ask insightful questions to clarify requirements and ensure technical feasibility.
|
|
24
|
+
|
|
25
|
+
Output Format:
|
|
26
|
+
You must return a valid JSON object with the following structure:
|
|
27
|
+
{
|
|
28
|
+
"questions": [
|
|
29
|
+
"Question 1?",
|
|
30
|
+
"Question 2?",
|
|
31
|
+
...
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Guidelines:
|
|
36
|
+
1. Ask 3-5 most critical questions. Do not overwhelm the user.
|
|
37
|
+
2. Focus on "what" and "why" rather than "how" unless it affects feasibility.
|
|
38
|
+
3. Be specific and reference parts of the PRD.
|
|
39
|
+
4. Consider the technology stack constraints.
|
|
40
|
+
`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BenchmarkConfig, BenchmarkRun, BenchmarkProgressEvent } from "../lib/benchmark/types";
|
|
2
|
+
export declare class BenchmarkService {
|
|
3
|
+
runBenchmark(operationId: string, input: any, config: BenchmarkConfig, onProgress?: (event: BenchmarkProgressEvent) => void): Promise<BenchmarkRun>;
|
|
4
|
+
getRun(id: string): BenchmarkRun | null;
|
|
5
|
+
listRuns(): Array<{
|
|
6
|
+
id: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
command: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export declare const benchmarkService: BenchmarkService;
|
|
12
|
+
//# sourceMappingURL=benchmark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark.d.ts","sourceRoot":"","sources":["../../src/services/benchmark.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,gBAAgB;IACrB,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,eAAe,EACvB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,GACnD,OAAO,CAAC,YAAY,CAAC;IAIxB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAIvC,QAAQ,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAGtE;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.benchmarkService = exports.BenchmarkService = void 0;
|
|
4
|
+
const runner_1 = require("../lib/benchmark/runner");
|
|
5
|
+
const storage_1 = require("../lib/benchmark/storage");
|
|
6
|
+
class BenchmarkService {
|
|
7
|
+
async runBenchmark(operationId, input, config, onProgress) {
|
|
8
|
+
return await runner_1.benchmarkRunner.run(operationId, input, config, onProgress);
|
|
9
|
+
}
|
|
10
|
+
getRun(id) {
|
|
11
|
+
return storage_1.benchmarkStorage.getRun(id);
|
|
12
|
+
}
|
|
13
|
+
listRuns() {
|
|
14
|
+
return storage_1.benchmarkStorage.listRuns();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.BenchmarkService = BenchmarkService;
|
|
18
|
+
exports.benchmarkService = new BenchmarkService();
|
package/dist/services/prd.d.ts
CHANGED
|
@@ -17,6 +17,16 @@ export declare class PRDService {
|
|
|
17
17
|
streamingOptions?: StreamingOptions;
|
|
18
18
|
callbacks?: ProgressCallback;
|
|
19
19
|
}): Promise<PRDParseResult>;
|
|
20
|
+
generateQuestions(input: {
|
|
21
|
+
file: string;
|
|
22
|
+
workingDirectory?: string;
|
|
23
|
+
enableFilesystemTools?: boolean;
|
|
24
|
+
aiOptions?: AIOptions;
|
|
25
|
+
promptOverride?: string;
|
|
26
|
+
messageOverride?: string;
|
|
27
|
+
streamingOptions?: StreamingOptions;
|
|
28
|
+
callbacks?: ProgressCallback;
|
|
29
|
+
}): Promise<string[]>;
|
|
20
30
|
reworkPRD(input: {
|
|
21
31
|
file: string;
|
|
22
32
|
feedback: string;
|
|
@@ -29,6 +39,21 @@ export declare class PRDService {
|
|
|
29
39
|
streamingOptions?: StreamingOptions;
|
|
30
40
|
callbacks?: ProgressCallback;
|
|
31
41
|
}): Promise<string>;
|
|
42
|
+
refinePRDWithQuestions(input: {
|
|
43
|
+
file: string;
|
|
44
|
+
questionMode: "user" | "ai";
|
|
45
|
+
answers?: Record<string, string>;
|
|
46
|
+
questionAIOptions?: AIOptions;
|
|
47
|
+
workingDirectory?: string;
|
|
48
|
+
enableFilesystemTools?: boolean;
|
|
49
|
+
aiOptions?: AIOptions;
|
|
50
|
+
streamingOptions?: StreamingOptions;
|
|
51
|
+
callbacks?: ProgressCallback;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
questions: string[];
|
|
54
|
+
answers: Record<string, string>;
|
|
55
|
+
refinedPRDPath: string;
|
|
56
|
+
}>;
|
|
32
57
|
}
|
|
33
58
|
export declare const prdService: PRDService;
|
|
34
59
|
//# sourceMappingURL=prd.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prd.d.ts","sourceRoot":"","sources":["../../src/services/prd.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prd.d.ts","sourceRoot":"","sources":["../../src/services/prd.ts"],"names":[],"mappings":"AASA,OAAO,EAAiB,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAY,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;GAGG;AACH,qBAAa,UAAU;IACf,QAAQ,CAAC,KAAK,EAAE;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B,GAAG,OAAO,CAAC,cAAc,CAAC;IA6NrB,iBAAiB,CAAC,KAAK,EAAE;QAC7B,IAAI,EAAE,MAAM,CAAC;QACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqDf,SAAS,CAAC,KAAK,EAAE;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B,GAAG,OAAO,CAAC,MAAM,CAAC;IAiEb,sBAAsB,CAAC,KAAK,EAAE;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,iBAAiB,CAAC,EAAE,SAAS,CAAC;QAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,SAAS,CAAC,EAAE,gBAAgB,CAAC;KAC9B,GAAG,OAAO,CAAC;QACV,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CAwHH;AAGD,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|