task-o-matic 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/display/progress.d.ts +1 -1
- package/dist/cli/display/progress.d.ts.map +1 -1
- package/dist/cli/display/progress.js +16 -13
- package/dist/commands/benchmark.js +70 -2
- package/dist/commands/init.js +48 -27
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +201 -0
- package/dist/commands/tasks/execute-loop.d.ts.map +1 -1
- package/dist/commands/tasks/execute-loop.js +21 -16
- package/dist/commands/tasks/execute.d.ts.map +1 -1
- package/dist/commands/tasks/execute.js +4 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +56 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lib/ai-service/ai-operations.d.ts +13 -10
- package/dist/lib/ai-service/ai-operations.d.ts.map +1 -1
- package/dist/lib/ai-service/ai-operations.js +35 -995
- package/dist/lib/ai-service/base-operations.d.ts +13 -0
- package/dist/lib/ai-service/base-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/base-operations.js +79 -0
- package/dist/lib/ai-service/documentation-operations.d.ts +18 -0
- package/dist/lib/ai-service/documentation-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/documentation-operations.js +301 -0
- package/dist/lib/ai-service/prd-operations.d.ts +14 -0
- package/dist/lib/ai-service/prd-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/prd-operations.js +398 -0
- package/dist/lib/ai-service/task-operations.d.ts +12 -0
- package/dist/lib/ai-service/task-operations.d.ts.map +1 -0
- package/dist/lib/ai-service/task-operations.js +225 -0
- package/dist/lib/benchmark/registry.d.ts.map +1 -1
- package/dist/lib/benchmark/registry.js +127 -0
- package/dist/lib/better-t-stack-cli.d.ts +4 -1
- package/dist/lib/better-t-stack-cli.d.ts.map +1 -1
- package/dist/lib/better-t-stack-cli.js +126 -5
- package/dist/lib/config.d.ts +13 -6
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +90 -48
- package/dist/lib/context-builder.d.ts +13 -1
- package/dist/lib/context-builder.d.ts.map +1 -1
- package/dist/lib/context-builder.js +68 -36
- package/dist/lib/executors/claude-code-executor.d.ts +5 -2
- package/dist/lib/executors/claude-code-executor.d.ts.map +1 -1
- package/dist/lib/executors/claude-code-executor.js +30 -3
- package/dist/lib/executors/codex-executor.d.ts +5 -2
- package/dist/lib/executors/codex-executor.d.ts.map +1 -1
- package/dist/lib/executors/codex-executor.js +30 -3
- package/dist/lib/executors/executor-factory.d.ts +2 -2
- package/dist/lib/executors/executor-factory.d.ts.map +1 -1
- package/dist/lib/executors/executor-factory.js +5 -5
- package/dist/lib/executors/gemini-executor.d.ts +5 -2
- package/dist/lib/executors/gemini-executor.d.ts.map +1 -1
- package/dist/lib/executors/gemini-executor.js +30 -3
- package/dist/lib/executors/opencode-executor.d.ts +5 -2
- package/dist/lib/executors/opencode-executor.d.ts.map +1 -1
- package/dist/lib/executors/opencode-executor.js +32 -7
- package/dist/lib/prompt-builder.d.ts +11 -0
- package/dist/lib/prompt-builder.d.ts.map +1 -1
- package/dist/lib/prompt-builder.js +60 -0
- package/dist/lib/prompt-registry.d.ts.map +1 -1
- package/dist/lib/prompt-registry.js +158 -0
- package/dist/lib/storage/file-system.d.ts +3 -7
- package/dist/lib/storage/file-system.d.ts.map +1 -1
- package/dist/lib/storage/file-system.js +50 -230
- package/dist/lib/storage/storage-callbacks.d.ts +17 -0
- package/dist/lib/storage/storage-callbacks.d.ts.map +1 -0
- package/dist/lib/storage/storage-callbacks.js +94 -0
- package/dist/lib/task-execution.d.ts.map +1 -1
- package/dist/lib/task-execution.js +29 -51
- package/dist/lib/task-loop-execution.d.ts.map +1 -1
- package/dist/lib/task-loop-execution.js +234 -42
- package/dist/prompts/documentation-recap.d.ts +3 -0
- package/dist/prompts/documentation-recap.d.ts.map +1 -0
- package/dist/prompts/documentation-recap.js +13 -0
- package/dist/prompts/index.d.ts +6 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +6 -0
- package/dist/prompts/prd-combination.d.ts +2 -0
- package/dist/prompts/prd-combination.d.ts.map +1 -0
- package/dist/prompts/prd-combination.js +35 -0
- package/dist/prompts/prd-generation.d.ts +2 -0
- package/dist/prompts/prd-generation.d.ts.map +1 -0
- package/dist/prompts/prd-generation.js +49 -0
- package/dist/prompts/prd-question-answer.d.ts +3 -0
- package/dist/prompts/prd-question-answer.d.ts.map +1 -0
- package/dist/prompts/prd-question-answer.js +27 -0
- package/dist/prompts/task-execution.d.ts +3 -0
- package/dist/prompts/task-execution.d.ts.map +1 -0
- package/dist/prompts/task-execution.js +21 -0
- package/dist/prompts/workflow-prompts.d.ts +9 -0
- package/dist/prompts/workflow-prompts.d.ts.map +1 -0
- package/dist/prompts/workflow-prompts.js +93 -0
- package/dist/services/prd.d.ts +43 -0
- package/dist/services/prd.d.ts.map +1 -1
- package/dist/services/prd.js +121 -0
- package/dist/services/workflow-ai-assistant.d.ts.map +1 -1
- package/dist/services/workflow-ai-assistant.js +73 -134
- package/dist/services/workflow.d.ts +10 -0
- package/dist/services/workflow.d.ts.map +1 -1
- package/dist/services/workflow.js +118 -40
- package/dist/test/integration/callbacks.test.d.ts +2 -0
- package/dist/test/integration/callbacks.test.d.ts.map +1 -0
- package/dist/test/integration/callbacks.test.js +64 -0
- package/dist/types/callbacks.d.ts +9 -6
- package/dist/types/callbacks.d.ts.map +1 -1
- package/dist/types/index.d.ts +17 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/workflow-options.d.ts +7 -0
- package/dist/types/workflow-options.d.ts.map +1 -1
- package/dist/utils/ai-service-factory.d.ts +15 -1
- package/dist/utils/ai-service-factory.d.ts.map +1 -1
- package/dist/utils/ai-service-factory.js +29 -1
- package/dist/utils/streaming-options.d.ts +1 -1
- package/dist/utils/streaming-options.d.ts.map +1 -1
- package/dist/utils/streaming-options.js +31 -18
- package/docs/agents/cli.md +191 -0
- package/package.json +3 -2
|
@@ -1,1016 +1,56 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.AIOperations = void 0;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const aiConfig = { ...this.modelProvider.getAIConfig(), ...config };
|
|
55
|
-
return this.retryHandler.executeWithRetry(async () => {
|
|
56
|
-
const model = this.modelProvider.getModel(aiConfig);
|
|
57
|
-
const result = (0, ai_1.streamText)({
|
|
58
|
-
model,
|
|
59
|
-
system: systemPrompt,
|
|
60
|
-
messages: [{ role: "user", content: userMessage || prompt }],
|
|
61
|
-
maxRetries: 0, // Disable built-in retries since we handle them manually
|
|
62
|
-
onError: ({ error }) => {
|
|
63
|
-
// Call user's error callback if provided
|
|
64
|
-
streamingOptions?.onError?.(error);
|
|
65
|
-
// Re-throw the FULL error to maintain existing error handling behavior
|
|
66
|
-
throw error;
|
|
67
|
-
},
|
|
68
|
-
onChunk: streamingOptions?.onChunk
|
|
69
|
-
? ({ chunk }) => {
|
|
70
|
-
if (chunk.type === "text-delta") {
|
|
71
|
-
streamingOptions.onChunk(chunk.text);
|
|
72
|
-
}
|
|
73
|
-
else if (chunk.type === "tool-result" &&
|
|
74
|
-
chunk.toolName === "get-library-docs") {
|
|
75
|
-
const docs = chunk.output;
|
|
76
|
-
if (docs && typeof docs === "object" && "content" in docs) {
|
|
77
|
-
this.context7Client.saveContext7Documentation(chunk.input?.context7CompatibleLibraryID || "unknown", docs.content, chunk.input?.topic || "general");
|
|
78
|
-
}
|
|
79
|
-
else if (docs && typeof docs === "string") {
|
|
80
|
-
this.context7Client.saveContext7Documentation(chunk.input?.context7CompatibleLibraryID || "unknown", docs, chunk.input?.topic || "general");
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
: undefined,
|
|
85
|
-
onFinish: streamingOptions?.onFinish
|
|
86
|
-
? ({ text, finishReason, usage }) => {
|
|
87
|
-
streamingOptions.onFinish({
|
|
88
|
-
text,
|
|
89
|
-
finishReason,
|
|
90
|
-
usage,
|
|
91
|
-
isAborted: false,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
: undefined,
|
|
95
|
-
// Add reasoning configuration only for OpenRouter with explicit reasoning parameter
|
|
96
|
-
...(aiConfig.provider === "openrouter" &&
|
|
97
|
-
aiConfig.reasoning &&
|
|
98
|
-
aiConfig.reasoning.maxTokens
|
|
99
|
-
? {
|
|
100
|
-
providerOptions: {
|
|
101
|
-
openrouter: {
|
|
102
|
-
reasoning: {
|
|
103
|
-
max_tokens: aiConfig.reasoning.maxTokens,
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
}
|
|
108
|
-
: {}),
|
|
109
|
-
});
|
|
110
|
-
let fullText = "";
|
|
111
|
-
for await (const textPart of result.textStream) {
|
|
112
|
-
fullText += textPart;
|
|
113
|
-
}
|
|
114
|
-
return fullText;
|
|
115
|
-
}, retryConfig, "AI streaming");
|
|
116
|
-
}
|
|
117
|
-
async parsePRD(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, // Working directory passed from service layer
|
|
118
|
-
enableFilesystemTools) {
|
|
119
|
-
return this.retryHandler.executeWithRetry(async () => {
|
|
120
|
-
// Get stack context for better PRD parsing using PromptBuilder
|
|
121
|
-
// Pass working directory explicitly to avoid process.cwd() issues
|
|
122
|
-
let stackInfo = "";
|
|
123
|
-
try {
|
|
124
|
-
stackInfo = await prompt_builder_1.PromptBuilder.detectStackInfo(workingDirectory);
|
|
125
|
-
if (stackInfo === "Not detected") {
|
|
126
|
-
stackInfo = "";
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
// Stack info not available, will use empty string
|
|
131
|
-
}
|
|
132
|
-
// Use PromptBuilder if no prompt override provided
|
|
133
|
-
let enhancedPrompt;
|
|
134
|
-
if (promptOverride) {
|
|
135
|
-
enhancedPrompt = promptOverride;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
const variables = {
|
|
139
|
-
PRD_CONTENT: prdContent,
|
|
140
|
-
};
|
|
141
|
-
// Only include stack info if we have it
|
|
142
|
-
if (stackInfo) {
|
|
143
|
-
variables.STACK_INFO = stackInfo;
|
|
144
|
-
}
|
|
145
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
146
|
-
name: "prd-parsing",
|
|
147
|
-
type: "user",
|
|
148
|
-
variables,
|
|
149
|
-
});
|
|
150
|
-
if (!promptResult.success) {
|
|
151
|
-
throw new Error(`Failed to build PRD parsing prompt: ${promptResult.error}`);
|
|
152
|
-
}
|
|
153
|
-
enhancedPrompt = promptResult.prompt; // TypeScript: prompt is guaranteed when success is true
|
|
154
|
-
}
|
|
155
|
-
let response;
|
|
156
|
-
if (enableFilesystemTools) {
|
|
157
|
-
// Use filesystem tools when enabled
|
|
158
|
-
const model = this.modelProvider.getModel({
|
|
159
|
-
...this.modelProvider.getAIConfig(),
|
|
160
|
-
...config,
|
|
161
|
-
});
|
|
162
|
-
const allTools = {
|
|
163
|
-
...filesystem_tools_1.filesystemTools,
|
|
164
|
-
};
|
|
165
|
-
const result = await (0, ai_1.streamText)({
|
|
166
|
-
model,
|
|
167
|
-
tools: allTools, // Filesystem tools for project analysis
|
|
168
|
-
system: prompts_1.PRD_PARSING_SYSTEM_PROMPT +
|
|
169
|
-
`
|
|
170
|
-
|
|
171
|
-
You have access to filesystem tools that allow you to:
|
|
172
|
-
- readFile: Read the contents of any file in the project
|
|
173
|
-
- listDirectory: List contents of directories
|
|
174
|
-
|
|
175
|
-
Use these tools to understand the project structure, existing code patterns, and dependencies when parsing the PRD and creating tasks.`,
|
|
176
|
-
messages: [
|
|
177
|
-
{ role: "user", content: userMessage || enhancedPrompt },
|
|
178
|
-
],
|
|
179
|
-
maxRetries: 0,
|
|
180
|
-
onChunk: streamingOptions?.onChunk
|
|
181
|
-
? ({ chunk }) => {
|
|
182
|
-
if (chunk.type === "text-delta") {
|
|
183
|
-
streamingOptions.onChunk(chunk.text);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
: undefined,
|
|
187
|
-
onFinish: streamingOptions?.onFinish
|
|
188
|
-
? ({ text, finishReason, usage }) => {
|
|
189
|
-
streamingOptions.onFinish({
|
|
190
|
-
text,
|
|
191
|
-
finishReason,
|
|
192
|
-
usage,
|
|
193
|
-
isAborted: false,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
: undefined,
|
|
197
|
-
});
|
|
198
|
-
response = await result.text;
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
// Use standard streamText without tools
|
|
202
|
-
response = await this.streamText("", // empty prompt since we use messages
|
|
203
|
-
config, prompts_1.PRD_PARSING_SYSTEM_PROMPT, userMessage || enhancedPrompt, streamingOptions, { maxAttempts: 1 } // Disable retries here since we're handling them at the outer level
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
// Parse JSON from response using proper typing
|
|
207
|
-
const parseResult = this.jsonParser.parseJSONFromResponse(response);
|
|
208
|
-
if (!parseResult.success) {
|
|
209
|
-
throw new Error(parseResult.error || "Failed to parse PRD response");
|
|
210
|
-
}
|
|
211
|
-
const parsed = parseResult.data;
|
|
212
|
-
// Transform to our format with proper IDs for dependencies
|
|
213
|
-
const tasks = (parsed?.tasks || []).map((task, index) => {
|
|
214
|
-
const taskId = task.id || (index + 1).toString();
|
|
215
|
-
// Extract all AI-generated keys except the ones we handle separately
|
|
216
|
-
const { title, description, content, effort, dependencies, ...extraData } = task;
|
|
217
|
-
// Create comprehensive content from all AI data
|
|
218
|
-
let fullContent = "";
|
|
219
|
-
if (description || content) {
|
|
220
|
-
fullContent = description || content || "";
|
|
221
|
-
}
|
|
222
|
-
// Add any extra AI-generated data as structured content
|
|
223
|
-
if (Object.keys(extraData).length > 0) {
|
|
224
|
-
fullContent += "\n\n## Additional AI-Generated Information\n";
|
|
225
|
-
for (const [key, value] of Object.entries(extraData)) {
|
|
226
|
-
fullContent += `\n**${key}:** ${JSON.stringify(value, null, 2)}`;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return {
|
|
230
|
-
id: taskId,
|
|
231
|
-
title: task.title,
|
|
232
|
-
description: (task.description || task.content || "").substring(0, 200) +
|
|
233
|
-
((task.description || task.content || "").length > 200
|
|
234
|
-
? "..."
|
|
235
|
-
: ""),
|
|
236
|
-
content: fullContent,
|
|
237
|
-
status: "todo",
|
|
238
|
-
createdAt: Date.now(),
|
|
239
|
-
updatedAt: Date.now(),
|
|
240
|
-
estimatedEffort: task.effort,
|
|
241
|
-
dependencies: task.dependencies || [],
|
|
242
|
-
tags: task.tags || [],
|
|
243
|
-
};
|
|
244
|
-
});
|
|
245
|
-
return {
|
|
246
|
-
tasks,
|
|
247
|
-
summary: parsed?.summary || "PRD parsed successfully",
|
|
248
|
-
estimatedDuration: parsed?.estimatedDuration || "Unknown",
|
|
249
|
-
confidence: parsed?.confidence || 0.7,
|
|
250
|
-
};
|
|
251
|
-
}, retryConfig, "PRD parsing");
|
|
252
|
-
}
|
|
4
|
+
const base_operations_1 = require("./base-operations");
|
|
5
|
+
const prd_operations_1 = require("./prd-operations");
|
|
6
|
+
const task_operations_1 = require("./task-operations");
|
|
7
|
+
const documentation_operations_1 = require("./documentation-operations");
|
|
8
|
+
/**
|
|
9
|
+
* Main AIOperations class that delegates to specialized operation classes.
|
|
10
|
+
* This provides a unified API for all AI operations while keeping the implementation modular.
|
|
11
|
+
*/
|
|
12
|
+
class AIOperations extends base_operations_1.BaseOperations {
|
|
13
|
+
prdOps = new prd_operations_1.PRDOperations();
|
|
14
|
+
taskOps = new task_operations_1.TaskOperations();
|
|
15
|
+
docOps = new documentation_operations_1.DocumentationOperations();
|
|
16
|
+
// PRD Operations - Delegated to PRDOperations
|
|
17
|
+
async parsePRD(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools) {
|
|
18
|
+
return this.prdOps.parsePRD(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools);
|
|
19
|
+
}
|
|
20
|
+
// Task Operations - Delegated to TaskOperations
|
|
253
21
|
async breakdownTask(task, config, promptOverride, userMessage, streamingOptions, retryConfig, fullContent, stackInfo, existingSubtasks, enableFilesystemTools) {
|
|
254
|
-
return this.
|
|
255
|
-
// Use PromptBuilder if no prompt override provided
|
|
256
|
-
let prompt;
|
|
257
|
-
if (promptOverride) {
|
|
258
|
-
prompt = promptOverride;
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// Build enhanced variables for task breakdown
|
|
262
|
-
const variables = {
|
|
263
|
-
TASK_TITLE: task.title,
|
|
264
|
-
TASK_DESCRIPTION: task.description || "No description",
|
|
265
|
-
};
|
|
266
|
-
// Add full content if available
|
|
267
|
-
if (fullContent) {
|
|
268
|
-
variables.TASK_CONTENT = fullContent;
|
|
269
|
-
}
|
|
270
|
-
// Add existing subtasks if available
|
|
271
|
-
if (existingSubtasks && existingSubtasks.length > 0) {
|
|
272
|
-
const existingSubtasksText = existingSubtasks
|
|
273
|
-
.map((subtask, index) => `${index + 1}. ${subtask.title}: ${subtask.description || "No description"}`)
|
|
274
|
-
.join("\n");
|
|
275
|
-
variables.EXISTING_SUBTASKS = existingSubtasksText;
|
|
276
|
-
}
|
|
277
|
-
// Add stack info if available
|
|
278
|
-
if (stackInfo) {
|
|
279
|
-
variables.STACK_INFO = stackInfo;
|
|
280
|
-
}
|
|
281
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
282
|
-
name: "task-breakdown",
|
|
283
|
-
type: "user",
|
|
284
|
-
variables,
|
|
285
|
-
});
|
|
286
|
-
if (!promptResult.success) {
|
|
287
|
-
throw new Error(`Failed to build task breakdown prompt: ${promptResult.error}`);
|
|
288
|
-
}
|
|
289
|
-
prompt = promptResult.prompt;
|
|
290
|
-
}
|
|
291
|
-
let response;
|
|
292
|
-
if (enableFilesystemTools) {
|
|
293
|
-
// Use filesystem tools when enabled
|
|
294
|
-
const model = this.modelProvider.getModel({
|
|
295
|
-
...this.modelProvider.getAIConfig(),
|
|
296
|
-
...config,
|
|
297
|
-
});
|
|
298
|
-
const allTools = {
|
|
299
|
-
...filesystem_tools_1.filesystemTools,
|
|
300
|
-
};
|
|
301
|
-
const result = await (0, ai_1.streamText)({
|
|
302
|
-
model,
|
|
303
|
-
tools: allTools, // Filesystem tools for project analysis
|
|
304
|
-
system: prompts_1.TASK_BREAKDOWN_SYSTEM_PROMPT +
|
|
305
|
-
`
|
|
306
|
-
|
|
307
|
-
You have access to filesystem tools that allow you to:
|
|
308
|
-
- readFile: Read the contents of any file in the project
|
|
309
|
-
- listDirectory: List contents of directories
|
|
310
|
-
|
|
311
|
-
Use these tools to understand the project structure, existing code, and dependencies when breaking down tasks into subtasks.`,
|
|
312
|
-
messages: [{ role: "user", content: userMessage || prompt }],
|
|
313
|
-
maxRetries: 0,
|
|
314
|
-
onChunk: streamingOptions?.onChunk
|
|
315
|
-
? ({ chunk }) => {
|
|
316
|
-
if (chunk.type === "text-delta") {
|
|
317
|
-
streamingOptions.onChunk(chunk.text);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
: undefined,
|
|
321
|
-
onFinish: streamingOptions?.onFinish
|
|
322
|
-
? ({ text, finishReason, usage }) => {
|
|
323
|
-
streamingOptions.onFinish({
|
|
324
|
-
text,
|
|
325
|
-
finishReason,
|
|
326
|
-
usage,
|
|
327
|
-
isAborted: false,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
: undefined,
|
|
331
|
-
});
|
|
332
|
-
response = await result.text;
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
// Use standard streamText without tools
|
|
336
|
-
response = await this.streamText("", // empty prompt since we use messages
|
|
337
|
-
config, prompts_1.TASK_BREAKDOWN_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 } // Disable retries here since we're handling them at the outer level
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
// Parse JSON from response using proper typing
|
|
341
|
-
const parseResult = this.jsonParser.parseJSONFromResponse(response);
|
|
342
|
-
if (!parseResult.success) {
|
|
343
|
-
throw new Error(parseResult.error || "Failed to parse task breakdown response");
|
|
344
|
-
}
|
|
345
|
-
const parsed = parseResult.data;
|
|
346
|
-
// Return plain task data - let storage layer handle IDs and metadata
|
|
347
|
-
return (parsed?.subtasks || []).map((subtask) => ({
|
|
348
|
-
title: subtask.title,
|
|
349
|
-
content: subtask.description || "",
|
|
350
|
-
estimatedEffort: subtask.effort,
|
|
351
|
-
}));
|
|
352
|
-
}, retryConfig, "Task breakdown");
|
|
22
|
+
return this.taskOps.breakdownTask(task, config, promptOverride, userMessage, streamingOptions, retryConfig, fullContent, stackInfo, existingSubtasks, enableFilesystemTools);
|
|
353
23
|
}
|
|
354
24
|
async enhanceTask(title, description, config, promptOverride, userMessage, taskId, streamingOptions, retryConfig) {
|
|
355
|
-
return this.
|
|
356
|
-
let contextInfo = "";
|
|
357
|
-
let prdContent = "";
|
|
358
|
-
// If taskId is provided, include existing documentation context
|
|
359
|
-
if (taskId) {
|
|
360
|
-
const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
|
|
361
|
-
try {
|
|
362
|
-
const context = await contextBuilder.buildContext(taskId);
|
|
363
|
-
if (context.documentation || context.stack || context.prdContent) {
|
|
364
|
-
contextInfo = "\n\nAvailable Context:\n";
|
|
365
|
-
if (context.stack) {
|
|
366
|
-
contextInfo += (0, stack_formatter_1.formatStackForContext)(context.stack) + "\n";
|
|
367
|
-
}
|
|
368
|
-
if (context.documentation) {
|
|
369
|
-
contextInfo += `Documentation Available: ${context.documentation.recap}\n`;
|
|
370
|
-
if (context.documentation.files.length > 0) {
|
|
371
|
-
contextInfo += `Documentation Files: ${context.documentation.files
|
|
372
|
-
.map((f) => f.path)
|
|
373
|
-
.join(", ")}\n`;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (context.prdContent) {
|
|
377
|
-
prdContent = context.prdContent;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
throw error;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
// Use PromptBuilder if no prompt override provided
|
|
386
|
-
let prompt;
|
|
387
|
-
if (promptOverride) {
|
|
388
|
-
prompt = promptOverride;
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
392
|
-
name: "task-enhancement",
|
|
393
|
-
type: "user",
|
|
394
|
-
variables: {
|
|
395
|
-
TASK_TITLE: title,
|
|
396
|
-
TASK_DESCRIPTION: description || "None",
|
|
397
|
-
CONTEXT_INFO: contextInfo,
|
|
398
|
-
PRD_CONTENT: prdContent || "No PRD content available",
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
if (!promptResult.success) {
|
|
402
|
-
throw new Error(`Failed to build task enhancement prompt: ${promptResult.error}`);
|
|
403
|
-
}
|
|
404
|
-
prompt = promptResult.prompt;
|
|
405
|
-
}
|
|
406
|
-
return this.streamText("", // empty prompt since we use messages
|
|
407
|
-
config, prompts_1.TASK_ENHANCEMENT_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 } // Disable retries here since we're handling them at the outer level
|
|
408
|
-
);
|
|
409
|
-
}, retryConfig, "Task enhancement");
|
|
25
|
+
return this.taskOps.enhanceTask(title, description, config, promptOverride, userMessage, taskId, streamingOptions, retryConfig);
|
|
410
26
|
}
|
|
411
|
-
async reworkPRD(prdContent, feedback, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory,
|
|
412
|
-
|
|
413
|
-
return this.retryHandler.executeWithRetry(async () => {
|
|
414
|
-
// Get stack context for better PRD rework using PromptBuilder
|
|
415
|
-
// Pass working directory explicitly to avoid process.cwd() issues
|
|
416
|
-
let stackInfo = "";
|
|
417
|
-
try {
|
|
418
|
-
stackInfo = await prompt_builder_1.PromptBuilder.detectStackInfo(workingDirectory);
|
|
419
|
-
if (stackInfo === "Not detected") {
|
|
420
|
-
stackInfo = "";
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
catch (error) {
|
|
424
|
-
// Stack info not available, will use empty string
|
|
425
|
-
}
|
|
426
|
-
// Use PromptBuilder if no prompt override provided
|
|
427
|
-
let prompt;
|
|
428
|
-
if (promptOverride) {
|
|
429
|
-
prompt = promptOverride;
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
const variables = {
|
|
433
|
-
PRD_CONTENT: prdContent,
|
|
434
|
-
USER_FEEDBACK: feedback,
|
|
435
|
-
};
|
|
436
|
-
// Only include stack info if we have it
|
|
437
|
-
if (stackInfo) {
|
|
438
|
-
variables.STACK_INFO = stackInfo;
|
|
439
|
-
}
|
|
440
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
441
|
-
name: "prd-rework",
|
|
442
|
-
type: "user",
|
|
443
|
-
variables,
|
|
444
|
-
});
|
|
445
|
-
if (!promptResult.success) {
|
|
446
|
-
throw new Error(`Failed to build PRD rework prompt: ${promptResult.error}`);
|
|
447
|
-
}
|
|
448
|
-
prompt = promptResult.prompt;
|
|
449
|
-
}
|
|
450
|
-
if (enableFilesystemTools) {
|
|
451
|
-
// Use filesystem tools when enabled
|
|
452
|
-
const model = this.modelProvider.getModel({
|
|
453
|
-
...this.modelProvider.getAIConfig(),
|
|
454
|
-
...config,
|
|
455
|
-
});
|
|
456
|
-
const allTools = {
|
|
457
|
-
...filesystem_tools_1.filesystemTools,
|
|
458
|
-
};
|
|
459
|
-
const result = await (0, ai_1.streamText)({
|
|
460
|
-
model,
|
|
461
|
-
tools: allTools, // Filesystem tools for project analysis
|
|
462
|
-
system: prompts_1.PRD_REWORK_SYSTEM_PROMPT +
|
|
463
|
-
`
|
|
464
|
-
|
|
465
|
-
You have access to filesystem tools that allow you to:
|
|
466
|
-
- readFile: Read the contents of any file in the project
|
|
467
|
-
- listDirectory: List contents of directories
|
|
468
|
-
|
|
469
|
-
Use these tools to understand the current project structure, existing code patterns, and dependencies when reworking the PRD based on feedback.`,
|
|
470
|
-
messages: [{ role: "user", content: userMessage || prompt }],
|
|
471
|
-
maxRetries: 0,
|
|
472
|
-
onChunk: streamingOptions?.onChunk
|
|
473
|
-
? ({ chunk }) => {
|
|
474
|
-
if (chunk.type === "text-delta") {
|
|
475
|
-
streamingOptions.onChunk(chunk.text);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
: undefined,
|
|
479
|
-
onFinish: streamingOptions?.onFinish
|
|
480
|
-
? ({ text, finishReason, usage }) => {
|
|
481
|
-
streamingOptions.onFinish({
|
|
482
|
-
text,
|
|
483
|
-
finishReason,
|
|
484
|
-
usage,
|
|
485
|
-
isAborted: false,
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
: undefined,
|
|
489
|
-
});
|
|
490
|
-
return await result.text;
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
// Use standard streamText without tools
|
|
494
|
-
return this.streamText("", // empty prompt since we use messages
|
|
495
|
-
config, prompts_1.PRD_REWORK_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 } // Disable retries here since we're handling them at the outer level
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
}, retryConfig, "PRD rework");
|
|
27
|
+
async reworkPRD(prdContent, feedback, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools) {
|
|
28
|
+
return this.prdOps.reworkPRD(prdContent, feedback, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools);
|
|
499
29
|
}
|
|
500
30
|
async generatePRDQuestions(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools) {
|
|
501
|
-
return this.
|
|
502
|
-
let stackInfo = "";
|
|
503
|
-
try {
|
|
504
|
-
stackInfo = await prompt_builder_1.PromptBuilder.detectStackInfo(workingDirectory);
|
|
505
|
-
if (stackInfo === "Not detected") {
|
|
506
|
-
stackInfo = "";
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
catch (error) {
|
|
510
|
-
// Stack info not available
|
|
511
|
-
}
|
|
512
|
-
let prompt;
|
|
513
|
-
if (promptOverride) {
|
|
514
|
-
prompt = promptOverride;
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
const variables = {
|
|
518
|
-
PRD_CONTENT: prdContent,
|
|
519
|
-
};
|
|
520
|
-
if (stackInfo) {
|
|
521
|
-
variables.STACK_INFO = stackInfo;
|
|
522
|
-
}
|
|
523
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
524
|
-
name: "prd-question",
|
|
525
|
-
type: "user",
|
|
526
|
-
variables,
|
|
527
|
-
});
|
|
528
|
-
if (!promptResult.success) {
|
|
529
|
-
throw new Error(`Failed to build PRD question prompt: ${promptResult.error}`);
|
|
530
|
-
}
|
|
531
|
-
prompt = promptResult.prompt;
|
|
532
|
-
}
|
|
533
|
-
// Use PRD_QUESTION_SYSTEM_PROMPT import
|
|
534
|
-
const { PRD_QUESTION_SYSTEM_PROMPT } = await Promise.resolve().then(() => __importStar(require("../../prompts")));
|
|
535
|
-
let response;
|
|
536
|
-
if (enableFilesystemTools) {
|
|
537
|
-
const model = this.modelProvider.getModel({
|
|
538
|
-
...this.modelProvider.getAIConfig(),
|
|
539
|
-
...config,
|
|
540
|
-
});
|
|
541
|
-
const allTools = { ...filesystem_tools_1.filesystemTools };
|
|
542
|
-
const result = await (0, ai_1.streamText)({
|
|
543
|
-
model,
|
|
544
|
-
tools: allTools,
|
|
545
|
-
system: PRD_QUESTION_SYSTEM_PROMPT +
|
|
546
|
-
`\n\nYou have access to filesystem tools to check existing code/structure if needed.`,
|
|
547
|
-
messages: [{ role: "user", content: userMessage || prompt }],
|
|
548
|
-
maxRetries: 0,
|
|
549
|
-
onChunk: streamingOptions?.onChunk
|
|
550
|
-
? ({ chunk }) => {
|
|
551
|
-
if (chunk.type === "text-delta") {
|
|
552
|
-
streamingOptions.onChunk(chunk.text);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
: undefined,
|
|
556
|
-
onFinish: streamingOptions?.onFinish
|
|
557
|
-
? ({ text, finishReason, usage }) => {
|
|
558
|
-
streamingOptions.onFinish({
|
|
559
|
-
text,
|
|
560
|
-
finishReason,
|
|
561
|
-
usage,
|
|
562
|
-
isAborted: false,
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
: undefined,
|
|
566
|
-
});
|
|
567
|
-
response = await result.text;
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
response = await this.streamText("", config, PRD_QUESTION_SYSTEM_PROMPT, userMessage || prompt, streamingOptions, { maxAttempts: 1 });
|
|
571
|
-
}
|
|
572
|
-
const parseResult = this.jsonParser.parseJSONFromResponse(response);
|
|
573
|
-
if (!parseResult.success) {
|
|
574
|
-
throw new Error(parseResult.error || "Failed to parse PRD questions");
|
|
575
|
-
}
|
|
576
|
-
return parseResult.data?.questions || [];
|
|
577
|
-
}, retryConfig, "PRD questioning");
|
|
31
|
+
return this.prdOps.generatePRDQuestions(prdContent, config, promptOverride, userMessage, streamingOptions, retryConfig, workingDirectory, enableFilesystemTools);
|
|
578
32
|
}
|
|
579
33
|
async answerPRDQuestions(prdContent, questions, config, contextInfo, streamingOptions, retryConfig) {
|
|
580
|
-
return this.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
? `Technology Stack: ${contextInfo.stackInfo}\n`
|
|
588
|
-
: ""}${contextInfo.projectDescription
|
|
589
|
-
? `Project Description: ${contextInfo.projectDescription}\n`
|
|
590
|
-
: ""}`
|
|
591
|
-
: "";
|
|
592
|
-
const prompt = `You are a product expert helping to clarify a PRD.
|
|
593
|
-
|
|
594
|
-
PRD Content:
|
|
595
|
-
${prdContent}${contextText}
|
|
596
|
-
|
|
597
|
-
Please answer the following questions based on the PRD and context:
|
|
598
|
-
|
|
599
|
-
${questionsText}
|
|
600
|
-
|
|
601
|
-
Provide thoughtful, specific answers that will help refine the PRD.
|
|
602
|
-
Format your response as JSON with the following structure:
|
|
603
|
-
{
|
|
604
|
-
"answers": {
|
|
605
|
-
"1": "answer to question 1",
|
|
606
|
-
"2": "answer to question 2",
|
|
607
|
-
...
|
|
608
|
-
}
|
|
609
|
-
}`;
|
|
610
|
-
const systemPrompt = `You are a product expert analyzing PRDs and answering clarifying questions.
|
|
611
|
-
Your answers should be:
|
|
612
|
-
- Specific and actionable
|
|
613
|
-
- Based on the PRD content and project context
|
|
614
|
-
- Helpful for refining the PRD
|
|
615
|
-
- Formatted as JSON`;
|
|
616
|
-
const response = await this.streamText("", config, systemPrompt, prompt, streamingOptions, { maxAttempts: 1 });
|
|
617
|
-
// Parse JSON response
|
|
618
|
-
const parseResult = this.jsonParser.parseJSONFromResponse(response);
|
|
619
|
-
if (!parseResult.success) {
|
|
620
|
-
throw new Error(parseResult.error || "Failed to parse PRD answers response");
|
|
621
|
-
}
|
|
622
|
-
// Convert numbered keys to question text keys
|
|
623
|
-
const answers = {};
|
|
624
|
-
const numberedAnswers = parseResult.data?.answers || {};
|
|
625
|
-
questions.forEach((question, index) => {
|
|
626
|
-
const key = String(index + 1);
|
|
627
|
-
if (numberedAnswers[key]) {
|
|
628
|
-
answers[question] = numberedAnswers[key];
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
return answers;
|
|
632
|
-
}, retryConfig, "PRD question answering");
|
|
34
|
+
return this.prdOps.answerPRDQuestions(prdContent, questions, config, contextInfo, streamingOptions, retryConfig);
|
|
35
|
+
}
|
|
36
|
+
async generatePRD(description, config, promptOverride, userMessage, streamingOptions, retryConfig) {
|
|
37
|
+
return this.prdOps.generatePRD(description, config, promptOverride, userMessage, streamingOptions, retryConfig);
|
|
38
|
+
}
|
|
39
|
+
async combinePRDs(prds, originalDescription, config, promptOverride, userMessage, streamingOptions, retryConfig) {
|
|
40
|
+
return this.prdOps.combinePRDs(prds, originalDescription, config, promptOverride, userMessage, streamingOptions, retryConfig);
|
|
633
41
|
}
|
|
634
|
-
//
|
|
635
|
-
async enhanceTaskWithDocumentation(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch
|
|
636
|
-
|
|
637
|
-
) {
|
|
638
|
-
return this.retryHandler
|
|
639
|
-
.executeWithRetry(async () => {
|
|
640
|
-
// Context7 integration via HTTP calls
|
|
641
|
-
const mcpTools = await this.context7Client.getMCPTools();
|
|
642
|
-
const defaultAIConfig = this.modelProvider.getAIConfig();
|
|
643
|
-
const aiConfig = config
|
|
644
|
-
? { ...defaultAIConfig, ...config }
|
|
645
|
-
: defaultAIConfig;
|
|
646
|
-
const model = this.modelProvider.getModel(aiConfig);
|
|
647
|
-
// Get custom research tools
|
|
648
|
-
// const customResearchTools = this.researchTools.getResearchTools();
|
|
649
|
-
// Build context for this operation using the actual task ID
|
|
650
|
-
const contextBuilder = (0, ai_service_factory_1.getContextBuilder)();
|
|
651
|
-
const builtContext = await contextBuilder.buildContext("1.1");
|
|
652
|
-
// Build existing research context
|
|
653
|
-
const existingResearchContext = existingResearch
|
|
654
|
-
? Object.entries(existingResearch)
|
|
655
|
-
.map(([lib, entries]) => `### ${lib}\n${entries
|
|
656
|
-
.map((e) => `- Query: "${e.query}"`)
|
|
657
|
-
.join("\n")}`)
|
|
658
|
-
.join("\n\n")
|
|
659
|
-
: "No existing research available.";
|
|
660
|
-
// const existingResearchContext = existingResearch
|
|
661
|
-
// ? "## Existing research\n" +
|
|
662
|
-
// existingResearch
|
|
663
|
-
// .map(
|
|
664
|
-
// (rs) =>
|
|
665
|
-
// rs &&
|
|
666
|
-
// `${rs.recap}\n#### librairies\n${rs.libraries.join(`- \n`)}\n\n#### files:\n${rs.libraries.join(`- \n`)}`,
|
|
667
|
-
// )
|
|
668
|
-
// .join("\n\n")
|
|
669
|
-
// : "No existing research available.";
|
|
670
|
-
// Use PromptBuilder for consistent prompt handling
|
|
671
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
672
|
-
name: "task-enhancement",
|
|
673
|
-
type: "user",
|
|
674
|
-
variables: {
|
|
675
|
-
TASK_TITLE: taskTitle,
|
|
676
|
-
TASK_DESCRIPTION: taskDescription,
|
|
677
|
-
CONTEXT_INFO: `Technology stack: ${stackInfo || "Not specified"}`,
|
|
678
|
-
EXISTING_RESEARCH: existingResearchContext,
|
|
679
|
-
PRD_CONTENT: builtContext.prdContent || "No PRD content available",
|
|
680
|
-
},
|
|
681
|
-
});
|
|
682
|
-
if (!promptResult.success) {
|
|
683
|
-
throw new Error(`Failed to build task enhancement prompt: ${promptResult.error}`);
|
|
684
|
-
}
|
|
685
|
-
const prompt = promptResult.prompt;
|
|
686
|
-
// Merge Context7 MCP tools with filesystem tools
|
|
687
|
-
const allTools = {
|
|
688
|
-
...mcpTools,
|
|
689
|
-
...filesystem_tools_1.filesystemTools,
|
|
690
|
-
};
|
|
691
|
-
const result = await (0, ai_1.streamText)({
|
|
692
|
-
model,
|
|
693
|
-
tools: allTools, // Context7 MCP tools + filesystem tools
|
|
694
|
-
system: prompts_1.TASK_ENHANCEMENT_SYSTEM_PROMPT +
|
|
695
|
-
`
|
|
696
|
-
|
|
697
|
-
You have access to Context7 documentation tools and filesystem tools.
|
|
698
|
-
|
|
699
|
-
## Available Tools:
|
|
700
|
-
- Context7 MCP tools (context7_resolve_library_id, context7_get_library_docs) for library documentation
|
|
701
|
-
- readFile: Read the contents of any file in the project
|
|
702
|
-
- listDirectory: List contents of directories
|
|
703
|
-
|
|
704
|
-
## Research Strategy:
|
|
705
|
-
1. Use Context7 MCP tools for library documentation research
|
|
706
|
-
2. Use filesystem tools to understand project structure, existing code, and dependencies
|
|
707
|
-
3. Synthesize information from all sources to enhance the task
|
|
708
|
-
|
|
709
|
-
Technology stack context: ${stackInfo || "Not specified"}
|
|
710
|
-
|
|
711
|
-
## Available Cached Research:
|
|
712
|
-
${existingResearchContext}`,
|
|
713
|
-
messages: [
|
|
714
|
-
{
|
|
715
|
-
role: "user",
|
|
716
|
-
content: prompt,
|
|
717
|
-
},
|
|
718
|
-
],
|
|
719
|
-
maxRetries: 0, // Disable built-in retries since we handle them manually
|
|
720
|
-
stopWhen: (0, ai_1.stepCountIs)(8), // Allow more steps for comprehensive research
|
|
721
|
-
onError: ({ error }) => {
|
|
722
|
-
// Call user's error callback if provided
|
|
723
|
-
streamingOptions?.onError?.(error);
|
|
724
|
-
// Re-throw errors to maintain existing error handling behavior
|
|
725
|
-
throw error;
|
|
726
|
-
},
|
|
727
|
-
onChunk: streamingOptions?.onChunk
|
|
728
|
-
? ({ chunk }) => {
|
|
729
|
-
if (chunk.type === "text-delta") {
|
|
730
|
-
streamingOptions.onChunk(chunk.text);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
: undefined,
|
|
734
|
-
onFinish: streamingOptions?.onFinish
|
|
735
|
-
? ({ text, finishReason, usage }) => {
|
|
736
|
-
streamingOptions.onFinish({
|
|
737
|
-
text,
|
|
738
|
-
finishReason,
|
|
739
|
-
usage,
|
|
740
|
-
isAborted: false,
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
: undefined,
|
|
744
|
-
});
|
|
745
|
-
// Process tool calls and results properly
|
|
746
|
-
const toolCalls = await result.toolCalls;
|
|
747
|
-
const toolResults = await result.toolResults;
|
|
748
|
-
// Log tool interactions for debugging
|
|
749
|
-
if (toolCalls.length > 0) {
|
|
750
|
-
console.log("AI made tool calls:", toolCalls.map((tc) => ({ tool: tc.toolName, input: tc.input })));
|
|
751
|
-
}
|
|
752
|
-
if (toolResults.length > 0) {
|
|
753
|
-
console.log("Tool results received:", toolResults.map((tr) => ({
|
|
754
|
-
tool: tr.toolName,
|
|
755
|
-
output: tr.output,
|
|
756
|
-
})));
|
|
757
|
-
}
|
|
758
|
-
// Process the stream properly to ensure tool calls execute
|
|
759
|
-
let fullText = "";
|
|
760
|
-
for await (const textPart of result.textStream) {
|
|
761
|
-
fullText += textPart;
|
|
762
|
-
if (streamingOptions?.onChunk) {
|
|
763
|
-
streamingOptions.onChunk(textPart);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
return fullText;
|
|
767
|
-
}, retryConfig, "Task enhancement with documentation")
|
|
768
|
-
.finally(async () => {
|
|
769
|
-
await this.context7Client.closeMCPConnection();
|
|
770
|
-
});
|
|
42
|
+
// Documentation Operations - Delegated to DocumentationOperations
|
|
43
|
+
async enhanceTaskWithDocumentation(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch) {
|
|
44
|
+
return this.docOps.enhanceTaskWithDocumentation(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch);
|
|
771
45
|
}
|
|
772
46
|
async analyzeDocumentationNeeds(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch) {
|
|
773
|
-
return this.
|
|
774
|
-
.executeWithRetry(async () => {
|
|
775
|
-
const mcpTools = await this.context7Client.getMCPTools();
|
|
776
|
-
const defaultAIConfig = this.modelProvider.getAIConfig();
|
|
777
|
-
const aiConfig = config
|
|
778
|
-
? { ...defaultAIConfig, ...config }
|
|
779
|
-
: defaultAIConfig;
|
|
780
|
-
const model = this.modelProvider.getModel(aiConfig);
|
|
781
|
-
// Build existing research context
|
|
782
|
-
const existingResearchContext = existingResearch
|
|
783
|
-
? "## Existing research\n" +
|
|
784
|
-
existingResearch
|
|
785
|
-
.map((rs) => rs &&
|
|
786
|
-
`${rs.recap}\n#### librairies\n${rs.libraries.join(`- \n`)}\n\n#### files:\n${rs.libraries.join(`- \n`)}`)
|
|
787
|
-
.join("\n\n")
|
|
788
|
-
: "No existing research available.";
|
|
789
|
-
// Use PromptBuilder for consistent prompt handling
|
|
790
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
791
|
-
name: "documentation-detection",
|
|
792
|
-
type: "user",
|
|
793
|
-
variables: {
|
|
794
|
-
TASK_TITLE: taskTitle,
|
|
795
|
-
TASK_DESCRIPTION: taskDescription,
|
|
796
|
-
STACK_INFO: stackInfo || "Not specified",
|
|
797
|
-
EXISTING_RESEARCH: existingResearchContext,
|
|
798
|
-
},
|
|
799
|
-
});
|
|
800
|
-
if (!promptResult.success) {
|
|
801
|
-
throw new Error(`Failed to build documentation detection prompt: ${promptResult.error}`);
|
|
802
|
-
}
|
|
803
|
-
const prompt = promptResult.prompt;
|
|
804
|
-
// Track actual libraries and files saved
|
|
805
|
-
const libraries = [];
|
|
806
|
-
const files = [];
|
|
807
|
-
// Merge Context7 MCP tools with custom research tools
|
|
808
|
-
const allTools = {
|
|
809
|
-
...mcpTools,
|
|
810
|
-
};
|
|
811
|
-
const result = await (0, ai_1.streamText)({
|
|
812
|
-
model,
|
|
813
|
-
tools: allTools,
|
|
814
|
-
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.`,
|
|
815
|
-
messages: [{ role: "user", content: prompt }],
|
|
816
|
-
maxRetries: 0,
|
|
817
|
-
stopWhen: (0, ai_1.stepCountIs)(8), // Allow more steps for comprehensive analysis
|
|
818
|
-
onError: ({ error }) => {
|
|
819
|
-
// Call user's error callback if provided
|
|
820
|
-
streamingOptions?.onError?.(error);
|
|
821
|
-
// Re-throw errors to maintain existing error handling behavior
|
|
822
|
-
throw error;
|
|
823
|
-
},
|
|
824
|
-
onChunk: streamingOptions?.onChunk
|
|
825
|
-
? ({ chunk }) => {
|
|
826
|
-
if (chunk.type === "text-delta") {
|
|
827
|
-
streamingOptions.onChunk(chunk.text);
|
|
828
|
-
}
|
|
829
|
-
else if (chunk.type === "tool-result") {
|
|
830
|
-
// Save Context7 documentation if this is a library docs result
|
|
831
|
-
if (chunk.toolName === "get-library-docs" && chunk.output) {
|
|
832
|
-
(async () => {
|
|
833
|
-
try {
|
|
834
|
-
// Extract library from the tool input
|
|
835
|
-
const input = chunk.input;
|
|
836
|
-
const libraryId = input.context7CompatibleLibraryID;
|
|
837
|
-
// Generate unique filename using library ID and timestamp
|
|
838
|
-
const libraryName = libraryId.split("/").pop() || "unknown";
|
|
839
|
-
const timestamp = Date.now();
|
|
840
|
-
const filename = input.topic ?? `${libraryName}-${timestamp}`;
|
|
841
|
-
// Extract content from tool result object
|
|
842
|
-
let content = "";
|
|
843
|
-
if (chunk.output &&
|
|
844
|
-
typeof chunk.output === "object") {
|
|
845
|
-
if ("content" in chunk.output) {
|
|
846
|
-
const contentArray = chunk.output
|
|
847
|
-
.content;
|
|
848
|
-
if (Array.isArray(contentArray)) {
|
|
849
|
-
content = contentArray
|
|
850
|
-
.map((item) => item.text || "")
|
|
851
|
-
.join("\n");
|
|
852
|
-
}
|
|
853
|
-
else {
|
|
854
|
-
content = String(contentArray);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else if ("text" in chunk.output) {
|
|
858
|
-
content = chunk.output.text;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
else if (typeof chunk.output === "string") {
|
|
862
|
-
content = chunk.output;
|
|
863
|
-
}
|
|
864
|
-
if (content) {
|
|
865
|
-
// Save documentation and get the file path
|
|
866
|
-
const docFile = await this.context7Client.saveContext7Documentation(libraryName, filename, content);
|
|
867
|
-
// BUILD THE LIBRARIES ARRAY WITH ACTUAL DATA
|
|
868
|
-
libraries.push({
|
|
869
|
-
name: libraryName,
|
|
870
|
-
context7Id: libraryId,
|
|
871
|
-
reason: "Documentation fetched for task implementation",
|
|
872
|
-
searchQuery: filename,
|
|
873
|
-
});
|
|
874
|
-
// TRACK THE ACTUAL FILE PATH
|
|
875
|
-
files.push(docFile);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
catch (error) {
|
|
879
|
-
console.error("Failed to save Context7 documentation:", error);
|
|
880
|
-
}
|
|
881
|
-
})();
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
: undefined,
|
|
886
|
-
onFinish: streamingOptions?.onFinish
|
|
887
|
-
? ({ text, finishReason, usage }) => {
|
|
888
|
-
streamingOptions.onFinish({
|
|
889
|
-
text,
|
|
890
|
-
finishReason,
|
|
891
|
-
usage,
|
|
892
|
-
isAborted: false,
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
: undefined,
|
|
896
|
-
});
|
|
897
|
-
// Process the stream properly to ensure tool calls execute
|
|
898
|
-
let fullText = "";
|
|
899
|
-
for await (const textPart of result.textStream) {
|
|
900
|
-
fullText += textPart;
|
|
901
|
-
}
|
|
902
|
-
// Get tool results for processing
|
|
903
|
-
const toolResults = await result.toolResults;
|
|
904
|
-
const toolCalls = await result.toolCalls;
|
|
905
|
-
// Save the generated documentation to a task-specific file
|
|
906
|
-
if (fullText.trim()) {
|
|
907
|
-
try {
|
|
908
|
-
const storage = (0, ai_service_factory_1.getStorage)();
|
|
909
|
-
const taskDocFile = await storage.saveTaskDocumentation(taskId, fullText);
|
|
910
|
-
files.push(taskDocFile);
|
|
911
|
-
}
|
|
912
|
-
catch (error) {
|
|
913
|
-
console.error("Failed to save task documentation:", error);
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
return {
|
|
917
|
-
libraries,
|
|
918
|
-
confidence: libraries.length > 0 ? 0.8 : 0.3,
|
|
919
|
-
toolResults: toolResults.map((tr) => ({
|
|
920
|
-
toolName: tr.toolName,
|
|
921
|
-
output: tr.output,
|
|
922
|
-
})),
|
|
923
|
-
files, // Return actual files saved
|
|
924
|
-
};
|
|
925
|
-
}, retryConfig, "Documentation needs analysis")
|
|
926
|
-
.finally(async () => {
|
|
927
|
-
await this.context7Client.closeMCPConnection();
|
|
928
|
-
});
|
|
47
|
+
return this.docOps.analyzeDocumentationNeeds(taskId, taskTitle, taskDescription, stackInfo, streamingOptions, retryConfig, config, existingResearch);
|
|
929
48
|
}
|
|
930
49
|
async generateDocumentationRecap(libraries, documentContents, streamingOptions, retryConfig) {
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
Libraries:
|
|
934
|
-
${libraries
|
|
935
|
-
.map((lib) => `- ${lib.name} (${lib.context7Id}): ${lib.reason}`)
|
|
936
|
-
.join("\n")}
|
|
937
|
-
|
|
938
|
-
Documentation Contents:
|
|
939
|
-
${documentContents
|
|
940
|
-
.map((doc) => `## ${doc.library}\n${doc.content.substring(0, 500)}...`)
|
|
941
|
-
.join("\n\n")}
|
|
942
|
-
|
|
943
|
-
Please provide a 2-3 sentence summary of what documentation is available and how it relates to the task.`;
|
|
944
|
-
return this.retryHandler.executeWithRetry(async () => {
|
|
945
|
-
return this.streamText(prompt, undefined, "You are a technical writer who creates concise summaries of documentation collections.", undefined, streamingOptions, { maxAttempts: 1 } // Disable retries here since we're handling them at the outer level
|
|
946
|
-
);
|
|
947
|
-
}, retryConfig, "Documentation recap generation");
|
|
50
|
+
return this.docOps.generateDocumentationRecap(libraries, documentContents, streamingOptions, retryConfig);
|
|
948
51
|
}
|
|
949
52
|
async planTask(taskContext, taskDetails, config, promptOverride, userMessage, streamingOptions, retryConfig) {
|
|
950
|
-
return this.
|
|
951
|
-
// Use PromptBuilder if no prompt override provided
|
|
952
|
-
let prompt;
|
|
953
|
-
if (promptOverride) {
|
|
954
|
-
prompt = promptOverride;
|
|
955
|
-
}
|
|
956
|
-
else {
|
|
957
|
-
const promptResult = prompt_builder_1.PromptBuilder.buildPrompt({
|
|
958
|
-
name: "task-planning",
|
|
959
|
-
type: "user",
|
|
960
|
-
variables: {
|
|
961
|
-
TASK_CONTEXT: taskContext,
|
|
962
|
-
TASK_DETAILS: taskDetails,
|
|
963
|
-
},
|
|
964
|
-
});
|
|
965
|
-
if (!promptResult.success) {
|
|
966
|
-
throw new Error(`Failed to build task planning prompt: ${promptResult.error}`);
|
|
967
|
-
}
|
|
968
|
-
prompt = promptResult.prompt;
|
|
969
|
-
}
|
|
970
|
-
const model = this.modelProvider.getModel({
|
|
971
|
-
...this.modelProvider.getAIConfig(),
|
|
972
|
-
...config,
|
|
973
|
-
});
|
|
974
|
-
// Get MCP tools and merge with filesystem tools
|
|
975
|
-
const mcpTools = await this.context7Client.getMCPTools();
|
|
976
|
-
const allTools = {
|
|
977
|
-
...mcpTools,
|
|
978
|
-
...filesystem_tools_1.filesystemTools,
|
|
979
|
-
};
|
|
980
|
-
const result = (0, ai_1.streamText)({
|
|
981
|
-
model,
|
|
982
|
-
tools: allTools, // Context7 MCP tools + filesystem tools
|
|
983
|
-
system: prompts_1.TASK_PLANNING_SYSTEM_PROMPT +
|
|
984
|
-
`
|
|
985
|
-
|
|
986
|
-
You have access to filesystem tools that allow you to:
|
|
987
|
-
- readFile: Read the contents of any file in the project
|
|
988
|
-
- listDirectory: List contents of directories
|
|
989
|
-
|
|
990
|
-
Use these tools to understand the project structure, existing code, and dependencies when creating implementation plans.`,
|
|
991
|
-
messages: [{ role: "user", content: userMessage || prompt }],
|
|
992
|
-
maxRetries: 0,
|
|
993
|
-
onChunk: streamingOptions?.onChunk
|
|
994
|
-
? ({ chunk }) => {
|
|
995
|
-
if (chunk.type === "text-delta") {
|
|
996
|
-
streamingOptions.onChunk(chunk.text);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
: undefined,
|
|
1000
|
-
onFinish: streamingOptions?.onFinish
|
|
1001
|
-
? ({ text, finishReason, usage }) => {
|
|
1002
|
-
streamingOptions.onFinish({
|
|
1003
|
-
text,
|
|
1004
|
-
finishReason,
|
|
1005
|
-
usage,
|
|
1006
|
-
isAborted: false,
|
|
1007
|
-
});
|
|
1008
|
-
}
|
|
1009
|
-
: undefined,
|
|
1010
|
-
});
|
|
1011
|
-
// Return the plan text directly - no JSON parsing needed
|
|
1012
|
-
return (await result).text;
|
|
1013
|
-
}, retryConfig, "Task planning");
|
|
53
|
+
return this.taskOps.planTask(taskContext, taskDetails, config, promptOverride, userMessage, streamingOptions, retryConfig);
|
|
1014
54
|
}
|
|
1015
55
|
}
|
|
1016
56
|
exports.AIOperations = AIOperations;
|