task-o-matic 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/display/progress.d.ts +15 -2
- package/dist/cli/display/progress.d.ts.map +1 -1
- package/dist/cli/display/progress.js +72 -4
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +11 -3
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +19 -4
- package/dist/commands/prd.js +7 -1
- package/dist/commands/tasks/delete.d.ts.map +1 -1
- package/dist/commands/tasks/delete.js +2 -1
- package/dist/commands/tasks/document/add.d.ts.map +1 -1
- package/dist/commands/tasks/document/add.js +2 -1
- package/dist/commands/tasks/document/get.d.ts.map +1 -1
- package/dist/commands/tasks/document/get.js +2 -1
- package/dist/commands/tasks/plan/set.d.ts.map +1 -1
- package/dist/commands/tasks/plan/set.js +11 -3
- package/dist/commands/tasks/show.d.ts.map +1 -1
- package/dist/commands/tasks/show.js +2 -1
- package/dist/commands/tasks/status.d.ts.map +1 -1
- package/dist/commands/tasks/status.js +2 -1
- package/dist/commands/tasks/update.d.ts.map +1 -1
- package/dist/commands/tasks/update.js +7 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +15 -2
- package/dist/lib/ai-service/base-operations.d.ts +8 -0
- package/dist/lib/ai-service/base-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/base-operations.js +23 -10
- package/dist/lib/ai-service/model-provider.d.ts.map +1 -1
- package/dist/lib/ai-service/model-provider.js +37 -6
- package/dist/lib/ai-service/prd-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/prd-operations.js +50 -7
- package/dist/lib/ai-service/task-operations.d.ts +1 -0
- package/dist/lib/ai-service/task-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/task-operations.js +158 -171
- package/dist/lib/benchmark/registry.d.ts.map +1 -1
- package/dist/lib/benchmark/registry.js +6 -10
- package/dist/lib/config-validation.d.ts +215 -0
- package/dist/lib/config-validation.d.ts.map +1 -0
- package/dist/lib/config-validation.js +246 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +30 -7
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +2 -1
- package/dist/lib/storage/file-system.d.ts.map +1 -1
- package/dist/lib/storage/file-system.js +81 -21
- package/dist/lib/task-execution-core.d.ts.map +1 -1
- package/dist/lib/task-execution-core.js +3 -2
- package/dist/services/prd.d.ts +17 -0
- package/dist/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +69 -84
- package/dist/services/tasks.d.ts +315 -1
- package/dist/services/tasks.d.ts.map +1 -1
- package/dist/services/tasks.js +486 -121
- package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
- package/dist/services/workflow-ai-assistant.js +19 -6
- package/dist/services/workflow.d.ts.map +1 -1
- package/dist/services/workflow.js +7 -1
- package/dist/test/lib/ai-service/task-operations.test.d.ts +2 -0
- package/dist/test/lib/ai-service/task-operations.test.d.ts.map +1 -0
- package/dist/test/lib/ai-service/task-operations.test.js +362 -0
- package/dist/test/mocks/mock-ai-operations.d.ts +15 -0
- package/dist/test/mocks/mock-ai-operations.d.ts.map +1 -0
- package/dist/test/mocks/mock-ai-operations.js +107 -0
- package/dist/test/mocks/mock-context-builder.d.ts +10 -0
- package/dist/test/mocks/mock-context-builder.d.ts.map +1 -0
- package/dist/test/mocks/mock-context-builder.js +81 -0
- package/dist/test/mocks/mock-model-provider.d.ts +7 -0
- package/dist/test/mocks/mock-model-provider.d.ts.map +1 -0
- package/dist/test/mocks/mock-model-provider.js +21 -0
- package/dist/test/mocks/mock-service-factory.d.ts +11 -0
- package/dist/test/mocks/mock-service-factory.d.ts.map +1 -0
- package/dist/test/mocks/mock-service-factory.js +61 -0
- package/dist/test/mocks/mock-storage.d.ts +50 -0
- package/dist/test/mocks/mock-storage.d.ts.map +1 -0
- package/dist/test/mocks/mock-storage.js +145 -0
- package/dist/test/services/task-service.test.d.ts +2 -0
- package/dist/test/services/task-service.test.d.ts.map +1 -0
- package/dist/test/services/task-service.test.js +459 -0
- package/dist/test/test-mock-setup.d.ts +26 -0
- package/dist/test/test-mock-setup.d.ts.map +1 -0
- package/dist/test/test-mock-setup.js +41 -0
- package/dist/test/test-setup.d.ts +9 -0
- package/dist/test/test-setup.d.ts.map +1 -0
- package/dist/test/test-setup.js +44 -0
- package/dist/test/test-utils.d.ts +22 -0
- package/dist/test/test-utils.d.ts.map +1 -0
- package/dist/test/test-utils.js +37 -0
- package/dist/test/utils/ai-operation-utility.test.d.ts +2 -0
- package/dist/test/utils/ai-operation-utility.test.d.ts.map +1 -0
- package/dist/test/utils/ai-operation-utility.test.js +290 -0
- package/dist/test/utils/error-handling.test.d.ts +2 -0
- package/dist/test/utils/error-handling.test.d.ts.map +1 -0
- package/dist/test/utils/error-handling.test.js +231 -0
- package/dist/utils/ai-operation-utility.d.ts +142 -0
- package/dist/utils/ai-operation-utility.d.ts.map +1 -0
- package/dist/utils/ai-operation-utility.js +279 -0
- package/dist/utils/ai-service-factory.d.ts +10 -0
- package/dist/utils/ai-service-factory.d.ts.map +1 -1
- package/dist/utils/ai-service-factory.js +19 -1
- package/dist/utils/cli-validators.d.ts +2 -2
- package/dist/utils/cli-validators.d.ts.map +1 -1
- package/dist/utils/cli-validators.js +7 -6
- package/dist/utils/error-utils.d.ts +3 -3
- package/dist/utils/error-utils.d.ts.map +1 -1
- package/dist/utils/error-utils.js +5 -4
- package/dist/utils/file-utils.d.ts +27 -4
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +46 -6
- package/dist/utils/id-generator.d.ts +1 -1
- package/dist/utils/id-generator.d.ts.map +1 -1
- package/dist/utils/id-generator.js +8 -2
- package/dist/utils/metadata-utils.d.ts +40 -0
- package/dist/utils/metadata-utils.d.ts.map +1 -0
- package/dist/utils/metadata-utils.js +43 -0
- package/dist/utils/model-executor-parser.d.ts +1 -1
- package/dist/utils/model-executor-parser.d.ts.map +1 -1
- package/dist/utils/model-executor-parser.js +3 -2
- package/dist/utils/storage-utils.d.ts +3 -3
- package/dist/utils/storage-utils.d.ts.map +1 -1
- package/dist/utils/storage-utils.js +7 -6
- package/dist/utils/task-o-matic-error.d.ts +206 -0
- package/dist/utils/task-o-matic-error.d.ts.map +1 -0
- package/dist/utils/task-o-matic-error.js +304 -0
- package/package.json +7 -2
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FileSystemStorage = void 0;
|
|
4
4
|
const config_1 = require("../config");
|
|
5
5
|
const storage_callbacks_1 = require("./storage-callbacks");
|
|
6
|
+
const task_o_matic_error_1 = require("../../utils/task-o-matic-error");
|
|
6
7
|
class FileSystemStorage {
|
|
7
8
|
callbacks;
|
|
8
9
|
constructor(callbacks) {
|
|
@@ -16,30 +17,54 @@ class FileSystemStorage {
|
|
|
16
17
|
}
|
|
17
18
|
validateTaskId(taskId) {
|
|
18
19
|
if (!taskId || typeof taskId !== "string" || taskId.trim() === "") {
|
|
19
|
-
throw
|
|
20
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Task ID must be a non-empty string", {
|
|
21
|
+
context: "validateTaskId",
|
|
22
|
+
suggestions: ["Provide a valid task ID string"],
|
|
23
|
+
metadata: { taskId },
|
|
24
|
+
});
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
validateTaskRequest(task) {
|
|
23
28
|
if (!task || typeof task !== "object") {
|
|
24
|
-
throw
|
|
29
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Task request must be a valid object", {
|
|
30
|
+
context: "validateTaskRequest",
|
|
31
|
+
suggestions: ["Provide a valid task request object"],
|
|
32
|
+
});
|
|
25
33
|
}
|
|
26
34
|
if (!task.title ||
|
|
27
35
|
typeof task.title !== "string" ||
|
|
28
36
|
task.title.trim() === "") {
|
|
29
|
-
throw
|
|
37
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Task title is required and must be a non-empty string", {
|
|
38
|
+
context: "validateTaskRequest - title validation",
|
|
39
|
+
suggestions: ["Provide a non-empty title for the task"],
|
|
40
|
+
});
|
|
30
41
|
}
|
|
31
42
|
if (task.parentId && typeof task.parentId !== "string") {
|
|
32
|
-
throw
|
|
43
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Parent ID must be a string if provided", {
|
|
44
|
+
context: "validateTaskRequest - parentId validation",
|
|
45
|
+
suggestions: ["Provide a valid parent ID string or omit the field"],
|
|
46
|
+
metadata: { parentId: task.parentId },
|
|
47
|
+
});
|
|
33
48
|
}
|
|
34
49
|
if (task.estimatedEffort &&
|
|
35
50
|
!["small", "medium", "large"].includes(task.estimatedEffort)) {
|
|
36
|
-
throw
|
|
51
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Estimated effort must be 'small', 'medium', or 'large'", {
|
|
52
|
+
context: "validateTaskRequest - estimatedEffort validation",
|
|
53
|
+
suggestions: ["Use 'small', 'medium', or 'large' for estimatedEffort"],
|
|
54
|
+
metadata: { estimatedEffort: task.estimatedEffort },
|
|
55
|
+
});
|
|
37
56
|
}
|
|
38
57
|
if (task.dependencies && !Array.isArray(task.dependencies)) {
|
|
39
|
-
throw
|
|
58
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Dependencies must be an array if provided", {
|
|
59
|
+
context: "validateTaskRequest - dependencies validation",
|
|
60
|
+
suggestions: ["Provide dependencies as an array of task IDs"],
|
|
61
|
+
});
|
|
40
62
|
}
|
|
41
63
|
if (task.tags && !Array.isArray(task.tags)) {
|
|
42
|
-
throw
|
|
64
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Tags must be an array if provided", {
|
|
65
|
+
context: "validateTaskRequest - tags validation",
|
|
66
|
+
suggestions: ["Provide tags as an array of strings"],
|
|
67
|
+
});
|
|
43
68
|
}
|
|
44
69
|
}
|
|
45
70
|
async loadTasksData() {
|
|
@@ -60,7 +85,7 @@ class FileSystemStorage {
|
|
|
60
85
|
await this.callbacks.write("tasks.json", JSON.stringify(data, null, 2));
|
|
61
86
|
}
|
|
62
87
|
catch (error) {
|
|
63
|
-
throw
|
|
88
|
+
throw (0, task_o_matic_error_1.formatStorageError)("write tasks.json", error instanceof Error ? error : undefined);
|
|
64
89
|
}
|
|
65
90
|
}
|
|
66
91
|
findTaskInHierarchy(tasks, id) {
|
|
@@ -111,7 +136,15 @@ class FileSystemStorage {
|
|
|
111
136
|
if (task.parentId) {
|
|
112
137
|
const parentResult = this.findTaskInHierarchy(data.tasks, task.parentId);
|
|
113
138
|
if (!parentResult.task) {
|
|
114
|
-
throw
|
|
139
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND, `Parent task with ID ${task.parentId} not found`, {
|
|
140
|
+
context: "createTask - parent validation",
|
|
141
|
+
suggestions: [
|
|
142
|
+
"Create the parent task first",
|
|
143
|
+
"Check that the parent ID is correct",
|
|
144
|
+
"List all tasks to see available parent IDs",
|
|
145
|
+
],
|
|
146
|
+
metadata: { parentId: task.parentId },
|
|
147
|
+
});
|
|
115
148
|
}
|
|
116
149
|
const siblingCount = (parentResult.task.subtasks?.length || 0) + 1;
|
|
117
150
|
id = `${task.parentId}.${siblingCount}`;
|
|
@@ -129,11 +162,26 @@ class FileSystemStorage {
|
|
|
129
162
|
for (const depId of task.dependencies) {
|
|
130
163
|
const depExists = this.taskExists(data.tasks, depId);
|
|
131
164
|
if (!depExists) {
|
|
132
|
-
throw
|
|
165
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND, `Dependency task not found: ${depId}`, {
|
|
166
|
+
context: "createTask - dependency validation",
|
|
167
|
+
suggestions: [
|
|
168
|
+
"Create the dependency task first",
|
|
169
|
+
"Check that the dependency ID is correct",
|
|
170
|
+
"Remove the dependency from the task",
|
|
171
|
+
],
|
|
172
|
+
metadata: { dependencyId: depId, taskDependencies: task.dependencies },
|
|
173
|
+
});
|
|
133
174
|
}
|
|
134
175
|
}
|
|
135
176
|
if (this.wouldCreateCircularDependency(data.tasks, id, task.dependencies)) {
|
|
136
|
-
throw
|
|
177
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.STORAGE_INTEGRITY_ERROR, `Circular dependency detected for task ${id}`, {
|
|
178
|
+
context: "createTask - circular dependency check",
|
|
179
|
+
suggestions: [
|
|
180
|
+
"Remove circular dependencies from the task",
|
|
181
|
+
"Review the dependency chain",
|
|
182
|
+
],
|
|
183
|
+
metadata: { taskId: id, dependencies: task.dependencies },
|
|
184
|
+
});
|
|
137
185
|
}
|
|
138
186
|
}
|
|
139
187
|
let contentFile;
|
|
@@ -184,7 +232,10 @@ class FileSystemStorage {
|
|
|
184
232
|
async updateTask(id, updates) {
|
|
185
233
|
this.validateTaskId(id);
|
|
186
234
|
if (!updates || typeof updates !== "object") {
|
|
187
|
-
throw
|
|
235
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Updates must be a valid object", {
|
|
236
|
+
context: "updateTask - updates validation",
|
|
237
|
+
suggestions: ["Provide a valid updates object with task properties"],
|
|
238
|
+
});
|
|
188
239
|
}
|
|
189
240
|
const data = await this.loadTasksData();
|
|
190
241
|
const result = this.findTaskInHierarchy(data.tasks, id);
|
|
@@ -314,28 +365,34 @@ class FileSystemStorage {
|
|
|
314
365
|
async saveTaskContent(taskId, content) {
|
|
315
366
|
this.validateTaskId(taskId);
|
|
316
367
|
if (typeof content !== "string") {
|
|
317
|
-
throw
|
|
368
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Content must be a string", {
|
|
369
|
+
context: "Content validation",
|
|
370
|
+
suggestions: ["Provide content as a string"],
|
|
371
|
+
});
|
|
318
372
|
}
|
|
319
373
|
const contentFileName = `tasks/${taskId}.md`;
|
|
320
374
|
try {
|
|
321
375
|
await this.callbacks.write(contentFileName, content);
|
|
322
376
|
}
|
|
323
377
|
catch (error) {
|
|
324
|
-
throw
|
|
378
|
+
throw (0, task_o_matic_error_1.formatStorageError)("write task content", error instanceof Error ? error : undefined);
|
|
325
379
|
}
|
|
326
380
|
return contentFileName;
|
|
327
381
|
}
|
|
328
382
|
async saveEnhancedTaskContent(taskId, content) {
|
|
329
383
|
this.validateTaskId(taskId);
|
|
330
384
|
if (typeof content !== "string") {
|
|
331
|
-
throw
|
|
385
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Content must be a string", {
|
|
386
|
+
context: "Content validation",
|
|
387
|
+
suggestions: ["Provide content as a string"],
|
|
388
|
+
});
|
|
332
389
|
}
|
|
333
390
|
const contentFileName = `tasks/enhanced/${taskId}.md`;
|
|
334
391
|
try {
|
|
335
392
|
await this.callbacks.write(contentFileName, content);
|
|
336
393
|
}
|
|
337
394
|
catch (error) {
|
|
338
|
-
throw
|
|
395
|
+
throw (0, task_o_matic_error_1.formatStorageError)("write enhanced task content", error instanceof Error ? error : undefined);
|
|
339
396
|
}
|
|
340
397
|
return contentFileName;
|
|
341
398
|
}
|
|
@@ -496,7 +553,7 @@ class FileSystemStorage {
|
|
|
496
553
|
await this.callbacks.write(planFile, JSON.stringify(planData, null, 2));
|
|
497
554
|
}
|
|
498
555
|
catch (error) {
|
|
499
|
-
throw
|
|
556
|
+
throw (0, task_o_matic_error_1.formatStorageError)(`write plan for task ${taskId}`, error instanceof Error ? error : undefined);
|
|
500
557
|
}
|
|
501
558
|
}
|
|
502
559
|
async getPlan(taskId) {
|
|
@@ -509,7 +566,7 @@ class FileSystemStorage {
|
|
|
509
566
|
return JSON.parse(content);
|
|
510
567
|
}
|
|
511
568
|
catch (error) {
|
|
512
|
-
throw
|
|
569
|
+
throw (0, task_o_matic_error_1.formatStorageError)(`read plan for task ${taskId}`, error instanceof Error ? error : undefined);
|
|
513
570
|
}
|
|
514
571
|
}
|
|
515
572
|
async listPlans() {
|
|
@@ -534,7 +591,7 @@ class FileSystemStorage {
|
|
|
534
591
|
return plans.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
535
592
|
}
|
|
536
593
|
catch (error) {
|
|
537
|
-
throw
|
|
594
|
+
throw (0, task_o_matic_error_1.formatStorageError)("list plans", error instanceof Error ? error : undefined);
|
|
538
595
|
}
|
|
539
596
|
}
|
|
540
597
|
async deletePlan(taskId) {
|
|
@@ -551,14 +608,17 @@ class FileSystemStorage {
|
|
|
551
608
|
async saveTaskDocumentation(taskId, documentation) {
|
|
552
609
|
this.validateTaskId(taskId);
|
|
553
610
|
if (typeof documentation !== "string") {
|
|
554
|
-
throw
|
|
611
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "Documentation must be a string", {
|
|
612
|
+
context: "Documentation validation",
|
|
613
|
+
suggestions: ["Provide documentation as a string"],
|
|
614
|
+
});
|
|
555
615
|
}
|
|
556
616
|
const documentationFileName = `docs/tasks/${taskId}.md`;
|
|
557
617
|
try {
|
|
558
618
|
await this.callbacks.write(documentationFileName, documentation);
|
|
559
619
|
}
|
|
560
620
|
catch (error) {
|
|
561
|
-
throw
|
|
621
|
+
throw (0, task_o_matic_error_1.formatStorageError)("write task documentation", error instanceof Error ? error : undefined);
|
|
562
622
|
}
|
|
563
623
|
return documentationFileName;
|
|
564
624
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-execution-core.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution-core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-execution-core.d.ts","sourceRoot":"","sources":["../../src/lib/task-execution-core.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EAKpB,MAAM,UAAU,CAAC;AAWlB;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA+D9B"}
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.executeTaskCore = executeTaskCore;
|
|
7
|
+
const task_o_matic_error_1 = require("../utils/task-o-matic-error");
|
|
7
8
|
const tasks_1 = require("../services/tasks");
|
|
8
9
|
const executor_factory_1 = require("./executors/executor-factory");
|
|
9
10
|
const validation_1 = require("./validation");
|
|
@@ -23,7 +24,7 @@ async function executeTaskCore(taskId, config) {
|
|
|
23
24
|
// Load task
|
|
24
25
|
const task = await tasks_1.taskService.getTask(taskId);
|
|
25
26
|
if (!task) {
|
|
26
|
-
throw
|
|
27
|
+
throw (0, task_o_matic_error_1.formatTaskNotFoundError)(taskId);
|
|
27
28
|
}
|
|
28
29
|
// Check if task has subtasks and should execute them recursively
|
|
29
30
|
if (executeSubtasks && !customMessage) {
|
|
@@ -254,7 +255,7 @@ async function executeSingleAttempt(task, config, attempts, planContent, attempt
|
|
|
254
255
|
retryContext,
|
|
255
256
|
});
|
|
256
257
|
if (!promptResult.success) {
|
|
257
|
-
throw
|
|
258
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.CONFIGURATION_ERROR, `Failed to build execution prompt: ${promptResult.error}`);
|
|
258
259
|
}
|
|
259
260
|
executionMessage = promptResult.prompt;
|
|
260
261
|
}
|
package/dist/services/prd.d.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
+
import { getAIOperations, getStorage } from "../utils/ai-service-factory";
|
|
1
2
|
import { AIOptions } from "../utils/ai-config-builder";
|
|
2
3
|
import { StreamingOptions } from "../types";
|
|
3
4
|
import { PRDParseResult } from "../types/results";
|
|
4
5
|
import { ProgressCallback } from "../types/callbacks";
|
|
6
|
+
/**
|
|
7
|
+
* Dependencies for PRDService
|
|
8
|
+
*/
|
|
9
|
+
export interface PRDServiceDependencies {
|
|
10
|
+
storage?: ReturnType<typeof getStorage>;
|
|
11
|
+
aiOperations?: ReturnType<typeof getAIOperations>;
|
|
12
|
+
}
|
|
5
13
|
/**
|
|
6
14
|
* PRDService - Business logic for PRD operations
|
|
7
15
|
* Handles PRD parsing, task extraction, and PRD improvement
|
|
8
16
|
*/
|
|
9
17
|
export declare class PRDService {
|
|
18
|
+
private storage;
|
|
19
|
+
private aiOperations;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new PRDService
|
|
22
|
+
*
|
|
23
|
+
* @param dependencies - Optional dependencies to inject (for testing)
|
|
24
|
+
*/
|
|
25
|
+
constructor(dependencies?: PRDServiceDependencies);
|
|
10
26
|
parsePRD(input: {
|
|
11
27
|
file: string;
|
|
12
28
|
workingDirectory?: string;
|
|
@@ -98,5 +114,6 @@ export declare class PRDService {
|
|
|
98
114
|
};
|
|
99
115
|
}>;
|
|
100
116
|
}
|
|
117
|
+
export declare function getPRDService(): PRDService;
|
|
101
118
|
export declare const prdService: PRDService;
|
|
102
119
|
//# 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":"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"}
|
package/dist/services/prd.js
CHANGED
|
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.prdService = exports.PRDService = void 0;
|
|
37
|
+
exports.getPRDService = getPRDService;
|
|
38
|
+
const task_o_matic_error_1 = require("../utils/task-o-matic-error");
|
|
37
39
|
const fs_1 = require("fs");
|
|
38
40
|
const path_1 = require("path");
|
|
39
41
|
const ai_service_factory_1 = require("../utils/ai-service-factory");
|
|
@@ -47,6 +49,18 @@ const file_utils_1 = require("../utils/file-utils");
|
|
|
47
49
|
* Handles PRD parsing, task extraction, and PRD improvement
|
|
48
50
|
*/
|
|
49
51
|
class PRDService {
|
|
52
|
+
storage;
|
|
53
|
+
aiOperations;
|
|
54
|
+
/**
|
|
55
|
+
* Create a new PRDService
|
|
56
|
+
*
|
|
57
|
+
* @param dependencies - Optional dependencies to inject (for testing)
|
|
58
|
+
*/
|
|
59
|
+
constructor(dependencies = {}) {
|
|
60
|
+
// Use injected dependencies or fall back to singletons
|
|
61
|
+
this.storage = dependencies.storage ?? (0, ai_service_factory_1.getStorage)();
|
|
62
|
+
this.aiOperations = dependencies.aiOperations ?? (0, ai_service_factory_1.getAIOperations)();
|
|
63
|
+
}
|
|
50
64
|
async parsePRD(input) {
|
|
51
65
|
const startTime = Date.now();
|
|
52
66
|
const steps = [];
|
|
@@ -59,7 +73,9 @@ class PRDService {
|
|
|
59
73
|
// Ensure we're in a task-o-matic project
|
|
60
74
|
const taskOMaticDir = config_1.configManager.getTaskOMaticDir();
|
|
61
75
|
if (!(0, fs_1.existsSync)(taskOMaticDir)) {
|
|
62
|
-
throw
|
|
76
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.CONFIGURATION_ERROR, "Not a task-o-matic project. Run 'task-o-matic init init' first.", {
|
|
77
|
+
suggestions: ["Run `task-o-matic init init` in your project root."],
|
|
78
|
+
});
|
|
63
79
|
}
|
|
64
80
|
// Set working directory and reload config (DRY fix 1.4)
|
|
65
81
|
const workingDir = input.workingDirectory || process.cwd();
|
|
@@ -94,7 +110,9 @@ class PRDService {
|
|
|
94
110
|
// Validate AI provider if specified
|
|
95
111
|
if (input.aiOptions?.aiProvider &&
|
|
96
112
|
!(0, validation_1.isValidAIProvider)(input.aiOptions.aiProvider)) {
|
|
97
|
-
throw
|
|
113
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR, `Invalid AI provider: ${input.aiOptions.aiProvider}`, {
|
|
114
|
+
suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
|
|
115
|
+
});
|
|
98
116
|
}
|
|
99
117
|
const aiConfig = (0, ai_config_builder_1.buildAIConfig)(input.aiOptions);
|
|
100
118
|
input.callbacks?.onProgress?.({
|
|
@@ -104,7 +122,7 @@ class PRDService {
|
|
|
104
122
|
const stepStart2 = Date.now();
|
|
105
123
|
// Use utility to wrap streaming options and capture metrics (DRY fix 1.1)
|
|
106
124
|
const { options: metricsStreamingOptions, getMetrics } = (0, streaming_utils_1.createMetricsStreamingOptions)(input.streamingOptions, stepStart2);
|
|
107
|
-
const result = await
|
|
125
|
+
const result = await this.aiOperations.parsePRD(prdContent, aiConfig, input.promptOverride, input.messageOverride, metricsStreamingOptions, undefined, // retryConfig
|
|
108
126
|
workingDir, // Pass working directory to AI operations
|
|
109
127
|
input.enableFilesystemTools);
|
|
110
128
|
// Extract metrics after AI call
|
|
@@ -130,7 +148,7 @@ class PRDService {
|
|
|
130
148
|
current: i + 1,
|
|
131
149
|
total: result.tasks.length,
|
|
132
150
|
});
|
|
133
|
-
const createdTask = await
|
|
151
|
+
const createdTask = await this.storage.createTask({
|
|
134
152
|
id: task.id, // Preserve AI-generated ID for dependencies
|
|
135
153
|
title: task.title,
|
|
136
154
|
description: task.description,
|
|
@@ -152,7 +170,7 @@ class PRDService {
|
|
|
152
170
|
aiModel: input.aiOptions?.aiModel,
|
|
153
171
|
generatedAt: Date.now(),
|
|
154
172
|
};
|
|
155
|
-
await
|
|
173
|
+
await this.storage.saveTaskAIMetadata(aiMetadata);
|
|
156
174
|
}
|
|
157
175
|
steps.push({
|
|
158
176
|
step: "Create Tasks",
|
|
@@ -209,14 +227,16 @@ class PRDService {
|
|
|
209
227
|
const prdContent = (0, fs_1.readFileSync)(input.file, "utf-8");
|
|
210
228
|
if (input.aiOptions?.aiProvider &&
|
|
211
229
|
!(0, validation_1.isValidAIProvider)(input.aiOptions.aiProvider)) {
|
|
212
|
-
throw
|
|
230
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR, `Invalid AI provider: ${input.aiOptions.aiProvider}`, {
|
|
231
|
+
suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
|
|
232
|
+
});
|
|
213
233
|
}
|
|
214
234
|
const aiConfig = (0, ai_config_builder_1.buildAIConfig)(input.aiOptions);
|
|
215
235
|
input.callbacks?.onProgress?.({
|
|
216
236
|
type: "progress",
|
|
217
237
|
message: "Analyzing PRD with AI...",
|
|
218
238
|
});
|
|
219
|
-
const questions = await
|
|
239
|
+
const questions = await this.aiOperations.generatePRDQuestions(prdContent, aiConfig, input.promptOverride, input.messageOverride, input.streamingOptions, undefined, workingDir, input.enableFilesystemTools);
|
|
220
240
|
input.callbacks?.onProgress?.({
|
|
221
241
|
type: "completed",
|
|
222
242
|
message: `Generated ${questions.length} questions`,
|
|
@@ -241,14 +261,16 @@ class PRDService {
|
|
|
241
261
|
// Validate AI provider if specified
|
|
242
262
|
if (input.aiOptions?.aiProvider &&
|
|
243
263
|
!(0, validation_1.isValidAIProvider)(input.aiOptions.aiProvider)) {
|
|
244
|
-
throw
|
|
264
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.AI_CONFIGURATION_ERROR, `Invalid AI provider: ${input.aiOptions.aiProvider}`, {
|
|
265
|
+
suggestions: ["Use a valid AI provider, e.g., 'openai', 'anthropic'"],
|
|
266
|
+
});
|
|
245
267
|
}
|
|
246
268
|
const aiConfig = (0, ai_config_builder_1.buildAIConfig)(input.aiOptions);
|
|
247
269
|
input.callbacks?.onProgress?.({
|
|
248
270
|
type: "progress",
|
|
249
271
|
message: "Calling AI to improve PRD...",
|
|
250
272
|
});
|
|
251
|
-
const improvedPRD = await
|
|
273
|
+
const improvedPRD = await this.aiOperations.reworkPRD(prdContent, input.feedback, aiConfig, input.promptOverride, input.messageOverride, input.streamingOptions, undefined, // retryConfig
|
|
252
274
|
workingDir, // Pass working directory to AI operations
|
|
253
275
|
input.enableFilesystemTools);
|
|
254
276
|
input.callbacks?.onProgress?.({
|
|
@@ -311,7 +333,7 @@ class PRDService {
|
|
|
311
333
|
// User mode: return questions for CLI to prompt user
|
|
312
334
|
// Answers should be provided in input.answers
|
|
313
335
|
if (!input.answers || Object.keys(input.answers).length === 0) {
|
|
314
|
-
throw
|
|
336
|
+
throw (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_INPUT, "User mode selected but no answers provided. CLI layer should collect answers.");
|
|
315
337
|
}
|
|
316
338
|
answers = input.answers;
|
|
317
339
|
}
|
|
@@ -324,7 +346,7 @@ class PRDService {
|
|
|
324
346
|
const prdContent = (0, fs_1.readFileSync)(input.file, "utf-8");
|
|
325
347
|
// Use questionAIOptions if provided, otherwise use main aiOptions
|
|
326
348
|
const answeringAIConfig = (0, ai_config_builder_1.buildAIConfig)(input.questionAIOptions || input.aiOptions);
|
|
327
|
-
answers = await
|
|
349
|
+
answers = await this.aiOperations.answerPRDQuestions(prdContent, questions, answeringAIConfig, {
|
|
328
350
|
stackInfo,
|
|
329
351
|
}, input.streamingOptions);
|
|
330
352
|
}
|
|
@@ -359,48 +381,23 @@ class PRDService {
|
|
|
359
381
|
}
|
|
360
382
|
async generatePRD(input) {
|
|
361
383
|
const startTime = Date.now();
|
|
362
|
-
let tokenUsage;
|
|
363
|
-
let timeToFirstToken;
|
|
364
|
-
let cost;
|
|
365
384
|
input.callbacks?.onProgress?.({
|
|
366
385
|
type: "started",
|
|
367
386
|
message: "Generating PRD...",
|
|
368
387
|
});
|
|
369
|
-
//
|
|
370
|
-
const metricsStreamingOptions =
|
|
371
|
-
...input.streamingOptions,
|
|
372
|
-
onFinish: async (result) => {
|
|
373
|
-
if (result.usage) {
|
|
374
|
-
tokenUsage = {
|
|
375
|
-
prompt: result.usage.inputTokens || result.usage.promptTokens || 0,
|
|
376
|
-
completion: result.usage.outputTokens || result.usage.completionTokens || 0,
|
|
377
|
-
total: result.usage.totalTokens || 0,
|
|
378
|
-
};
|
|
379
|
-
// Simple cost estimation placeholder
|
|
380
|
-
if (tokenUsage.total > 0) {
|
|
381
|
-
cost = tokenUsage.total * 0.000001;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
await input.streamingOptions?.onFinish?.(result);
|
|
385
|
-
},
|
|
386
|
-
onChunk: (chunk) => {
|
|
387
|
-
if (chunk && !timeToFirstToken) {
|
|
388
|
-
timeToFirstToken = Date.now() - startTime;
|
|
389
|
-
}
|
|
390
|
-
input.streamingOptions?.onChunk?.(chunk);
|
|
391
|
-
},
|
|
392
|
-
};
|
|
388
|
+
// Use utility to wrap streaming options and capture metrics
|
|
389
|
+
const { options: metricsStreamingOptions, getMetrics } = (0, streaming_utils_1.createMetricsStreamingOptions)(input.streamingOptions, startTime);
|
|
393
390
|
const aiConfig = (0, ai_config_builder_1.buildAIConfig)(input.aiOptions);
|
|
394
|
-
const content = await
|
|
395
|
-
//
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
391
|
+
const content = await this.aiOperations.generatePRD(input.description, aiConfig, undefined, undefined, metricsStreamingOptions);
|
|
392
|
+
// Get metrics after AI operation
|
|
393
|
+
const { tokenUsage, timeToFirstToken } = getMetrics();
|
|
394
|
+
// Calculate cost if needed
|
|
395
|
+
let cost;
|
|
396
|
+
if (tokenUsage && tokenUsage.total > 0) {
|
|
397
|
+
cost = tokenUsage.total * 0.000001; // Placeholder cost calculation
|
|
400
398
|
}
|
|
401
|
-
|
|
402
|
-
const path = (0,
|
|
403
|
-
(0, fs_1.writeFileSync)(path, content);
|
|
399
|
+
// Save file using utility
|
|
400
|
+
const path = (0, file_utils_1.savePRDFile)(content, input.filename, input.outputDir);
|
|
404
401
|
input.callbacks?.onProgress?.({
|
|
405
402
|
type: "completed",
|
|
406
403
|
message: `PRD generated and saved to ${path}`,
|
|
@@ -418,47 +415,23 @@ class PRDService {
|
|
|
418
415
|
}
|
|
419
416
|
async combinePRDs(input) {
|
|
420
417
|
const startTime = Date.now();
|
|
421
|
-
let tokenUsage;
|
|
422
|
-
let timeToFirstToken;
|
|
423
|
-
let cost;
|
|
424
418
|
input.callbacks?.onProgress?.({
|
|
425
419
|
type: "started",
|
|
426
420
|
message: "Combining PRDs...",
|
|
427
421
|
});
|
|
428
|
-
//
|
|
429
|
-
const metricsStreamingOptions =
|
|
430
|
-
...input.streamingOptions,
|
|
431
|
-
onFinish: async (result) => {
|
|
432
|
-
if (result.usage) {
|
|
433
|
-
tokenUsage = {
|
|
434
|
-
prompt: result.usage.inputTokens || result.usage.promptTokens || 0,
|
|
435
|
-
completion: result.usage.outputTokens || result.usage.completionTokens || 0,
|
|
436
|
-
total: result.usage.totalTokens || 0,
|
|
437
|
-
};
|
|
438
|
-
if (tokenUsage.total > 0) {
|
|
439
|
-
cost = tokenUsage.total * 0.000001;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
await input.streamingOptions?.onFinish?.(result);
|
|
443
|
-
},
|
|
444
|
-
onChunk: (chunk) => {
|
|
445
|
-
if (chunk && !timeToFirstToken) {
|
|
446
|
-
timeToFirstToken = Date.now() - startTime;
|
|
447
|
-
}
|
|
448
|
-
input.streamingOptions?.onChunk?.(chunk);
|
|
449
|
-
},
|
|
450
|
-
};
|
|
422
|
+
// Use utility to wrap streaming options and capture metrics
|
|
423
|
+
const { options: metricsStreamingOptions, getMetrics } = (0, streaming_utils_1.createMetricsStreamingOptions)(input.streamingOptions, startTime);
|
|
451
424
|
const aiConfig = (0, ai_config_builder_1.buildAIConfig)(input.aiOptions);
|
|
452
|
-
const content = await
|
|
453
|
-
//
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
425
|
+
const content = await this.aiOperations.combinePRDs(input.prds, input.originalDescription, aiConfig, undefined, undefined, metricsStreamingOptions);
|
|
426
|
+
// Get metrics after AI operation
|
|
427
|
+
const { tokenUsage, timeToFirstToken } = getMetrics();
|
|
428
|
+
// Calculate cost if needed
|
|
429
|
+
let cost;
|
|
430
|
+
if (tokenUsage && tokenUsage.total > 0) {
|
|
431
|
+
cost = tokenUsage.total * 0.000001;
|
|
458
432
|
}
|
|
459
|
-
|
|
460
|
-
const path = (0,
|
|
461
|
-
(0, fs_1.writeFileSync)(path, content);
|
|
433
|
+
// Save file using utility (defaults to "prd.md" if no filename, so we provide the default for combinePRDs)
|
|
434
|
+
const path = (0, file_utils_1.savePRDFile)(content, input.filename || "prd-master.md", input.outputDir);
|
|
462
435
|
input.callbacks?.onProgress?.({
|
|
463
436
|
type: "completed",
|
|
464
437
|
message: `Master PRD saved to ${path}`,
|
|
@@ -476,5 +449,17 @@ class PRDService {
|
|
|
476
449
|
}
|
|
477
450
|
}
|
|
478
451
|
exports.PRDService = PRDService;
|
|
479
|
-
//
|
|
480
|
-
|
|
452
|
+
// Lazy singleton instance - only created when first accessed
|
|
453
|
+
let prdServiceInstance;
|
|
454
|
+
function getPRDService() {
|
|
455
|
+
if (!prdServiceInstance) {
|
|
456
|
+
prdServiceInstance = new PRDService();
|
|
457
|
+
}
|
|
458
|
+
return prdServiceInstance;
|
|
459
|
+
}
|
|
460
|
+
// Backward compatibility: export as const but use getter
|
|
461
|
+
exports.prdService = new Proxy({}, {
|
|
462
|
+
get(target, prop) {
|
|
463
|
+
return getPRDService()[prop];
|
|
464
|
+
},
|
|
465
|
+
});
|