task-o-matic 0.0.1

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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +552 -0
  3. package/dist/cli/bin.d.ts +3 -0
  4. package/dist/cli/bin.d.ts.map +1 -0
  5. package/dist/cli/bin.js +8 -0
  6. package/dist/cli/display/common.d.ts +5 -0
  7. package/dist/cli/display/common.d.ts.map +1 -0
  8. package/dist/cli/display/common.js +44 -0
  9. package/dist/cli/display/plan.d.ts +11 -0
  10. package/dist/cli/display/plan.d.ts.map +1 -0
  11. package/dist/cli/display/plan.js +42 -0
  12. package/dist/cli/display/progress.d.ts +11 -0
  13. package/dist/cli/display/progress.d.ts.map +1 -0
  14. package/dist/cli/display/progress.js +47 -0
  15. package/dist/cli/display/task.d.ts +18 -0
  16. package/dist/cli/display/task.d.ts.map +1 -0
  17. package/dist/cli/display/task.js +250 -0
  18. package/dist/commands/config.d.ts +3 -0
  19. package/dist/commands/config.d.ts.map +1 -0
  20. package/dist/commands/config.js +61 -0
  21. package/dist/commands/init.d.ts +4 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +197 -0
  24. package/dist/commands/prd.d.ts +4 -0
  25. package/dist/commands/prd.d.ts.map +1 -0
  26. package/dist/commands/prd.js +131 -0
  27. package/dist/commands/prompt.d.ts +3 -0
  28. package/dist/commands/prompt.d.ts.map +1 -0
  29. package/dist/commands/prompt.js +192 -0
  30. package/dist/commands/tasks.d.ts +3 -0
  31. package/dist/commands/tasks.d.ts.map +1 -0
  32. package/dist/commands/tasks.js +599 -0
  33. package/dist/index.d.ts +18 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +67 -0
  36. package/dist/lib/ai-service/ai-operations.d.ts +31 -0
  37. package/dist/lib/ai-service/ai-operations.d.ts.map +1 -0
  38. package/dist/lib/ai-service/ai-operations.js +648 -0
  39. package/dist/lib/ai-service/json-parser.d.ts +9 -0
  40. package/dist/lib/ai-service/json-parser.d.ts.map +1 -0
  41. package/dist/lib/ai-service/json-parser.js +37 -0
  42. package/dist/lib/ai-service/mcp-client.d.ts +9 -0
  43. package/dist/lib/ai-service/mcp-client.d.ts.map +1 -0
  44. package/dist/lib/ai-service/mcp-client.js +48 -0
  45. package/dist/lib/ai-service/model-provider.d.ts +8 -0
  46. package/dist/lib/ai-service/model-provider.d.ts.map +1 -0
  47. package/dist/lib/ai-service/model-provider.js +71 -0
  48. package/dist/lib/ai-service/research-tools.d.ts +4 -0
  49. package/dist/lib/ai-service/research-tools.d.ts.map +1 -0
  50. package/dist/lib/ai-service/research-tools.js +8 -0
  51. package/dist/lib/ai-service/retry-handler.d.ts +8 -0
  52. package/dist/lib/ai-service/retry-handler.d.ts.map +1 -0
  53. package/dist/lib/ai-service/retry-handler.js +62 -0
  54. package/dist/lib/better-t-stack-cli.d.ts +35 -0
  55. package/dist/lib/better-t-stack-cli.d.ts.map +1 -0
  56. package/dist/lib/better-t-stack-cli.js +118 -0
  57. package/dist/lib/config.d.ts +24 -0
  58. package/dist/lib/config.d.ts.map +1 -0
  59. package/dist/lib/config.js +160 -0
  60. package/dist/lib/context-builder.d.ts +53 -0
  61. package/dist/lib/context-builder.d.ts.map +1 -0
  62. package/dist/lib/context-builder.js +294 -0
  63. package/dist/lib/executors/executor-factory.d.ts +5 -0
  64. package/dist/lib/executors/executor-factory.d.ts.map +1 -0
  65. package/dist/lib/executors/executor-factory.js +21 -0
  66. package/dist/lib/executors/opencode-executor.d.ts +6 -0
  67. package/dist/lib/executors/opencode-executor.d.ts.map +1 -0
  68. package/dist/lib/executors/opencode-executor.js +46 -0
  69. package/dist/lib/index.d.ts +89 -0
  70. package/dist/lib/index.d.ts.map +1 -0
  71. package/dist/lib/index.js +134 -0
  72. package/dist/lib/prompt-builder.d.ts +50 -0
  73. package/dist/lib/prompt-builder.d.ts.map +1 -0
  74. package/dist/lib/prompt-builder.js +171 -0
  75. package/dist/lib/prompt-registry.d.ts +22 -0
  76. package/dist/lib/prompt-registry.d.ts.map +1 -0
  77. package/dist/lib/prompt-registry.js +201 -0
  78. package/dist/lib/storage.d.ts +60 -0
  79. package/dist/lib/storage.d.ts.map +1 -0
  80. package/dist/lib/storage.js +768 -0
  81. package/dist/lib/task-execution.d.ts +3 -0
  82. package/dist/lib/task-execution.d.ts.map +1 -0
  83. package/dist/lib/task-execution.js +130 -0
  84. package/dist/lib/validation.d.ts +4 -0
  85. package/dist/lib/validation.d.ts.map +1 -0
  86. package/dist/lib/validation.js +52 -0
  87. package/dist/mcp/prompts.d.ts +3 -0
  88. package/dist/mcp/prompts.d.ts.map +1 -0
  89. package/dist/mcp/prompts.js +7 -0
  90. package/dist/mcp/resources.d.ts +3 -0
  91. package/dist/mcp/resources.d.ts.map +1 -0
  92. package/dist/mcp/resources.js +7 -0
  93. package/dist/mcp/server.d.ts +3 -0
  94. package/dist/mcp/server.d.ts.map +1 -0
  95. package/dist/mcp/server.js +25 -0
  96. package/dist/mcp/tools.d.ts +3 -0
  97. package/dist/mcp/tools.d.ts.map +1 -0
  98. package/dist/mcp/tools.js +99 -0
  99. package/dist/prompts/documentation-detection.d.ts +2 -0
  100. package/dist/prompts/documentation-detection.d.ts.map +1 -0
  101. package/dist/prompts/documentation-detection.js +24 -0
  102. package/dist/prompts/index.d.ts +7 -0
  103. package/dist/prompts/index.d.ts.map +1 -0
  104. package/dist/prompts/index.js +22 -0
  105. package/dist/prompts/prd-parsing.d.ts +3 -0
  106. package/dist/prompts/prd-parsing.d.ts.map +1 -0
  107. package/dist/prompts/prd-parsing.js +172 -0
  108. package/dist/prompts/prd-rework.d.ts +3 -0
  109. package/dist/prompts/prd-rework.d.ts.map +1 -0
  110. package/dist/prompts/prd-rework.js +81 -0
  111. package/dist/prompts/task-breakdown.d.ts +3 -0
  112. package/dist/prompts/task-breakdown.d.ts.map +1 -0
  113. package/dist/prompts/task-breakdown.js +151 -0
  114. package/dist/prompts/task-enhancement.d.ts +3 -0
  115. package/dist/prompts/task-enhancement.d.ts.map +1 -0
  116. package/dist/prompts/task-enhancement.js +140 -0
  117. package/dist/prompts/task-planning.d.ts +3 -0
  118. package/dist/prompts/task-planning.d.ts.map +1 -0
  119. package/dist/prompts/task-planning.js +66 -0
  120. package/dist/services/prd.d.ts +32 -0
  121. package/dist/services/prd.d.ts.map +1 -0
  122. package/dist/services/prd.js +191 -0
  123. package/dist/services/tasks.d.ts +67 -0
  124. package/dist/services/tasks.d.ts.map +1 -0
  125. package/dist/services/tasks.js +596 -0
  126. package/dist/test/commands.test.d.ts +2 -0
  127. package/dist/test/commands.test.d.ts.map +1 -0
  128. package/dist/test/commands.test.js +74 -0
  129. package/dist/test/storage.test.d.ts +2 -0
  130. package/dist/test/storage.test.d.ts.map +1 -0
  131. package/dist/test/storage.test.js +207 -0
  132. package/dist/types/callbacks.d.ts +27 -0
  133. package/dist/types/callbacks.d.ts.map +1 -0
  134. package/dist/types/callbacks.js +2 -0
  135. package/dist/types/index.d.ts +252 -0
  136. package/dist/types/index.d.ts.map +1 -0
  137. package/dist/types/index.js +2 -0
  138. package/dist/types/mcp.d.ts +3 -0
  139. package/dist/types/mcp.d.ts.map +1 -0
  140. package/dist/types/mcp.js +3 -0
  141. package/dist/types/options.d.ts +94 -0
  142. package/dist/types/options.d.ts.map +1 -0
  143. package/dist/types/options.js +2 -0
  144. package/dist/types/results.d.ts +90 -0
  145. package/dist/types/results.d.ts.map +1 -0
  146. package/dist/types/results.js +2 -0
  147. package/dist/utils/ai-config-builder.d.ts +14 -0
  148. package/dist/utils/ai-config-builder.d.ts.map +1 -0
  149. package/dist/utils/ai-config-builder.js +22 -0
  150. package/dist/utils/ai-service-factory.d.ts +10 -0
  151. package/dist/utils/ai-service-factory.d.ts.map +1 -0
  152. package/dist/utils/ai-service-factory.js +52 -0
  153. package/dist/utils/stack-formatter.d.ts +11 -0
  154. package/dist/utils/stack-formatter.d.ts.map +1 -0
  155. package/dist/utils/stack-formatter.js +30 -0
  156. package/dist/utils/streaming-options.d.ts +10 -0
  157. package/dist/utils/streaming-options.d.ts.map +1 -0
  158. package/dist/utils/streaming-options.js +53 -0
  159. package/package.json +82 -0
@@ -0,0 +1,648 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AIOperations = void 0;
4
+ const ai_1 = require("ai");
5
+ const context_builder_1 = require("../context-builder");
6
+ const prompt_builder_1 = require("../prompt-builder");
7
+ const stack_formatter_1 = require("../../utils/stack-formatter");
8
+ const prompts_1 = require("../../prompts");
9
+ const ai_service_factory_1 = require("../../utils/ai-service-factory");
10
+ const json_parser_1 = require("./json-parser");
11
+ const mcp_client_1 = require("./mcp-client");
12
+ const retry_handler_1 = require("./retry-handler");
13
+ const model_provider_1 = require("./model-provider");
14
+ class AIOperations {
15
+ jsonParser = new json_parser_1.JSONParser();
16
+ context7Client = new mcp_client_1.Context7Client();
17
+ retryHandler = new retry_handler_1.RetryHandler();
18
+ modelProvider = new model_provider_1.ModelProvider();
19
+ // private researchTools = new ResearchTools(); // COMMENTED OUT - TESTING
20
+ async streamText(prompt, config, systemPrompt, userMessage, streamingOptions, retryConfig) {
21
+ const aiConfig = { ...this.modelProvider.getAIConfig(), ...config };
22
+ return this.retryHandler.executeWithRetry(async () => {
23
+ const model = this.modelProvider.getModel(aiConfig);
24
+ const result = (0, ai_1.streamText)({
25
+ model,
26
+ system: systemPrompt,
27
+ messages: [{ role: "user", content: userMessage || prompt }],
28
+ maxRetries: 0, // Disable built-in retries since we handle them manually
29
+ onError: ({ error }) => {
30
+ // Call user's error callback if provided
31
+ streamingOptions?.onError?.(error);
32
+ // Re-throw the FULL error to maintain existing error handling behavior
33
+ throw error;
34
+ },
35
+ onChunk: streamingOptions?.onChunk
36
+ ? ({ chunk }) => {
37
+ if (chunk.type === "text-delta") {
38
+ streamingOptions.onChunk(chunk.text);
39
+ }
40
+ else if (chunk.type === "tool-result" &&
41
+ chunk.toolName === "get-library-docs") {
42
+ const docs = chunk.output;
43
+ if (docs && typeof docs === "object" && "content" in docs) {
44
+ this.context7Client.saveContext7Documentation(chunk.input?.context7CompatibleLibraryID || "unknown", docs.content, chunk.input?.topic || "general");
45
+ }
46
+ else if (docs && typeof docs === "string") {
47
+ this.context7Client.saveContext7Documentation(chunk.input?.context7CompatibleLibraryID || "unknown", docs, chunk.input?.topic || "general");
48
+ }
49
+ }
50
+ }
51
+ : undefined,
52
+ onFinish: streamingOptions?.onFinish
53
+ ? ({ text, finishReason, usage }) => {
54
+ streamingOptions.onFinish({
55
+ text,
56
+ finishReason,
57
+ usage,
58
+ isAborted: false,
59
+ });
60
+ }
61
+ : undefined,
62
+ // Add reasoning configuration only for OpenRouter with explicit reasoning parameter
63
+ ...(aiConfig.provider === "openrouter" &&
64
+ aiConfig.reasoning &&
65
+ aiConfig.reasoning.maxTokens
66
+ ? {
67
+ providerOptions: {
68
+ openrouter: {
69
+ reasoning: {
70
+ max_tokens: aiConfig.reasoning.maxTokens,
71
+ },
72
+ },
73
+ },
74
+ }
75
+ : {}),
76
+ });
77
+ let fullText = "";
78
+ for await (const textPart of result.textStream) {
79
+ fullText += textPart;
80
+ }
81
+ return fullText;
82
+ }, retryConfig, "AI streaming");
83
+ }
84
+ async parsePRD(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory) {
85
+ return this.retryHandler.executeWithRetry(async () => {
86
+ // Get stack context for better PRD parsing using PromptBuilder
87
+ // Pass working directory explicitly to avoid process.cwd() issues
88
+ let stackInfo = "";
89
+ try {
90
+ stackInfo = await prompt_builder_1.PromptBuilder.detectStackInfo(workingDirectory);
91
+ if (stackInfo === "Not detected") {
92
+ stackInfo = "";
93
+ }
94
+ }
95
+ catch (error) {
96
+ // Stack info not available, will use empty string
97
+ }
98
+ // Use PromptBuilder if no prompt override provided
99
+ let enhancedPrompt;
100
+ if (promptOverride) {
101
+ enhancedPrompt = promptOverride;
102
+ }
103
+ else {
104
+ const variables = {
105
+ PRD_CONTENT: prdContent,
106
+ };
107
+ // Only include stack info if we have it
108
+ if (stackInfo) {
109
+ variables.STACK_INFO = stackInfo;
110
+ }
111
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
112
+ name: "prd-parsing",
113
+ type: "user",
114
+ variables,
115
+ });
116
+ if (!promptResult.success) {
117
+ throw new Error(`Failed to build PRD parsing prompt: ${promptResult.error}`);
118
+ }
119
+ enhancedPrompt = promptResult.prompt; // TypeScript: prompt is guaranteed when success is true
120
+ }
121
+ const response = await this.streamText("", // empty prompt since we use messages
122
+ config, prompts_1.PRD_PARSING_SYSTEM_PROMPT, userMessage || enhancedPrompt, streamingOptions, { maxAttempts: 1 });
123
+ // Parse JSON from response using proper typing
124
+ const parseResult = this.jsonParser.parseJSONFromResponse(response);
125
+ if (!parseResult.success) {
126
+ throw new Error(parseResult.error || "Failed to parse PRD response");
127
+ }
128
+ const parsed = parseResult.data;
129
+ // Transform to our format with proper IDs for dependencies
130
+ const tasks = (parsed?.tasks || []).map((task, index) => {
131
+ const taskId = task.id || (index + 1).toString();
132
+ // Extract all AI-generated keys except the ones we handle separately
133
+ const { title, description, content, effort, dependencies, ...extraData } = task;
134
+ // Create comprehensive content from all AI data
135
+ let fullContent = "";
136
+ if (description || content) {
137
+ fullContent = description || content || "";
138
+ }
139
+ // Add any extra AI-generated data as structured content
140
+ if (Object.keys(extraData).length > 0) {
141
+ fullContent += "\n\n## Additional AI-Generated Information\n";
142
+ for (const [key, value] of Object.entries(extraData)) {
143
+ fullContent += `\n**${key}:** ${JSON.stringify(value, null, 2)}`;
144
+ }
145
+ }
146
+ return {
147
+ id: taskId,
148
+ title: task.title,
149
+ description: (task.description || task.content || "").substring(0, 200) +
150
+ ((task.description || task.content || "").length > 200
151
+ ? "..."
152
+ : ""),
153
+ content: fullContent,
154
+ status: "todo",
155
+ createdAt: Date.now(),
156
+ updatedAt: Date.now(),
157
+ estimatedEffort: task.effort,
158
+ dependencies: task.dependencies || [],
159
+ tags: task.tags || [],
160
+ };
161
+ });
162
+ return {
163
+ tasks,
164
+ summary: parsed?.summary || "PRD parsed successfully",
165
+ estimatedDuration: parsed?.estimatedDuration || "Unknown",
166
+ confidence: parsed?.confidence || 0.7,
167
+ };
168
+ }, retryConfig, "PRD parsing");
169
+ }
170
+ async breakdownTask(task, config, promptOverride, userMessage, streamingOptions, retryConfig, fullContent, stackInfo, existingSubtasks) {
171
+ return this.retryHandler.executeWithRetry(async () => {
172
+ // Use PromptBuilder if no prompt override provided
173
+ let prompt;
174
+ if (promptOverride) {
175
+ prompt = promptOverride;
176
+ }
177
+ else {
178
+ // Build enhanced variables for task breakdown
179
+ const variables = {
180
+ TASK_TITLE: task.title,
181
+ TASK_DESCRIPTION: task.description || "No description",
182
+ };
183
+ // Add full content if available
184
+ if (fullContent) {
185
+ variables.TASK_CONTENT = fullContent;
186
+ }
187
+ // Add existing subtasks if available
188
+ if (existingSubtasks && existingSubtasks.length > 0) {
189
+ const existingSubtasksText = existingSubtasks
190
+ .map((subtask, index) => `${index + 1}. ${subtask.title}: ${subtask.description || "No description"}`)
191
+ .join("\n");
192
+ variables.EXISTING_SUBTASKS = existingSubtasksText;
193
+ }
194
+ // Add stack info if available
195
+ if (stackInfo) {
196
+ variables.STACK_INFO = stackInfo;
197
+ }
198
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
199
+ name: "task-breakdown",
200
+ type: "user",
201
+ variables,
202
+ });
203
+ if (!promptResult.success) {
204
+ throw new Error(`Failed to build task breakdown prompt: ${promptResult.error}`);
205
+ }
206
+ prompt = promptResult.prompt;
207
+ }
208
+ const response = await this.streamText("", // empty prompt since we use messages
209
+ config, prompts_1.TASK_BREAKDOWN_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 });
210
+ // Parse JSON from response using proper typing
211
+ const parseResult = this.jsonParser.parseJSONFromResponse(response);
212
+ if (!parseResult.success) {
213
+ throw new Error(parseResult.error || "Failed to parse task breakdown response");
214
+ }
215
+ const parsed = parseResult.data;
216
+ // Return plain task data - let storage layer handle IDs and metadata
217
+ return (parsed?.subtasks || []).map((subtask) => ({
218
+ title: subtask.title,
219
+ content: subtask.description || "",
220
+ estimatedEffort: subtask.effort,
221
+ }));
222
+ }, retryConfig, "Task breakdown");
223
+ }
224
+ async enhanceTask(title, description, config, promptOverride, userMessage, taskId, streamingOptions, retryConfig) {
225
+ return this.retryHandler.executeWithRetry(async () => {
226
+ let contextInfo = "";
227
+ let prdContent = "";
228
+ // If taskId is provided, include existing documentation context
229
+ if (taskId) {
230
+ const contextBuilder = new context_builder_1.ContextBuilder();
231
+ try {
232
+ const context = await contextBuilder.buildContext(taskId);
233
+ if (context.documentation || context.stack || context.prdContent) {
234
+ contextInfo = "\n\nAvailable Context:\n";
235
+ if (context.stack) {
236
+ contextInfo += (0, stack_formatter_1.formatStackForContext)(context.stack) + "\n";
237
+ }
238
+ if (context.documentation) {
239
+ contextInfo += `Documentation Available: ${context.documentation.recap}\n`;
240
+ if (context.documentation.files.length > 0) {
241
+ contextInfo += `Documentation Files: ${context.documentation.files.map((f) => f.path).join(", ")}\n`;
242
+ }
243
+ }
244
+ if (context.prdContent) {
245
+ prdContent = context.prdContent;
246
+ }
247
+ }
248
+ }
249
+ catch (error) {
250
+ throw error;
251
+ }
252
+ }
253
+ // Use PromptBuilder if no prompt override provided
254
+ let prompt;
255
+ if (promptOverride) {
256
+ prompt = promptOverride;
257
+ }
258
+ else {
259
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
260
+ name: "task-enhancement",
261
+ type: "user",
262
+ variables: {
263
+ TASK_TITLE: title,
264
+ TASK_DESCRIPTION: description || "None",
265
+ CONTEXT_INFO: contextInfo,
266
+ PRD_CONTENT: prdContent || "No PRD content available",
267
+ },
268
+ });
269
+ if (!promptResult.success) {
270
+ throw new Error(`Failed to build task enhancement prompt: ${promptResult.error}`);
271
+ }
272
+ prompt = promptResult.prompt;
273
+ }
274
+ return this.streamText("", // empty prompt since we use messages
275
+ config, prompts_1.TASK_ENHANCEMENT_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 });
276
+ }, retryConfig, "Task enhancement");
277
+ }
278
+ async reworkPRD(prdContent, feedback, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory) {
279
+ return this.retryHandler.executeWithRetry(async () => {
280
+ // Get stack context for better PRD rework using PromptBuilder
281
+ // Pass working directory explicitly to avoid process.cwd() issues
282
+ let stackInfo = "";
283
+ try {
284
+ stackInfo = await prompt_builder_1.PromptBuilder.detectStackInfo(workingDirectory);
285
+ if (stackInfo === "Not detected") {
286
+ stackInfo = "";
287
+ }
288
+ }
289
+ catch (error) {
290
+ // Stack info not available, will use empty string
291
+ }
292
+ // Use PromptBuilder if no prompt override provided
293
+ let prompt;
294
+ if (promptOverride) {
295
+ prompt = promptOverride;
296
+ }
297
+ else {
298
+ const variables = {
299
+ PRD_CONTENT: prdContent,
300
+ USER_FEEDBACK: feedback,
301
+ };
302
+ // Only include stack info if we have it
303
+ if (stackInfo) {
304
+ variables.STACK_INFO = stackInfo;
305
+ }
306
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
307
+ name: "prd-rework",
308
+ type: "user",
309
+ variables,
310
+ });
311
+ if (!promptResult.success) {
312
+ throw new Error(`Failed to build PRD rework prompt: ${promptResult.error}`);
313
+ }
314
+ prompt = promptResult.prompt;
315
+ }
316
+ return this.streamText("", // empty prompt since we use messages
317
+ config, prompts_1.PRD_REWORK_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 });
318
+ }, retryConfig, "PRD rework");
319
+ }
320
+ // Context7 Integration Methods
321
+ async enhanceTaskWithDocumentation(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch) {
322
+ return this.retryHandler
323
+ .executeWithRetry(async () => {
324
+ // Context7 integration via HTTP calls
325
+ const mcpTools = await this.context7Client.getMCPTools();
326
+ const defaultAIConfig = this.modelProvider.getAIConfig();
327
+ const aiConfig = config
328
+ ? { ...defaultAIConfig, ...config }
329
+ : defaultAIConfig;
330
+ const model = this.modelProvider.getModel(aiConfig);
331
+ // Get custom research tools
332
+ // const customResearchTools = this.researchTools.getResearchTools();
333
+ // Build context for this operation using the actual task ID
334
+ const contextBuilder = new context_builder_1.ContextBuilder();
335
+ const builtContext = await contextBuilder.buildContext("1.1");
336
+ // Build existing research context
337
+ const existingResearchContext = existingResearch
338
+ ? Object.entries(existingResearch)
339
+ .map(([lib, entries]) => `### ${lib}\n${entries.map((e) => `- Query: "${e.query}"`).join("\n")}`)
340
+ .join("\n\n")
341
+ : "No existing research available.";
342
+ // const existingResearchContext = existingResearch
343
+ // ? "## Existing research\n" +
344
+ // existingResearch
345
+ // .map(
346
+ // (rs) =>
347
+ // rs &&
348
+ // `${rs.recap}\n#### librairies\n${rs.libraries.join(`- \n`)}\n\n#### files:\n${rs.libraries.join(`- \n`)}`,
349
+ // )
350
+ // .join("\n\n")
351
+ // : "No existing research available.";
352
+ // Use PromptBuilder for consistent prompt handling
353
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
354
+ name: "task-enhancement",
355
+ type: "user",
356
+ variables: {
357
+ TASK_TITLE: taskTitle,
358
+ TASK_DESCRIPTION: taskDescription,
359
+ CONTEXT_INFO: `Technology stack: ${stackInfo || "Not specified"}`,
360
+ EXISTING_RESEARCH: existingResearchContext,
361
+ PRD_CONTENT: builtContext.prdContent || "No PRD content available",
362
+ },
363
+ });
364
+ if (!promptResult.success) {
365
+ throw new Error(`Failed to build task enhancement prompt: ${promptResult.error}`);
366
+ }
367
+ const prompt = promptResult.prompt;
368
+ // Merge Context7 MCP tools with custom research tools
369
+ const allTools = {
370
+ ...mcpTools,
371
+ // ...customResearchTools,
372
+ };
373
+ const result = (0, ai_1.streamText)({
374
+ model,
375
+ tools: allTools, // Context7 MCP tools + custom research tools
376
+ system: prompts_1.TASK_ENHANCEMENT_SYSTEM_PROMPT +
377
+ `
378
+
379
+ You have access to Context7 documentation tools.
380
+
381
+ ## Research Strategy:
382
+ 1. Use Context7 MCP tools (context7_resolve_library_id, context7_get_library_docs) for any new research needed
383
+ 2. Synthesize information from all sources to enhance the task
384
+
385
+ Technology stack context: ${stackInfo || "Not specified"}
386
+
387
+ ## Available Cached Research:
388
+ ${existingResearchContext}`,
389
+ messages: [
390
+ {
391
+ role: "user",
392
+ content: prompt,
393
+ },
394
+ ],
395
+ maxRetries: 0, // Disable built-in retries since we handle them manually
396
+ stopWhen: (0, ai_1.stepCountIs)(8), // Allow more steps for comprehensive research
397
+ onError: ({ error }) => {
398
+ // Call user's error callback if provided
399
+ streamingOptions?.onError?.(error);
400
+ // Re-throw errors to maintain existing error handling behavior
401
+ throw error;
402
+ },
403
+ onChunk: streamingOptions?.onChunk
404
+ ? ({ chunk }) => {
405
+ if (chunk.type === "text-delta") {
406
+ streamingOptions.onChunk(chunk.text);
407
+ }
408
+ }
409
+ : undefined,
410
+ onFinish: streamingOptions?.onFinish
411
+ ? ({ text, finishReason, usage }) => {
412
+ streamingOptions.onFinish({
413
+ text,
414
+ finishReason,
415
+ usage,
416
+ isAborted: false,
417
+ });
418
+ }
419
+ : undefined,
420
+ });
421
+ // Process tool calls and results properly
422
+ const toolCalls = await result.toolCalls;
423
+ const toolResults = await result.toolResults;
424
+ // Log tool interactions for debugging
425
+ if (toolCalls.length > 0) {
426
+ console.log("AI made tool calls:", toolCalls.map((tc) => ({ tool: tc.toolName, input: tc.input })));
427
+ }
428
+ if (toolResults.length > 0) {
429
+ console.log("Tool results received:", toolResults.map((tr) => ({
430
+ tool: tr.toolName,
431
+ output: tr.output,
432
+ })));
433
+ }
434
+ // Process the stream properly to ensure tool calls execute
435
+ let fullText = "";
436
+ for await (const textPart of result.textStream) {
437
+ fullText += textPart;
438
+ if (streamingOptions?.onChunk) {
439
+ streamingOptions.onChunk(textPart);
440
+ }
441
+ }
442
+ return fullText;
443
+ }, retryConfig, "Task enhancement with documentation")
444
+ .finally(async () => {
445
+ await this.context7Client.closeMCPConnection();
446
+ });
447
+ }
448
+ async analyzeDocumentationNeeds(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch) {
449
+ return this.retryHandler
450
+ .executeWithRetry(async () => {
451
+ const mcpTools = await this.context7Client.getMCPTools();
452
+ const defaultAIConfig = this.modelProvider.getAIConfig();
453
+ const aiConfig = config
454
+ ? { ...defaultAIConfig, ...config }
455
+ : defaultAIConfig;
456
+ const model = this.modelProvider.getModel(aiConfig);
457
+ // Build existing research context
458
+ const existingResearchContext = existingResearch
459
+ ? "## Existing research\n" +
460
+ existingResearch
461
+ .map((rs) => rs &&
462
+ `${rs.recap}\n#### librairies\n${rs.libraries.join(`- \n`)}\n\n#### files:\n${rs.libraries.join(`- \n`)}`)
463
+ .join("\n\n")
464
+ : "No existing research available.";
465
+ // Use PromptBuilder for consistent prompt handling
466
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
467
+ name: "documentation-detection",
468
+ type: "user",
469
+ variables: {
470
+ TASK_TITLE: taskTitle,
471
+ TASK_DESCRIPTION: taskDescription,
472
+ STACK_INFO: stackInfo || "Not specified",
473
+ EXISTING_RESEARCH: existingResearchContext,
474
+ },
475
+ });
476
+ if (!promptResult.success) {
477
+ throw new Error(`Failed to build documentation detection prompt: ${promptResult.error}`);
478
+ }
479
+ const prompt = promptResult.prompt;
480
+ // Track actual libraries and files saved
481
+ const libraries = [];
482
+ const files = [];
483
+ // Merge Context7 MCP tools with custom research tools
484
+ const allTools = {
485
+ ...mcpTools,
486
+ };
487
+ const result = (0, ai_1.streamText)({
488
+ model,
489
+ tools: allTools,
490
+ system: `You are an expert developer.\nYou have access to Context7 MCP tools for documentation research.\nFetch documentation relevant to the task in the project context and create a document giving that knowledge to the AI assistant that will implement the task.`,
491
+ messages: [{ role: "user", content: prompt }],
492
+ maxRetries: 0,
493
+ stopWhen: (0, ai_1.stepCountIs)(8), // Allow more steps for comprehensive analysis
494
+ onError: ({ error }) => {
495
+ // Call user's error callback if provided
496
+ streamingOptions?.onError?.(error);
497
+ // Re-throw errors to maintain existing error handling behavior
498
+ throw error;
499
+ },
500
+ onChunk: streamingOptions?.onChunk
501
+ ? ({ chunk }) => {
502
+ if (chunk.type === "text-delta") {
503
+ streamingOptions.onChunk(chunk.text);
504
+ }
505
+ else if (chunk.type === "tool-result") {
506
+ // Save Context7 documentation if this is a library docs result
507
+ if (chunk.toolName === "get-library-docs" && chunk.output) {
508
+ (async () => {
509
+ try {
510
+ // Extract library from the tool input
511
+ const input = chunk.input;
512
+ const libraryId = input.context7CompatibleLibraryID;
513
+ // Generate unique filename using library ID and timestamp
514
+ const libraryName = libraryId.split("/").pop() || "unknown";
515
+ const timestamp = Date.now();
516
+ const filename = input.topic ?? `${libraryName}-${timestamp}`;
517
+ // Extract content from tool result object
518
+ let content = "";
519
+ if (chunk.output &&
520
+ typeof chunk.output === "object") {
521
+ if ("content" in chunk.output) {
522
+ const contentArray = chunk.output
523
+ .content;
524
+ if (Array.isArray(contentArray)) {
525
+ content = contentArray
526
+ .map((item) => item.text || "")
527
+ .join("\n");
528
+ }
529
+ else {
530
+ content = String(contentArray);
531
+ }
532
+ }
533
+ else if ("text" in chunk.output) {
534
+ content = chunk.output.text;
535
+ }
536
+ }
537
+ else if (typeof chunk.output === "string") {
538
+ content = chunk.output;
539
+ }
540
+ if (content) {
541
+ // Save documentation and get the file path
542
+ const docFile = await this.context7Client.saveContext7Documentation(libraryName, filename, content);
543
+ // BUILD THE LIBRARIES ARRAY WITH ACTUAL DATA
544
+ libraries.push({
545
+ name: libraryName,
546
+ context7Id: libraryId,
547
+ reason: "Documentation fetched for task implementation",
548
+ searchQuery: filename,
549
+ });
550
+ // TRACK THE ACTUAL FILE PATH
551
+ files.push(docFile);
552
+ }
553
+ }
554
+ catch (error) {
555
+ console.error("Failed to save Context7 documentation:", error);
556
+ }
557
+ })();
558
+ }
559
+ }
560
+ }
561
+ : undefined,
562
+ onFinish: streamingOptions?.onFinish
563
+ ? ({ text, finishReason, usage }) => {
564
+ streamingOptions.onFinish({
565
+ text,
566
+ finishReason,
567
+ usage,
568
+ isAborted: false,
569
+ });
570
+ }
571
+ : undefined,
572
+ });
573
+ // Process the stream properly to ensure tool calls execute
574
+ let fullText = "";
575
+ for await (const textPart of result.textStream) {
576
+ fullText += textPart;
577
+ }
578
+ // Get tool results for processing
579
+ const toolResults = await result.toolResults;
580
+ const toolCalls = await result.toolCalls;
581
+ // Save the generated documentation to a task-specific file
582
+ if (fullText.trim()) {
583
+ try {
584
+ const storage = (0, ai_service_factory_1.getStorage)();
585
+ const taskDocFile = await storage.saveTaskDocumentation(taskId, fullText);
586
+ files.push(taskDocFile);
587
+ }
588
+ catch (error) {
589
+ console.error("Failed to save task documentation:", error);
590
+ }
591
+ }
592
+ return {
593
+ libraries,
594
+ confidence: libraries.length > 0 ? 0.8 : 0.3,
595
+ toolResults: toolResults.map((tr) => ({
596
+ toolName: tr.toolName,
597
+ output: tr.output,
598
+ })),
599
+ files, // Return actual files saved
600
+ };
601
+ }, retryConfig, "Documentation needs analysis")
602
+ .finally(async () => {
603
+ await this.context7Client.closeMCPConnection();
604
+ });
605
+ }
606
+ async generateDocumentationRecap(libraries, documentContents, streamingOptions, retryConfig) {
607
+ const prompt = `Create a concise recap of the documentation fetched for these libraries:
608
+
609
+ Libraries:
610
+ ${libraries.map((lib) => `- ${lib.name} (${lib.context7Id}): ${lib.reason}`).join("\n")}
611
+
612
+ Documentation Contents:
613
+ ${documentContents.map((doc) => `## ${doc.library}\n${doc.content.substring(0, 500)}...`).join("\n\n")}
614
+
615
+ Please provide a 2-3 sentence summary of what documentation is available and how it relates to the task.`;
616
+ return this.retryHandler.executeWithRetry(async () => {
617
+ return this.streamText(prompt, undefined, "You are a technical writer who creates concise summaries of documentation collections.", undefined, streamingOptions, { maxAttempts: 1 });
618
+ }, retryConfig, "Documentation recap generation");
619
+ }
620
+ async planTask(taskContext, taskDetails, config, promptOverride, userMessage, streamingOptions, retryConfig) {
621
+ return this.retryHandler.executeWithRetry(async () => {
622
+ // Use PromptBuilder if no prompt override provided
623
+ let prompt;
624
+ if (promptOverride) {
625
+ prompt = promptOverride;
626
+ }
627
+ else {
628
+ const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
629
+ name: "task-planning",
630
+ type: "user",
631
+ variables: {
632
+ TASK_CONTEXT: taskContext,
633
+ TASK_DETAILS: taskDetails,
634
+ },
635
+ });
636
+ if (!promptResult.success) {
637
+ throw new Error(`Failed to build task planning prompt: ${promptResult.error}`);
638
+ }
639
+ prompt = promptResult.prompt;
640
+ }
641
+ const response = await this.streamText("", // empty prompt since we use messages
642
+ config, prompts_1.TASK_PLANNING_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 });
643
+ // Return the plan text directly - no JSON parsing needed
644
+ return response;
645
+ }, retryConfig, "Task planning");
646
+ }
647
+ }
648
+ exports.AIOperations = AIOperations;
@@ -0,0 +1,9 @@
1
+ import { JSONParseResult } from "../../types";
2
+ export declare class JSONParser {
3
+ /**
4
+ * Parses JSON from AI text response with improved error handling
5
+ * @deprecated Use generateObject instead for structured output
6
+ */
7
+ parseJSONFromResponse<T>(text: string): JSONParseResult<T>;
8
+ }
9
+ //# sourceMappingURL=json-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-parser.d.ts","sourceRoot":"","sources":["../../../src/lib/ai-service/json-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,qBAAa,UAAU;IACrB;;;OAGG;IACH,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC;CA4B3D"}