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.
- package/LICENSE +21 -0
- package/README.md +552 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +8 -0
- package/dist/cli/display/common.d.ts +5 -0
- package/dist/cli/display/common.d.ts.map +1 -0
- package/dist/cli/display/common.js +44 -0
- package/dist/cli/display/plan.d.ts +11 -0
- package/dist/cli/display/plan.d.ts.map +1 -0
- package/dist/cli/display/plan.js +42 -0
- package/dist/cli/display/progress.d.ts +11 -0
- package/dist/cli/display/progress.d.ts.map +1 -0
- package/dist/cli/display/progress.js +47 -0
- package/dist/cli/display/task.d.ts +18 -0
- package/dist/cli/display/task.d.ts.map +1 -0
- package/dist/cli/display/task.js +250 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +61 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +197 -0
- package/dist/commands/prd.d.ts +4 -0
- package/dist/commands/prd.d.ts.map +1 -0
- package/dist/commands/prd.js +131 -0
- package/dist/commands/prompt.d.ts +3 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/prompt.js +192 -0
- package/dist/commands/tasks.d.ts +3 -0
- package/dist/commands/tasks.d.ts.map +1 -0
- package/dist/commands/tasks.js +599 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/lib/ai-service/ai-operations.d.ts +31 -0
- package/dist/lib/ai-service/ai-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/ai-operations.js +648 -0
- package/dist/lib/ai-service/json-parser.d.ts +9 -0
- package/dist/lib/ai-service/json-parser.d.ts.map +1 -0
- package/dist/lib/ai-service/json-parser.js +37 -0
- package/dist/lib/ai-service/mcp-client.d.ts +9 -0
- package/dist/lib/ai-service/mcp-client.d.ts.map +1 -0
- package/dist/lib/ai-service/mcp-client.js +48 -0
- package/dist/lib/ai-service/model-provider.d.ts +8 -0
- package/dist/lib/ai-service/model-provider.d.ts.map +1 -0
- package/dist/lib/ai-service/model-provider.js +71 -0
- package/dist/lib/ai-service/research-tools.d.ts +4 -0
- package/dist/lib/ai-service/research-tools.d.ts.map +1 -0
- package/dist/lib/ai-service/research-tools.js +8 -0
- package/dist/lib/ai-service/retry-handler.d.ts +8 -0
- package/dist/lib/ai-service/retry-handler.d.ts.map +1 -0
- package/dist/lib/ai-service/retry-handler.js +62 -0
- package/dist/lib/better-t-stack-cli.d.ts +35 -0
- package/dist/lib/better-t-stack-cli.d.ts.map +1 -0
- package/dist/lib/better-t-stack-cli.js +118 -0
- package/dist/lib/config.d.ts +24 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +160 -0
- package/dist/lib/context-builder.d.ts +53 -0
- package/dist/lib/context-builder.d.ts.map +1 -0
- package/dist/lib/context-builder.js +294 -0
- package/dist/lib/executors/executor-factory.d.ts +5 -0
- package/dist/lib/executors/executor-factory.d.ts.map +1 -0
- package/dist/lib/executors/executor-factory.js +21 -0
- package/dist/lib/executors/opencode-executor.d.ts +6 -0
- package/dist/lib/executors/opencode-executor.d.ts.map +1 -0
- package/dist/lib/executors/opencode-executor.js +46 -0
- package/dist/lib/index.d.ts +89 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +134 -0
- package/dist/lib/prompt-builder.d.ts +50 -0
- package/dist/lib/prompt-builder.d.ts.map +1 -0
- package/dist/lib/prompt-builder.js +171 -0
- package/dist/lib/prompt-registry.d.ts +22 -0
- package/dist/lib/prompt-registry.d.ts.map +1 -0
- package/dist/lib/prompt-registry.js +201 -0
- package/dist/lib/storage.d.ts +60 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +768 -0
- package/dist/lib/task-execution.d.ts +3 -0
- package/dist/lib/task-execution.d.ts.map +1 -0
- package/dist/lib/task-execution.js +130 -0
- package/dist/lib/validation.d.ts +4 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +52 -0
- package/dist/mcp/prompts.d.ts +3 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +7 -0
- package/dist/mcp/resources.d.ts +3 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +7 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +25 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +99 -0
- package/dist/prompts/documentation-detection.d.ts +2 -0
- package/dist/prompts/documentation-detection.d.ts.map +1 -0
- package/dist/prompts/documentation-detection.js +24 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +22 -0
- package/dist/prompts/prd-parsing.d.ts +3 -0
- package/dist/prompts/prd-parsing.d.ts.map +1 -0
- package/dist/prompts/prd-parsing.js +172 -0
- package/dist/prompts/prd-rework.d.ts +3 -0
- package/dist/prompts/prd-rework.d.ts.map +1 -0
- package/dist/prompts/prd-rework.js +81 -0
- package/dist/prompts/task-breakdown.d.ts +3 -0
- package/dist/prompts/task-breakdown.d.ts.map +1 -0
- package/dist/prompts/task-breakdown.js +151 -0
- package/dist/prompts/task-enhancement.d.ts +3 -0
- package/dist/prompts/task-enhancement.d.ts.map +1 -0
- package/dist/prompts/task-enhancement.js +140 -0
- package/dist/prompts/task-planning.d.ts +3 -0
- package/dist/prompts/task-planning.d.ts.map +1 -0
- package/dist/prompts/task-planning.js +66 -0
- package/dist/services/prd.d.ts +32 -0
- package/dist/services/prd.d.ts.map +1 -0
- package/dist/services/prd.js +191 -0
- package/dist/services/tasks.d.ts +67 -0
- package/dist/services/tasks.d.ts.map +1 -0
- package/dist/services/tasks.js +596 -0
- package/dist/test/commands.test.d.ts +2 -0
- package/dist/test/commands.test.d.ts.map +1 -0
- package/dist/test/commands.test.js +74 -0
- package/dist/test/storage.test.d.ts +2 -0
- package/dist/test/storage.test.d.ts.map +1 -0
- package/dist/test/storage.test.js +207 -0
- package/dist/types/callbacks.d.ts +27 -0
- package/dist/types/callbacks.d.ts.map +1 -0
- package/dist/types/callbacks.js +2 -0
- package/dist/types/index.d.ts +252 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/mcp.d.ts +3 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +3 -0
- package/dist/types/options.d.ts +94 -0
- package/dist/types/options.d.ts.map +1 -0
- package/dist/types/options.js +2 -0
- package/dist/types/results.d.ts +90 -0
- package/dist/types/results.d.ts.map +1 -0
- package/dist/types/results.js +2 -0
- package/dist/utils/ai-config-builder.d.ts +14 -0
- package/dist/utils/ai-config-builder.d.ts.map +1 -0
- package/dist/utils/ai-config-builder.js +22 -0
- package/dist/utils/ai-service-factory.d.ts +10 -0
- package/dist/utils/ai-service-factory.d.ts.map +1 -0
- package/dist/utils/ai-service-factory.js +52 -0
- package/dist/utils/stack-formatter.d.ts +11 -0
- package/dist/utils/stack-formatter.d.ts.map +1 -0
- package/dist/utils/stack-formatter.js +30 -0
- package/dist/utils/streaming-options.d.ts +10 -0
- package/dist/utils/streaming-options.d.ts.map +1 -0
- package/dist/utils/streaming-options.js +53 -0
- 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"}
|