task-o-matic 0.0.8 ā 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 +200 -0
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +342 -0
- 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.js +39 -0
- package/dist/lib/benchmark/registry.d.ts.map +1 -1
- package/dist/lib/benchmark/registry.js +11 -0
- package/dist/lib/benchmark/types.d.ts +50 -0
- package/dist/lib/benchmark/types.d.ts.map +1 -1
- 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/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +36 -1
- 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.map +1 -1
- package/dist/services/workflow.js +120 -7
- 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 +50 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/results.d.ts +29 -1
- package/dist/types/results.d.ts.map +1 -1
- package/dist/types/workflow-results.d.ts +27 -0
- package/dist/types/workflow-results.d.ts.map +1 -1
- 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;
|
|
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"}
|
package/dist/services/prd.js
CHANGED
|
@@ -102,7 +102,32 @@ class PRDService {
|
|
|
102
102
|
message: "Parsing PRD with AI...",
|
|
103
103
|
});
|
|
104
104
|
const stepStart2 = Date.now();
|
|
105
|
-
|
|
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;
|
|
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;IAsIvB,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;IAyJrB,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;IAiIpB,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"}
|