task-o-matic 0.0.14 → 0.0.15

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 (110) hide show
  1. package/dist/cli/display/progress.d.ts +15 -2
  2. package/dist/cli/display/progress.d.ts.map +1 -1
  3. package/dist/cli/display/progress.js +72 -4
  4. package/dist/commands/benchmark.d.ts.map +1 -1
  5. package/dist/commands/benchmark.js +11 -3
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +19 -4
  8. package/dist/commands/prd.js +7 -1
  9. package/dist/commands/tasks/delete.d.ts.map +1 -1
  10. package/dist/commands/tasks/delete.js +2 -1
  11. package/dist/commands/tasks/document/add.d.ts.map +1 -1
  12. package/dist/commands/tasks/document/add.js +2 -1
  13. package/dist/commands/tasks/document/get.d.ts.map +1 -1
  14. package/dist/commands/tasks/document/get.js +2 -1
  15. package/dist/commands/tasks/plan/set.d.ts.map +1 -1
  16. package/dist/commands/tasks/plan/set.js +11 -3
  17. package/dist/commands/tasks/show.d.ts.map +1 -1
  18. package/dist/commands/tasks/show.js +2 -1
  19. package/dist/commands/tasks/status.d.ts.map +1 -1
  20. package/dist/commands/tasks/status.js +2 -1
  21. package/dist/commands/tasks/update.d.ts.map +1 -1
  22. package/dist/commands/tasks/update.js +7 -1
  23. package/dist/lib/ai-service/model-provider.d.ts.map +1 -1
  24. package/dist/lib/ai-service/model-provider.js +37 -6
  25. package/dist/lib/ai-service/task-operations.d.ts +1 -0
  26. package/dist/lib/ai-service/task-operations.d.ts.map +1 -1
  27. package/dist/lib/ai-service/task-operations.js +135 -173
  28. package/dist/lib/benchmark/registry.d.ts.map +1 -1
  29. package/dist/lib/benchmark/registry.js +6 -10
  30. package/dist/lib/config-validation.d.ts +215 -0
  31. package/dist/lib/config-validation.d.ts.map +1 -0
  32. package/dist/lib/config-validation.js +246 -0
  33. package/dist/lib/config.d.ts.map +1 -1
  34. package/dist/lib/config.js +19 -5
  35. package/dist/lib/storage/file-system.d.ts.map +1 -1
  36. package/dist/lib/storage/file-system.js +81 -21
  37. package/dist/lib/task-execution-core.d.ts.map +1 -1
  38. package/dist/lib/task-execution-core.js +3 -2
  39. package/dist/services/prd.d.ts +17 -0
  40. package/dist/services/prd.d.ts.map +1 -1
  41. package/dist/services/prd.js +49 -15
  42. package/dist/services/tasks.d.ts +315 -1
  43. package/dist/services/tasks.d.ts.map +1 -1
  44. package/dist/services/tasks.js +483 -107
  45. package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
  46. package/dist/services/workflow-ai-assistant.js +19 -6
  47. package/dist/test/lib/ai-service/task-operations.test.d.ts +2 -0
  48. package/dist/test/lib/ai-service/task-operations.test.d.ts.map +1 -0
  49. package/dist/test/lib/ai-service/task-operations.test.js +362 -0
  50. package/dist/test/mocks/mock-ai-operations.d.ts +15 -0
  51. package/dist/test/mocks/mock-ai-operations.d.ts.map +1 -0
  52. package/dist/test/mocks/mock-ai-operations.js +107 -0
  53. package/dist/test/mocks/mock-context-builder.d.ts +10 -0
  54. package/dist/test/mocks/mock-context-builder.d.ts.map +1 -0
  55. package/dist/test/mocks/mock-context-builder.js +81 -0
  56. package/dist/test/mocks/mock-model-provider.d.ts +7 -0
  57. package/dist/test/mocks/mock-model-provider.d.ts.map +1 -0
  58. package/dist/test/mocks/mock-model-provider.js +21 -0
  59. package/dist/test/mocks/mock-service-factory.d.ts +11 -0
  60. package/dist/test/mocks/mock-service-factory.d.ts.map +1 -0
  61. package/dist/test/mocks/mock-service-factory.js +61 -0
  62. package/dist/test/mocks/mock-storage.d.ts +50 -0
  63. package/dist/test/mocks/mock-storage.d.ts.map +1 -0
  64. package/dist/test/mocks/mock-storage.js +145 -0
  65. package/dist/test/services/task-service.test.d.ts +2 -0
  66. package/dist/test/services/task-service.test.d.ts.map +1 -0
  67. package/dist/test/services/task-service.test.js +352 -0
  68. package/dist/test/test-mock-setup.d.ts +26 -0
  69. package/dist/test/test-mock-setup.d.ts.map +1 -0
  70. package/dist/test/test-mock-setup.js +41 -0
  71. package/dist/test/test-setup.d.ts +9 -0
  72. package/dist/test/test-setup.d.ts.map +1 -0
  73. package/dist/test/test-setup.js +44 -0
  74. package/dist/test/test-utils.d.ts +22 -0
  75. package/dist/test/test-utils.d.ts.map +1 -0
  76. package/dist/test/test-utils.js +37 -0
  77. package/dist/test/utils/ai-operation-utility.test.d.ts +2 -0
  78. package/dist/test/utils/ai-operation-utility.test.d.ts.map +1 -0
  79. package/dist/test/utils/ai-operation-utility.test.js +290 -0
  80. package/dist/test/utils/error-handling.test.d.ts +2 -0
  81. package/dist/test/utils/error-handling.test.d.ts.map +1 -0
  82. package/dist/test/utils/error-handling.test.js +231 -0
  83. package/dist/utils/ai-operation-utility.d.ts +142 -0
  84. package/dist/utils/ai-operation-utility.d.ts.map +1 -0
  85. package/dist/utils/ai-operation-utility.js +288 -0
  86. package/dist/utils/ai-service-factory.d.ts +10 -0
  87. package/dist/utils/ai-service-factory.d.ts.map +1 -1
  88. package/dist/utils/ai-service-factory.js +19 -1
  89. package/dist/utils/cli-validators.d.ts +2 -2
  90. package/dist/utils/cli-validators.d.ts.map +1 -1
  91. package/dist/utils/cli-validators.js +7 -6
  92. package/dist/utils/error-utils.d.ts +3 -3
  93. package/dist/utils/error-utils.d.ts.map +1 -1
  94. package/dist/utils/error-utils.js +5 -4
  95. package/dist/utils/file-utils.d.ts +4 -4
  96. package/dist/utils/file-utils.d.ts.map +1 -1
  97. package/dist/utils/file-utils.js +11 -6
  98. package/dist/utils/id-generator.d.ts +1 -1
  99. package/dist/utils/id-generator.d.ts.map +1 -1
  100. package/dist/utils/id-generator.js +8 -2
  101. package/dist/utils/model-executor-parser.d.ts +1 -1
  102. package/dist/utils/model-executor-parser.d.ts.map +1 -1
  103. package/dist/utils/model-executor-parser.js +3 -2
  104. package/dist/utils/storage-utils.d.ts +3 -3
  105. package/dist/utils/storage-utils.d.ts.map +1 -1
  106. package/dist/utils/storage-utils.js +7 -6
  107. package/dist/utils/task-o-matic-error.d.ts +206 -0
  108. package/dist/utils/task-o-matic-error.d.ts.map +1 -0
  109. package/dist/utils/task-o-matic-error.js +304 -0
  110. package/package.json +2 -2
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ const ai_operation_utility_1 = require("../../utils/ai-operation-utility");
38
+ const task_o_matic_error_1 = require("../../utils/task-o-matic-error");
39
+ /**
40
+ * ⚠️ CRITICAL: These tests use 100% MOCKS - ZERO real AI calls
41
+ * No API calls are made, no costs incurred, tests run fast
42
+ */
43
+ describe("AIOperationUtility", () => {
44
+ let utility;
45
+ let mockRetryHandler;
46
+ let mockModelProvider;
47
+ let mockContext7Client;
48
+ beforeEach(() => {
49
+ // Create utility instance
50
+ utility = new ai_operation_utility_1.AIOperationUtility();
51
+ // Create mock retry handler - NO REAL RETRIES
52
+ mockRetryHandler = {
53
+ executeWithRetry: async (operation) => {
54
+ // Execute immediately without real retry logic
55
+ return await operation();
56
+ },
57
+ };
58
+ // Create mock model provider - NO REAL AI MODELS
59
+ mockModelProvider = {
60
+ getModel: () => ({
61
+ // Mock AI SDK v2 compliant model
62
+ specificationVersion: "v2",
63
+ provider: "mock",
64
+ modelId: "mock-model",
65
+ doGenerate: async () => ({
66
+ text: "Mock response",
67
+ finishReason: "stop",
68
+ usage: {
69
+ promptTokens: 10,
70
+ completionTokens: 5,
71
+ totalTokens: 15,
72
+ },
73
+ }),
74
+ doStream: async () => ({
75
+ stream: (async function* () {
76
+ yield { type: "text-delta", textDelta: "Mock" };
77
+ yield { type: "text-delta", textDelta: " response" };
78
+ })(),
79
+ }),
80
+ }),
81
+ getAIConfig: () => ({
82
+ provider: "mock",
83
+ model: "mock-model",
84
+ apiKey: "mock-key",
85
+ }),
86
+ };
87
+ // Create mock Context7 client - NO REAL DOCUMENTATION FETCHES
88
+ mockContext7Client = {
89
+ saveContext7Documentation: () => {
90
+ // Mock save - no real file writes
91
+ },
92
+ getMCPTools: async () => ({}),
93
+ };
94
+ // Inject mocks into utility
95
+ utility.retryHandler = mockRetryHandler;
96
+ utility.modelProvider = mockModelProvider;
97
+ utility.context7Client = mockContext7Client;
98
+ });
99
+ describe("executeAIOperation", () => {
100
+ it("should execute operation successfully and return result with metrics", async () => {
101
+ // Mock operation that returns a simple result
102
+ const mockResult = { data: "test result" };
103
+ const operation = async () => mockResult;
104
+ const result = await utility.executeAIOperation("test operation", operation);
105
+ assert.deepStrictEqual(result.result, mockResult);
106
+ assert.ok("duration" in result.metrics);
107
+ assert.strictEqual(typeof result.metrics.duration, "number");
108
+ assert.ok(result.metrics.duration >= 0);
109
+ });
110
+ it("should throw TaskOMaticError when operation fails", async () => {
111
+ // Mock operation that throws an error
112
+ const operation = async () => {
113
+ throw new Error("Mock AI failure");
114
+ };
115
+ try {
116
+ await utility.executeAIOperation("test operation", operation);
117
+ assert.fail("Should have thrown TaskOMaticError");
118
+ }
119
+ catch (error) {
120
+ assert.ok(error instanceof task_o_matic_error_1.TaskOMaticError);
121
+ assert.strictEqual(error.code, "AI_OPERATION_FAILED");
122
+ assert.ok(error.message.includes("test operation"));
123
+ assert.ok(Array.isArray(error.suggestions));
124
+ }
125
+ });
126
+ it("should preserve error details in TaskOMaticError", async () => {
127
+ const originalError = new Error("Original error message");
128
+ const operation = async () => {
129
+ throw originalError;
130
+ };
131
+ try {
132
+ await utility.executeAIOperation("test operation", operation);
133
+ assert.fail("Should have thrown");
134
+ }
135
+ catch (error) {
136
+ assert.ok(error instanceof task_o_matic_error_1.TaskOMaticError);
137
+ const taskError = error;
138
+ assert.strictEqual(taskError.cause, originalError);
139
+ assert.ok(taskError.context?.includes("test operation"));
140
+ assert.ok("operationName" in (taskError.metadata || {}));
141
+ }
142
+ });
143
+ it("should capture duration metrics correctly", async () => {
144
+ const operation = async () => {
145
+ // Simulate some work
146
+ await new Promise((resolve) => setTimeout(resolve, 50));
147
+ return { data: "result" };
148
+ };
149
+ const result = await utility.executeAIOperation("slow operation", operation);
150
+ assert.ok(result.metrics.duration >= 50);
151
+ });
152
+ it("should respect maxRetries option", async () => {
153
+ let attemptCount = 0;
154
+ // Mock retry handler that counts attempts
155
+ utility.retryHandler = {
156
+ executeWithRetry: async (operation, config) => {
157
+ attemptCount++;
158
+ assert.strictEqual(config.maxAttempts, 5);
159
+ return await operation();
160
+ },
161
+ };
162
+ const operation = async () => ({ data: "result" });
163
+ await utility.executeAIOperation("test", operation, { maxRetries: 5 });
164
+ assert.strictEqual(attemptCount, 1);
165
+ });
166
+ it("should extract token usage from result if available", async () => {
167
+ const mockResult = {
168
+ data: "test",
169
+ usage: {
170
+ prompt_tokens: 100,
171
+ completion_tokens: 50,
172
+ total_tokens: 150,
173
+ },
174
+ };
175
+ const operation = async () => mockResult;
176
+ const result = await utility.executeAIOperation("test", operation);
177
+ assert.deepStrictEqual(result.metrics.tokenUsage, {
178
+ prompt: 100,
179
+ completion: 50,
180
+ total: 150,
181
+ });
182
+ });
183
+ it("should return undefined tokenUsage if not available in result", async () => {
184
+ const mockResult = { data: "test" };
185
+ const operation = async () => mockResult;
186
+ const result = await utility.executeAIOperation("test", operation);
187
+ assert.strictEqual(result.metrics.tokenUsage, undefined);
188
+ });
189
+ });
190
+ describe("streamTextWithTools", () => {
191
+ it("should verify method signature exists - NO REAL AI CALL", async () => {
192
+ // This test verifies the method exists and has the correct signature
193
+ // We don't actually call it to avoid real AI calls
194
+ assert.strictEqual(typeof utility.streamTextWithTools, "function");
195
+ // Verify the method is accessible
196
+ assert.ok("streamTextWithTools" in utility);
197
+ });
198
+ it("should pass tools to streamText when provided - NO REAL AI CALL", async () => {
199
+ // This test would verify tool passing in a real scenario
200
+ // For now, we just verify the method accepts tools parameter
201
+ const mockTools = {
202
+ readFile: {
203
+ description: "Read a file",
204
+ parameters: {},
205
+ execute: async () => ({}),
206
+ },
207
+ };
208
+ // The method should not throw
209
+ try {
210
+ // We can't fully test without real AI, but we verify the signature
211
+ assert.strictEqual(typeof utility.streamTextWithTools, "function");
212
+ }
213
+ catch (error) {
214
+ assert.fail("Should not throw with valid parameters");
215
+ }
216
+ });
217
+ });
218
+ describe("Error Handling", () => {
219
+ it("should wrap string errors in Error objects", async () => {
220
+ const operation = async () => {
221
+ throw "String error";
222
+ };
223
+ try {
224
+ await utility.executeAIOperation("test", operation);
225
+ assert.fail("Should have thrown");
226
+ }
227
+ catch (error) {
228
+ assert.ok(error instanceof task_o_matic_error_1.TaskOMaticError);
229
+ assert.ok(error.cause instanceof Error);
230
+ }
231
+ });
232
+ it("should include suggestions in thrown errors", async () => {
233
+ const operation = async () => {
234
+ throw new Error("Network timeout");
235
+ };
236
+ try {
237
+ await utility.executeAIOperation("test", operation);
238
+ assert.fail("Should have thrown");
239
+ }
240
+ catch (error) {
241
+ const taskError = error;
242
+ assert.ok(Array.isArray(taskError.suggestions));
243
+ assert.ok(taskError.suggestions.length > 0);
244
+ assert.ok(taskError.suggestions?.includes("Check AI configuration"));
245
+ }
246
+ });
247
+ });
248
+ describe("Metrics Collection", () => {
249
+ it("should always include duration in metrics", async () => {
250
+ const operation = async () => ({ data: "result" });
251
+ const result = await utility.executeAIOperation("test", operation);
252
+ assert.ok("duration" in result.metrics);
253
+ assert.strictEqual(typeof result.metrics.duration, "number");
254
+ assert.ok(result.metrics.duration >= 0);
255
+ });
256
+ it("should capture metrics even on error", async () => {
257
+ const operation = async () => {
258
+ await new Promise((resolve) => setTimeout(resolve, 10));
259
+ throw new Error("Failure");
260
+ };
261
+ try {
262
+ await utility.executeAIOperation("test", operation);
263
+ assert.fail("Should have thrown");
264
+ }
265
+ catch (error) {
266
+ const taskError = error;
267
+ assert.ok("duration" in (taskError.metadata || {}));
268
+ assert.ok(taskError.metadata?.duration >= 10);
269
+ }
270
+ });
271
+ });
272
+ describe("Streaming Options", () => {
273
+ it("should wrap streaming options without modifying original", async () => {
274
+ let chunkCount = 0;
275
+ const originalOnChunk = (chunk) => {
276
+ chunkCount++;
277
+ };
278
+ const streamingOptions = {
279
+ onChunk: originalOnChunk,
280
+ };
281
+ // The wrapping happens internally, we just verify it doesn't throw
282
+ const operation = async () => ({ data: "result" });
283
+ await utility.executeAIOperation("test", operation, {
284
+ streamingOptions,
285
+ });
286
+ // Original callback should not be modified
287
+ assert.strictEqual(streamingOptions.onChunk, originalOnChunk);
288
+ });
289
+ });
290
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=error-handling.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handling.test.d.ts","sourceRoot":"","sources":["../../../src/test/utils/error-handling.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ const task_o_matic_error_1 = require("../../utils/task-o-matic-error");
38
+ describe("TaskOMaticError", () => {
39
+ describe("TaskOMaticError class", () => {
40
+ it("should create a TaskOMaticError with all properties", () => {
41
+ const error = new task_o_matic_error_1.TaskOMaticError("Test error", {
42
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
43
+ context: "Test context",
44
+ suggestions: ["Suggestion 1", "Suggestion 2"],
45
+ metadata: { key: "value" },
46
+ });
47
+ assert.strictEqual(error.name, "TaskOMaticError");
48
+ assert.strictEqual(error.message, "Test error");
49
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR);
50
+ assert.strictEqual(error.context, "Test context");
51
+ assert.deepStrictEqual(error.suggestions, [
52
+ "Suggestion 1",
53
+ "Suggestion 2",
54
+ ]);
55
+ assert.deepStrictEqual(error.metadata, { key: "value" });
56
+ assert.ok(error.timestamp);
57
+ assert.ok(error.stack);
58
+ });
59
+ it("should create a TaskOMaticError with minimal properties", () => {
60
+ const error = new task_o_matic_error_1.TaskOMaticError("Minimal error", {
61
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
62
+ });
63
+ assert.strictEqual(error.name, "TaskOMaticError");
64
+ assert.strictEqual(error.message, "Minimal error");
65
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR);
66
+ assert.ok(error.timestamp);
67
+ });
68
+ it("should include cause error", () => {
69
+ const cause = new Error("Original error");
70
+ const error = new task_o_matic_error_1.TaskOMaticError("Wrapped error", {
71
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
72
+ cause,
73
+ });
74
+ assert.strictEqual(error.cause, cause);
75
+ });
76
+ });
77
+ describe("getDetails()", () => {
78
+ it("should return formatted error details", () => {
79
+ const error = new task_o_matic_error_1.TaskOMaticError("Test error", {
80
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
81
+ context: "Test context",
82
+ suggestions: ["Suggestion 1"],
83
+ metadata: { key: "value" },
84
+ });
85
+ const details = error.getDetails();
86
+ assert.ok(details.includes("[TASK_O_MATIC_001]"));
87
+ assert.ok(details.includes("Test error"));
88
+ assert.ok(details.includes("Context: Test context"));
89
+ assert.ok(details.includes("Suggestions:"));
90
+ assert.ok(details.includes("Suggestion 1"));
91
+ assert.ok(details.includes("Metadata:"));
92
+ });
93
+ });
94
+ describe("toJSON()", () => {
95
+ it("should return structured error data", () => {
96
+ const error = new task_o_matic_error_1.TaskOMaticError("Test error", {
97
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
98
+ context: "Test context",
99
+ suggestions: ["Suggestion 1"],
100
+ metadata: { key: "value" },
101
+ });
102
+ const json = error.toJSON();
103
+ assert.strictEqual(json.name, "TaskOMaticError");
104
+ assert.strictEqual(json.code, task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR);
105
+ assert.strictEqual(json.message, "Test error");
106
+ assert.strictEqual(json.context, "Test context");
107
+ assert.deepStrictEqual(json.suggestions, ["Suggestion 1"]);
108
+ assert.deepStrictEqual(json.metadata, { key: "value" });
109
+ assert.ok(json.timestamp);
110
+ assert.ok(json.stack);
111
+ });
112
+ });
113
+ describe("createStandardError()", () => {
114
+ it("should create a standardized error", () => {
115
+ const error = (0, task_o_matic_error_1.createStandardError)(task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND, "Task not found", {
116
+ context: "Task search failed",
117
+ suggestions: ["Check task ID", "List all tasks"],
118
+ });
119
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
120
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND);
121
+ assert.strictEqual(error.message, "Task not found");
122
+ assert.strictEqual(error.context, "Task search failed");
123
+ assert.deepStrictEqual(error.suggestions, [
124
+ "Check task ID",
125
+ "List all tasks",
126
+ ]);
127
+ });
128
+ });
129
+ describe("formatStandardError()", () => {
130
+ it("should format an error with context and suggestions", () => {
131
+ const originalError = new Error("Original error");
132
+ const error = (0, task_o_matic_error_1.formatStandardError)(originalError, task_o_matic_error_1.TaskOMaticErrorCodes.STORAGE_ERROR, {
133
+ context: "Storage operation failed",
134
+ suggestions: ["Check permissions", "Retry operation"],
135
+ });
136
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
137
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.STORAGE_ERROR);
138
+ assert.strictEqual(error.message, "Original error");
139
+ assert.strictEqual(error.context, "Storage operation failed");
140
+ assert.deepStrictEqual(error.suggestions, [
141
+ "Check permissions",
142
+ "Retry operation",
143
+ ]);
144
+ assert.strictEqual(error.cause, originalError);
145
+ });
146
+ });
147
+ describe("isTaskOMaticError()", () => {
148
+ it("should return true for TaskOMaticError instances", () => {
149
+ const error = new task_o_matic_error_1.TaskOMaticError("Test", {
150
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
151
+ });
152
+ assert.strictEqual((0, task_o_matic_error_1.isTaskOMaticError)(error), true);
153
+ });
154
+ it("should return false for regular Errors", () => {
155
+ const error = new Error("Regular error");
156
+ assert.strictEqual((0, task_o_matic_error_1.isTaskOMaticError)(error), false);
157
+ });
158
+ it("should return false for other values", () => {
159
+ assert.strictEqual((0, task_o_matic_error_1.isTaskOMaticError)("string error"), false);
160
+ assert.strictEqual((0, task_o_matic_error_1.isTaskOMaticError)(null), false);
161
+ assert.strictEqual((0, task_o_matic_error_1.isTaskOMaticError)(undefined), false);
162
+ });
163
+ });
164
+ describe("Standard error formatters", () => {
165
+ it("should format task not found error", () => {
166
+ const error = (0, task_o_matic_error_1.formatTaskNotFoundError)("task-123");
167
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
168
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND);
169
+ assert.ok(error.message.includes("task-123"));
170
+ assert.ok(error.context);
171
+ assert.ok(error.suggestions);
172
+ });
173
+ it("should format invalid status transition error", () => {
174
+ const error = (0, task_o_matic_error_1.formatInvalidStatusTransitionError)("todo", "invalid");
175
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
176
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.INVALID_TASK_STATUS);
177
+ assert.ok(error.message.includes("todo"));
178
+ assert.ok(error.message.includes("invalid"));
179
+ assert.ok(error.context);
180
+ assert.ok(error.suggestions);
181
+ });
182
+ it("should format storage error", () => {
183
+ const cause = new Error("Storage failed");
184
+ const error = (0, task_o_matic_error_1.formatStorageError)("save", cause);
185
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
186
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.STORAGE_ERROR);
187
+ assert.ok(error.message.includes("save"));
188
+ assert.strictEqual(error.cause, cause);
189
+ });
190
+ it("should format AI operation error", () => {
191
+ const cause = new Error("AI failed");
192
+ const error = (0, task_o_matic_error_1.formatAIOperationError)("generate", cause);
193
+ assert.ok((0, task_o_matic_error_1.isTaskOMaticError)(error));
194
+ assert.strictEqual(error.code, task_o_matic_error_1.TaskOMaticErrorCodes.AI_OPERATION_FAILED);
195
+ assert.ok(error.message.includes("generate"));
196
+ assert.strictEqual(error.cause, cause);
197
+ });
198
+ });
199
+ describe("wrapErrorForBackwardCompatibility()", () => {
200
+ it("should return TaskOMaticError unchanged", () => {
201
+ const error = new task_o_matic_error_1.TaskOMaticError("Test", {
202
+ code: task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR,
203
+ });
204
+ const wrapped = (0, task_o_matic_error_1.wrapErrorForBackwardCompatibility)(error);
205
+ assert.strictEqual(wrapped, error);
206
+ });
207
+ it("should return regular Error unchanged", () => {
208
+ const error = new Error("Regular error");
209
+ const wrapped = (0, task_o_matic_error_1.wrapErrorForBackwardCompatibility)(error);
210
+ assert.strictEqual(wrapped, error);
211
+ });
212
+ it("should convert non-Error values to Error", () => {
213
+ const wrapped1 = (0, task_o_matic_error_1.wrapErrorForBackwardCompatibility)("string error");
214
+ assert.ok(wrapped1 instanceof Error);
215
+ assert.ok(wrapped1.message.includes("string error"));
216
+ const wrapped2 = (0, task_o_matic_error_1.wrapErrorForBackwardCompatibility)({
217
+ message: "object error",
218
+ });
219
+ assert.ok(wrapped2 instanceof Error);
220
+ assert.ok(wrapped2.message.includes("object error"));
221
+ });
222
+ });
223
+ describe("Error codes", () => {
224
+ it("should have defined error codes", () => {
225
+ assert.ok(task_o_matic_error_1.TaskOMaticErrorCodes.UNEXPECTED_ERROR);
226
+ assert.ok(task_o_matic_error_1.TaskOMaticErrorCodes.TASK_NOT_FOUND);
227
+ assert.ok(task_o_matic_error_1.TaskOMaticErrorCodes.STORAGE_ERROR);
228
+ assert.ok(task_o_matic_error_1.TaskOMaticErrorCodes.AI_OPERATION_FAILED);
229
+ });
230
+ });
231
+ });
@@ -0,0 +1,142 @@
1
+ import type { ToolSet } from "ai";
2
+ import { AIConfig, StreamingOptions, RetryConfig } from "../types";
3
+ import { BaseOperations } from "../lib/ai-service/base-operations";
4
+ export interface AIOperationResult<T> {
5
+ result: T;
6
+ metrics: {
7
+ duration: number;
8
+ tokenUsage?: {
9
+ prompt: number;
10
+ completion: number;
11
+ total: number;
12
+ };
13
+ timeToFirstToken?: number;
14
+ };
15
+ }
16
+ export interface AIOperationOptions {
17
+ streamingOptions?: StreamingOptions;
18
+ retryConfig?: Partial<RetryConfig>;
19
+ aiConfig?: Partial<AIConfig>;
20
+ enableFilesystemTools?: boolean;
21
+ context?: Record<string, unknown>;
22
+ maxRetries?: number;
23
+ }
24
+ /**
25
+ * AIOperationUtility - Centralized utility for AI operations with metrics and error handling
26
+ *
27
+ * Extends BaseOperations to inherit core AI functionality (streamText, model provider, etc.)
28
+ * and adds standardized metrics collection, error handling, and retry logic.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const utility = new AIOperationUtility();
33
+ *
34
+ * // Execute operation with metrics
35
+ * const result = await utility.executeAIOperation(
36
+ * "Task breakdown",
37
+ * async () => performBreakdown(task),
38
+ * {
39
+ * maxRetries: 3,
40
+ * streamingOptions: { onChunk: console.log }
41
+ * }
42
+ * );
43
+ *
44
+ * console.log(result.metrics.duration);
45
+ * console.log(result.metrics.timeToFirstToken);
46
+ * ```
47
+ */
48
+ export declare class AIOperationUtility extends BaseOperations {
49
+ /**
50
+ * Execute an AI operation with standardized metrics, error handling, and streaming support
51
+ *
52
+ * This method wraps AI operations to provide:
53
+ * - Automatic retry logic with configurable attempts
54
+ * - Metrics collection (duration, token usage, time to first token)
55
+ * - Error handling with TaskOMaticError wrapping
56
+ * - Streaming support with metrics capture
57
+ *
58
+ * @param operationName - Human-readable name for the operation (used in errors)
59
+ * @param operation - The async operation to execute
60
+ * @param options - Configuration options for retry, streaming, and AI config
61
+ * @returns Promise resolving to AIOperationResult with result and metrics
62
+ * @throws {TaskOMaticError} If operation fails after all retry attempts
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * try {
67
+ * const result = await utility.executeAIOperation(
68
+ * "Task enhancement",
69
+ * async () => enhanceTask(task),
70
+ * { maxRetries: 2 }
71
+ * );
72
+ * console.log(`Operation took ${result.metrics.duration}ms`);
73
+ * } catch (error) {
74
+ * if (error instanceof TaskOMaticError) {
75
+ * console.error(error.getDetails());
76
+ * }
77
+ * }
78
+ * ```
79
+ */
80
+ executeAIOperation<T>(operationName: string, operation: () => Promise<T>, options?: AIOperationOptions): Promise<AIOperationResult<T>>;
81
+ /**
82
+ * Standardized streaming text operation with tool support
83
+ *
84
+ * This method provides a unified interface for AI text generation with:
85
+ * - Optional tool integration (filesystem, MCP, etc.)
86
+ * - Streaming support with callbacks
87
+ * - Context7 documentation caching
88
+ * - Metrics collection
89
+ * - Error handling
90
+ *
91
+ * @param systemPrompt - System prompt for the AI
92
+ * @param userMessage - User message/prompt
93
+ * @param config - Optional AI configuration overrides
94
+ * @param streamingOptions - Optional streaming callbacks
95
+ * @param tools - Optional tool set to provide to the AI
96
+ * @returns Promise resolving to the generated text
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const result = await utility.streamTextWithTools(
101
+ * "You are a helpful assistant",
102
+ * "Explain quantum computing",
103
+ * undefined,
104
+ * { onChunk: (chunk) => console.log(chunk) },
105
+ * filesystemTools
106
+ * );
107
+ * ```
108
+ */
109
+ streamTextWithTools(systemPrompt: string, userMessage: string, config?: Partial<AIConfig>, streamingOptions?: StreamingOptions, tools?: ToolSet): Promise<string>;
110
+ /**
111
+ * Wrap streaming options to capture metrics
112
+ *
113
+ * @private
114
+ */
115
+ private wrapStreamingOptions;
116
+ /**
117
+ * Create streaming configuration with Context7 handling and callbacks
118
+ *
119
+ * This method creates a unified streaming configuration that:
120
+ * - Always handles Context7 tool-result events (even without streaming UI)
121
+ * - Forwards text-delta events to onChunk callback
122
+ * - Forwards reasoning-delta events to onReasoning callback
123
+ * - Handles errors with onError callback
124
+ * - Provides onFinish callback with completion data
125
+ *
126
+ * @private
127
+ */
128
+ private createStreamingConfig;
129
+ /**
130
+ * Extract token usage from AI result
131
+ *
132
+ * @private
133
+ */
134
+ private extractTokenUsageFromResult;
135
+ /**
136
+ * Get error message from any error type
137
+ *
138
+ * @private
139
+ */
140
+ private getErrorMessage;
141
+ }
142
+ //# sourceMappingURL=ai-operation-utility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-operation-utility.d.ts","sourceRoot":"","sources":["../../src/utils/ai-operation-utility.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGnE,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE;YACX,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,kBAAmB,SAAQ,cAAc;IACpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,kBAAkB,CAAC,CAAC,EACxB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAmEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,mBAAmB,CACvB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,EAC1B,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC;IAgClB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAgC5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,qBAAqB;IAsD7B;;;;OAIG;IACH,OAAO,CAAC,2BAA2B;IAiBnC;;;;OAIG;IACH,OAAO,CAAC,eAAe;CASxB"}