task-o-matic 0.0.8 → 0.0.10

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 (45) hide show
  1. package/README.md +200 -0
  2. package/dist/commands/benchmark.d.ts.map +1 -1
  3. package/dist/commands/benchmark.js +342 -0
  4. package/dist/commands/tasks/execute-loop.d.ts +3 -0
  5. package/dist/commands/tasks/execute-loop.d.ts.map +1 -0
  6. package/dist/commands/tasks/execute-loop.js +118 -0
  7. package/dist/commands/tasks/index.d.ts +1 -0
  8. package/dist/commands/tasks/index.d.ts.map +1 -1
  9. package/dist/commands/tasks/index.js +1 -0
  10. package/dist/commands/tasks.d.ts.map +1 -1
  11. package/dist/commands/tasks.js +1 -0
  12. package/dist/commands/workflow.js +39 -0
  13. package/dist/lib/benchmark/registry.d.ts.map +1 -1
  14. package/dist/lib/benchmark/registry.js +11 -0
  15. package/dist/lib/benchmark/types.d.ts +50 -0
  16. package/dist/lib/benchmark/types.d.ts.map +1 -1
  17. package/dist/lib/index.d.ts +5 -0
  18. package/dist/lib/index.d.ts.map +1 -1
  19. package/dist/lib/index.js +7 -1
  20. package/dist/lib/task-loop-execution.d.ts +25 -0
  21. package/dist/lib/task-loop-execution.d.ts.map +1 -0
  22. package/dist/lib/task-loop-execution.js +473 -0
  23. package/dist/services/prd.d.ts.map +1 -1
  24. package/dist/services/prd.js +36 -1
  25. package/dist/services/tasks.d.ts.map +1 -1
  26. package/dist/services/tasks.js +90 -3
  27. package/dist/services/workflow-benchmark.d.ts +34 -0
  28. package/dist/services/workflow-benchmark.d.ts.map +1 -0
  29. package/dist/services/workflow-benchmark.js +317 -0
  30. package/dist/services/workflow.d.ts.map +1 -1
  31. package/dist/services/workflow.js +120 -7
  32. package/dist/test/hooks.test.js +19 -10
  33. package/dist/test/task-loop-git.test.d.ts +2 -0
  34. package/dist/test/task-loop-git.test.d.ts.map +1 -0
  35. package/dist/test/task-loop-git.test.js +95 -0
  36. package/dist/test/validation.test.d.ts +2 -0
  37. package/dist/test/validation.test.d.ts.map +1 -0
  38. package/dist/test/validation.test.js +22 -0
  39. package/dist/types/index.d.ts +50 -0
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/results.d.ts +29 -1
  42. package/dist/types/results.d.ts.map +1 -1
  43. package/dist/types/workflow-results.d.ts +27 -0
  44. package/dist/types/workflow-results.d.ts.map +1 -1
  45. 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
+ }
@@ -1 +1 @@
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;IAmLrB,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"}
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"}
@@ -102,7 +102,32 @@ class PRDService {
102
102
  message: "Parsing PRD with AI...",
103
103
  });
104
104
  const stepStart2 = Date.now();
105
- const result = await (0, ai_service_factory_1.getAIOperations)().parsePRD(prdContent, aiConfig, input.promptOverride, input.messageOverride, input.streamingOptions, undefined, // retryConfig
105
+ // Capture metrics
106
+ let tokenUsage;
107
+ let timeToFirstToken;
108
+ // Wrap streaming options to capture metrics
109
+ const metricsStreamingOptions = {
110
+ ...input.streamingOptions,
111
+ onFinish: async (result) => {
112
+ if (result.usage) {
113
+ tokenUsage = {
114
+ prompt: result.usage.inputTokens || result.usage.promptTokens || 0,
115
+ completion: result.usage.outputTokens || result.usage.completionTokens || 0,
116
+ total: result.usage.totalTokens || 0,
117
+ };
118
+ }
119
+ // Call original onFinish if provided
120
+ await input.streamingOptions?.onFinish?.(result);
121
+ },
122
+ onChunk: (chunk) => {
123
+ if (chunk && !timeToFirstToken) {
124
+ timeToFirstToken = Date.now() - stepStart2;
125
+ }
126
+ // Call original onChunk if provided
127
+ input.streamingOptions?.onChunk?.(chunk);
128
+ },
129
+ };
130
+ const result = await (0, ai_service_factory_1.getAIOperations)().parsePRD(prdContent, aiConfig, input.promptOverride, input.messageOverride, metricsStreamingOptions, undefined, // retryConfig
106
131
  workingDir, // Pass working directory to AI operations
107
132
  input.enableFilesystemTools);
108
133
  steps.push({
@@ -161,6 +186,13 @@ class PRDService {
161
186
  message: `Successfully created ${createdTasks.length} tasks from PRD`,
162
187
  });
163
188
  const duration = Date.now() - startTime;
189
+ // Calculate cost if token usage is available
190
+ let cost;
191
+ if (tokenUsage) {
192
+ // Cost calculation would depend on the model
193
+ // For now, we'll leave it undefined and can add pricing later
194
+ // This matches the benchmark pattern where cost calculation is done elsewhere
195
+ }
164
196
  return {
165
197
  success: true,
166
198
  prd: {
@@ -174,6 +206,9 @@ class PRDService {
174
206
  duration,
175
207
  aiProvider: input.aiOptions?.aiProvider || "default",
176
208
  aiModel: input.aiOptions?.aiModel || "default",
209
+ tokenUsage,
210
+ timeToFirstToken,
211
+ cost,
177
212
  },
178
213
  steps,
179
214
  };
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/services/tasks.ts"],"names":[],"mappings":"AAOA,OAAO,EAAiB,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EACL,IAAI,EACJ,gBAAgB,EAIjB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,qBAAa,WAAW;IAKhB,UAAU,CAAC,KAAK,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkFvB,SAAS,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAsBtE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAIzC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIlD,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAIlD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAIxC,UAAU,CACd,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;KAC1B,GACA,OAAO,CAAC,IAAI,CAAC;IAuDV,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,UAAU,CACd,EAAE,EAAE,MAAM,EACV,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GACnD,OAAO,CAAC,gBAAgB,CAAC;IAqDtB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBlD,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BrD,WAAW,CAAC,OAAO,EAAE;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAmClB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAoC7C,WAAW,CACf,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,EACrB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,iBAAiB,CAAC;IAoGvB,SAAS,CACb,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,EACrB,cAAc,CAAC,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,EACxB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,qBAAqB,CAAC,EAAE,OAAO,GAC9B,OAAO,CAAC,eAAe,CAAC;IAuHrB,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,OAAe,EACtB,SAAS,CAAC,EAAE,SAAS,EACrB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,kBAAkB,CAAC;IAoJxB,QAAQ,CACZ,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,EACrB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,cAAc,CAAC;IA+FpB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI5D,4BAA4B,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IAkCtC,WAAW,CACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC;IA6CtC,WAAW,CACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAInE,aAAa,IAAI,OAAO,CAC5B,KAAK,CAAC;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CACH;IAIK,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGvD;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAC"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/services/tasks.ts"],"names":[],"mappings":"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"}