feat: Centralize AI prompts into JSON templates (#882)
* centralize prompt management * add changeset * add variant key to determine prompt version * update tests and add prompt manager test * determine internal path, don't use projectRoot * add promptManager mock * detailed prompt docs * add schemas and validator packages * add validate prompts command * add schema validation * update tests * move schemas to src/prompts/schemas * use this.promptsDir for better semantics * add prompt schemas * version schema files & update links * remove validate command * expect dependencies * update docs * fix test * remove suggestmode to ensure clean keys * remove default variant from research and update schema * now handled by prompt manager * add manual test to verify prompts * remove incorrect batch variant * consolidate variants * consolidate analyze-complexity to just default variant * consolidate parse-prd variants * add eq handler for handlebars * consolidate research prompt variants * use brevity * consolidate variants for update subtask * add not handler * consolidate variants for update-task * consolidate update-tasks variants * add conditional content to prompt when research used * update prompt tests * show correct research variant * make variant names link to below * remove changset * restore gitignore * Merge branch 'next' of https://github.com/eyaltoledano/claude-task-master into joedanz/centralize-prompts # Conflicts: # package-lock.json # scripts/modules/task-manager/expand-task.js # scripts/modules/task-manager/parse-prd.js remove unused * add else * update tests * update biome optional dependencies * responsive html output for mobile
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
} from '../utils.js';
|
||||
import { generateObjectService } from '../ai-services-unified.js';
|
||||
import { getDefaultPriority } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import ContextGatherer from '../utils/contextGatherer.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import {
|
||||
@@ -403,30 +404,6 @@ async function addTask(
|
||||
displayContextAnalysis(analysisData, prompt, gatheredContext.length);
|
||||
}
|
||||
|
||||
// System Prompt - Enhanced for dependency awareness
|
||||
const systemPrompt =
|
||||
"You are a helpful assistant that creates well-structured tasks for a software development project. Generate a single new task based on the user's description, adhering strictly to the provided JSON schema. Pay special attention to dependencies between tasks, ensuring the new task correctly references any tasks it depends on.\n\n" +
|
||||
'When determining dependencies for a new task, follow these principles:\n' +
|
||||
'1. Select dependencies based on logical requirements - what must be completed before this task can begin.\n' +
|
||||
'2. Prioritize task dependencies that are semantically related to the functionality being built.\n' +
|
||||
'3. Consider both direct dependencies (immediately prerequisite) and indirect dependencies.\n' +
|
||||
'4. Avoid adding unnecessary dependencies - only include tasks that are genuinely prerequisite.\n' +
|
||||
'5. Consider the current status of tasks - prefer completed tasks as dependencies when possible.\n' +
|
||||
"6. Pay special attention to foundation tasks (1-5) but don't automatically include them without reason.\n" +
|
||||
'7. Recent tasks (higher ID numbers) may be more relevant for newer functionality.\n\n' +
|
||||
'The dependencies array should contain task IDs (numbers) of prerequisite tasks.\n';
|
||||
|
||||
// Task Structure Description (for user prompt)
|
||||
const taskStructureDesc = `
|
||||
{
|
||||
"title": "Task title goes here",
|
||||
"description": "A concise one or two sentence description of what the task involves",
|
||||
"details": "Detailed implementation steps, considerations, code examples, or technical approach",
|
||||
"testStrategy": "Specific steps to verify correct implementation and functionality",
|
||||
"dependencies": [1, 3] // Example: IDs of tasks that must be completed before this task
|
||||
}
|
||||
`;
|
||||
|
||||
// Add any manually provided details to the prompt for context
|
||||
let contextFromArgs = '';
|
||||
if (manualTaskData?.title)
|
||||
@@ -438,18 +415,21 @@ async function addTask(
|
||||
if (manualTaskData?.testStrategy)
|
||||
contextFromArgs += `\n- Additional Test Strategy Context: "${manualTaskData.testStrategy}"`;
|
||||
|
||||
// User Prompt
|
||||
const userPrompt = `You are generating the details for Task #${newTaskId}. Based on the user's request: "${prompt}", create a comprehensive new task for a software development project.
|
||||
|
||||
${gatheredContext}
|
||||
|
||||
Based on the information about existing tasks provided above, include appropriate dependencies in the "dependencies" array. Only include task IDs that this new task directly depends on.
|
||||
|
||||
Return your answer as a single JSON object matching the schema precisely:
|
||||
${taskStructureDesc}
|
||||
|
||||
Make sure the details and test strategy are comprehensive and specific. DO NOT include the task ID in the title.
|
||||
`;
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||
'add-task',
|
||||
{
|
||||
prompt,
|
||||
newTaskId,
|
||||
existingTasks: allTasks,
|
||||
gatheredContext,
|
||||
contextFromArgs,
|
||||
useResearch,
|
||||
priority: effectivePriority,
|
||||
dependencies: numericDependencies
|
||||
}
|
||||
);
|
||||
|
||||
// Start the loading indicator - only for text mode
|
||||
if (outputFormat === 'text') {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
|
||||
import { getDebugFlag, getProjectName } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import {
|
||||
COMPLEXITY_REPORT_FILE,
|
||||
LEGACY_TASKS_FILE
|
||||
@@ -396,12 +397,21 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
}
|
||||
|
||||
// Continue with regular analysis path
|
||||
const prompt = generateInternalComplexityAnalysisPrompt(
|
||||
tasksData,
|
||||
gatheredContext
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
const promptParams = {
|
||||
tasks: tasksData.tasks,
|
||||
gatheredContext: gatheredContext || '',
|
||||
useResearch: useResearch
|
||||
};
|
||||
|
||||
const variantKey = useResearch ? 'research' : 'default';
|
||||
const { systemPrompt, userPrompt: prompt } = await promptManager.loadPrompt(
|
||||
'analyze-complexity',
|
||||
promptParams,
|
||||
variantKey
|
||||
);
|
||||
const systemPrompt =
|
||||
'You are an expert software architect and project manager analyzing task complexity. Respond only with the requested valid JSON array.';
|
||||
|
||||
let loadingIndicator = null;
|
||||
if (outputFormat === 'text') {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
|
||||
import { getDefaultSubtasks, getDebugFlag } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.js';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
@@ -60,128 +61,6 @@ const subtaskWrapperSchema = z.object({
|
||||
});
|
||||
// --- End Zod Schemas ---
|
||||
|
||||
/**
|
||||
* Generates the system prompt for the main AI role (e.g., Claude).
|
||||
* @param {number} subtaskCount - The target number of subtasks.
|
||||
* @returns {string} The system prompt.
|
||||
*/
|
||||
function generateMainSystemPrompt(subtaskCount) {
|
||||
return `You are an AI assistant helping with task breakdown for software development.
|
||||
You need to break down a high-level task into ${subtaskCount > 0 ? subtaskCount : 'an appropriate number of'} specific subtasks that can be implemented one by one.
|
||||
|
||||
Subtasks should:
|
||||
1. Be specific and actionable implementation steps
|
||||
2. Follow a logical sequence
|
||||
3. Each handle a distinct part of the parent task
|
||||
4. Include clear guidance on implementation approach
|
||||
5. Have appropriate dependency chains between subtasks (using the new sequential IDs)
|
||||
6. Collectively cover all aspects of the parent task
|
||||
|
||||
For each subtask, provide:
|
||||
- id: Sequential integer starting from the provided nextSubtaskId
|
||||
- title: Clear, specific title
|
||||
- description: Detailed description
|
||||
- dependencies: Array of prerequisite subtask IDs (use the new sequential IDs)
|
||||
- details: Implementation details, the output should be in string
|
||||
- testStrategy: Optional testing approach
|
||||
|
||||
|
||||
Respond ONLY with a valid JSON object containing a single key "subtasks" whose value is an array matching the structure described. Do not include any explanatory text, markdown formatting, or code block markers.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the user prompt for the main AI role (e.g., Claude).
|
||||
* @param {Object} task - The parent task object.
|
||||
* @param {number} subtaskCount - The target number of subtasks.
|
||||
* @param {string} additionalContext - Optional additional context.
|
||||
* @param {number} nextSubtaskId - The starting ID for the new subtasks.
|
||||
* @returns {string} The user prompt.
|
||||
*/
|
||||
function generateMainUserPrompt(
|
||||
task,
|
||||
subtaskCount,
|
||||
additionalContext,
|
||||
nextSubtaskId
|
||||
) {
|
||||
const contextPrompt = additionalContext
|
||||
? `\n\nAdditional context: ${additionalContext}`
|
||||
: '';
|
||||
const schemaDescription = `
|
||||
{
|
||||
"subtasks": [
|
||||
{
|
||||
"id": ${nextSubtaskId}, // First subtask ID
|
||||
"title": "Specific subtask title",
|
||||
"description": "Detailed description",
|
||||
"dependencies": [], // e.g., [${nextSubtaskId + 1}] if it depends on the next
|
||||
"details": "Implementation guidance",
|
||||
"testStrategy": "Optional testing approach"
|
||||
},
|
||||
// ... (repeat for ${subtaskCount ? 'a total of ' + subtaskCount : 'each of the'} subtasks with sequential IDs)
|
||||
]
|
||||
}`;
|
||||
|
||||
return `Break down this task into ${subtaskCount > 0 ? 'exactly ' + subtaskCount : 'an appropriate number of'} specific subtasks:
|
||||
|
||||
Task ID: ${task.id}
|
||||
Title: ${task.title}
|
||||
Description: ${task.description}
|
||||
Current details: ${task.details || 'None'}
|
||||
${contextPrompt}
|
||||
|
||||
Return ONLY the JSON object containing the "subtasks" array, matching this structure:
|
||||
${schemaDescription}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the user prompt for the research AI role (e.g., Perplexity).
|
||||
* @param {Object} task - The parent task object.
|
||||
* @param {number} subtaskCount - The target number of subtasks.
|
||||
* @param {string} additionalContext - Optional additional context.
|
||||
* @param {number} nextSubtaskId - The starting ID for the new subtasks.
|
||||
* @returns {string} The user prompt.
|
||||
*/
|
||||
function generateResearchUserPrompt(
|
||||
task,
|
||||
subtaskCount,
|
||||
additionalContext,
|
||||
nextSubtaskId
|
||||
) {
|
||||
const contextPrompt = additionalContext
|
||||
? `\n\nConsider this context: ${additionalContext}`
|
||||
: '';
|
||||
const schemaDescription = `
|
||||
{
|
||||
"subtasks": [
|
||||
{
|
||||
"id": <number>, // Sequential ID starting from ${nextSubtaskId}
|
||||
"title": "<string>",
|
||||
"description": "<string>",
|
||||
"dependencies": [<number>], // e.g., [${nextSubtaskId + 1}]. If no dependencies, use an empty array [].
|
||||
"details": "<string>",
|
||||
"testStrategy": "<string>" // Optional
|
||||
},
|
||||
// ... (repeat for ${subtaskCount} subtasks)
|
||||
]
|
||||
}`;
|
||||
|
||||
return `Analyze the following task and break it down into ${subtaskCount > 0 ? 'exactly ' + subtaskCount : 'an appropriate number of'} specific subtasks using your research capabilities. Assign sequential IDs starting from ${nextSubtaskId}.
|
||||
|
||||
Parent Task:
|
||||
ID: ${task.id}
|
||||
Title: ${task.title}
|
||||
Description: ${task.description}
|
||||
Current details: ${task.details || 'None'}
|
||||
${contextPrompt}
|
||||
|
||||
CRITICAL: Respond ONLY with a valid JSON object containing a single key "subtasks". The value must be an array of the generated subtasks, strictly matching this structure:
|
||||
${schemaDescription}
|
||||
|
||||
Important: For the 'dependencies' field, if a subtask has no dependencies, you MUST use an empty array, for example: "dependencies": []. Do not use null or omit the field.
|
||||
|
||||
Do not include ANY explanatory text, markdown, or code block markers. Just the JSON object.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse subtasks from AI's text response. Includes basic cleanup.
|
||||
* @param {string} text - Response text from AI.
|
||||
@@ -499,9 +378,7 @@ async function expandTask(
|
||||
|
||||
// --- Complexity Report Integration ---
|
||||
let finalSubtaskCount;
|
||||
let promptContent = '';
|
||||
let complexityReasoningContext = '';
|
||||
let systemPrompt; // Declare systemPrompt here
|
||||
|
||||
// Use tag-aware complexity report path
|
||||
const complexityReportPath = getTagAwareFilePath(
|
||||
@@ -570,52 +447,46 @@ async function expandTask(
|
||||
// Determine prompt content AND system prompt
|
||||
const nextSubtaskId = (task.subtasks?.length || 0) + 1;
|
||||
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
// Combine all context sources into a single additionalContext parameter
|
||||
let combinedAdditionalContext = '';
|
||||
if (additionalContext || complexityReasoningContext) {
|
||||
combinedAdditionalContext =
|
||||
`\n\n${additionalContext}${complexityReasoningContext}`.trim();
|
||||
}
|
||||
if (gatheredContext) {
|
||||
combinedAdditionalContext =
|
||||
`${combinedAdditionalContext}\n\n# Project Context\n\n${gatheredContext}`.trim();
|
||||
}
|
||||
|
||||
const promptParams = {
|
||||
task: task,
|
||||
subtaskCount: finalSubtaskCount,
|
||||
nextSubtaskId: nextSubtaskId,
|
||||
additionalContext: additionalContext,
|
||||
complexityReasoningContext: complexityReasoningContext,
|
||||
gatheredContext: gatheredContext,
|
||||
useResearch: useResearch,
|
||||
expansionPrompt: taskAnalysis?.expansionPrompt || null
|
||||
};
|
||||
|
||||
let variantKey = 'default';
|
||||
if (taskAnalysis?.expansionPrompt) {
|
||||
// Use prompt from complexity report
|
||||
promptContent = taskAnalysis.expansionPrompt;
|
||||
// Append additional context and reasoning
|
||||
promptContent += `\n\n${additionalContext}`.trim();
|
||||
promptContent += `${complexityReasoningContext}`.trim();
|
||||
if (gatheredContext) {
|
||||
promptContent += `\n\n# Project Context\n\n${gatheredContext}`;
|
||||
}
|
||||
|
||||
// --- Use Simplified System Prompt for Report Prompts ---
|
||||
systemPrompt = `You are an AI assistant helping with task breakdown. Generate ${finalSubtaskCount > 0 ? 'exactly ' + finalSubtaskCount : 'an appropriate number of'} subtasks based on the provided prompt and context. Respond ONLY with a valid JSON object containing a single key "subtasks" whose value is an array of the generated subtask objects. Each subtask object in the array must have keys: "id", "title", "description", "dependencies", "details", "status". Ensure the 'id' starts from ${nextSubtaskId} and is sequential. Ensure 'dependencies' only reference valid prior subtask IDs generated in this response (starting from ${nextSubtaskId}). Ensure 'status' is 'pending'. Do not include any other text or explanation.`;
|
||||
variantKey = 'complexity-report';
|
||||
logger.info(
|
||||
`Using expansion prompt from complexity report and simplified system prompt for task ${task.id}.`
|
||||
`Using expansion prompt from complexity report for task ${task.id}.`
|
||||
);
|
||||
// --- End Simplified System Prompt ---
|
||||
} else if (useResearch) {
|
||||
variantKey = 'research';
|
||||
logger.info(`Using research variant for task ${task.id}.`);
|
||||
} else {
|
||||
// Use standard prompt generation
|
||||
let combinedAdditionalContext =
|
||||
`${additionalContext}${complexityReasoningContext}`.trim();
|
||||
if (gatheredContext) {
|
||||
combinedAdditionalContext =
|
||||
`${combinedAdditionalContext}\n\n# Project Context\n\n${gatheredContext}`.trim();
|
||||
}
|
||||
|
||||
if (useResearch) {
|
||||
promptContent = generateResearchUserPrompt(
|
||||
task,
|
||||
finalSubtaskCount,
|
||||
combinedAdditionalContext,
|
||||
nextSubtaskId
|
||||
);
|
||||
// Use the specific research system prompt if needed, or a standard one
|
||||
systemPrompt = `You are an AI assistant that responds ONLY with valid JSON objects as requested. The object should contain a 'subtasks' array.`; // Or keep generateResearchSystemPrompt if it exists
|
||||
} else {
|
||||
promptContent = generateMainUserPrompt(
|
||||
task,
|
||||
finalSubtaskCount,
|
||||
combinedAdditionalContext,
|
||||
nextSubtaskId
|
||||
);
|
||||
// Use the original detailed system prompt for standard generation
|
||||
systemPrompt = generateMainSystemPrompt(finalSubtaskCount);
|
||||
}
|
||||
logger.info(`Using standard prompt generation for task ${task.id}.`);
|
||||
}
|
||||
|
||||
const { systemPrompt, userPrompt: promptContent } =
|
||||
await promptManager.loadPrompt('expand-task', promptParams, variantKey);
|
||||
// --- End Complexity Report / Prompt Logic ---
|
||||
|
||||
// --- AI Subtask Generation using generateTextService ---
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
|
||||
import { generateObjectService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { displayAiUsageSummary } from '../ui.js';
|
||||
|
||||
@@ -147,10 +148,8 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
|
||||
report(overwriteError.message, 'error');
|
||||
if (outputFormat === 'text') {
|
||||
console.error(chalk.red(overwriteError.message));
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw overwriteError;
|
||||
}
|
||||
throw overwriteError;
|
||||
} else {
|
||||
// Force overwrite is true
|
||||
report(
|
||||
@@ -172,74 +171,24 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
|
||||
throw new Error(`Input file ${prdPath} is empty or could not be read.`);
|
||||
}
|
||||
|
||||
// Research-specific enhancements to the system prompt
|
||||
const researchPromptAddition = research
|
||||
? `\nBefore breaking down the PRD into tasks, you will:
|
||||
1. Research and analyze the latest technologies, libraries, frameworks, and best practices that would be appropriate for this project
|
||||
2. Identify any potential technical challenges, security concerns, or scalability issues not explicitly mentioned in the PRD without discarding any explicit requirements or going overboard with complexity -- always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches
|
||||
3. Consider current industry standards and evolving trends relevant to this project (this step aims to solve LLM hallucinations and out of date information due to training data cutoff dates)
|
||||
4. Evaluate alternative implementation approaches and recommend the most efficient path
|
||||
5. Include specific library versions, helpful APIs, and concrete implementation guidance based on your research
|
||||
6. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
Your task breakdown should incorporate this research, resulting in more detailed implementation guidance, more accurate dependency mapping, and more precise technology recommendations than would be possible from the PRD text alone, while maintaining all explicit requirements and best practices and all details and nuances of the PRD.`
|
||||
: '';
|
||||
// Get defaultTaskPriority from config
|
||||
const { getDefaultPriority } = await import('../config-manager.js');
|
||||
const defaultTaskPriority = getDefaultPriority(projectRoot) || 'medium';
|
||||
|
||||
// Base system prompt for PRD parsing
|
||||
const systemPrompt = `You are an AI assistant specialized in analyzing Product Requirements Documents (PRDs) and generating a structured, logically ordered, dependency-aware and sequenced list of development tasks in JSON format.${researchPromptAddition}
|
||||
|
||||
Analyze the provided PRD content and generate ${numTasks > 0 ? 'approximately ' + numTasks : 'an appropriate number of'} top-level development tasks. If the complexity or the level of detail of the PRD is high, generate more tasks relative to the complexity of the PRD
|
||||
Each task should represent a logical unit of work needed to implement the requirements and focus on the most direct and effective way to implement the requirements without unnecessary complexity or overengineering. Include pseudo-code, implementation details, and test strategy for each task. Find the most up to date information to implement each task.
|
||||
Assign sequential IDs starting from ${nextId}. Infer title, description, details, and test strategy for each task based *only* on the PRD content.
|
||||
Set status to 'pending', dependencies to an empty array [], and priority to 'medium' initially for all tasks.
|
||||
Respond ONLY with a valid JSON object containing a single key "tasks", where the value is an array of task objects adhering to the provided Zod schema. Do not include any explanation or markdown formatting.
|
||||
|
||||
Each task should follow this JSON structure:
|
||||
{
|
||||
"id": number,
|
||||
"title": string,
|
||||
"description": string,
|
||||
"status": "pending",
|
||||
"dependencies": number[] (IDs of tasks this depends on),
|
||||
"priority": "high" | "medium" | "low",
|
||||
"details": string (implementation details),
|
||||
"testStrategy": string (validation approach)
|
||||
}
|
||||
|
||||
Guidelines:
|
||||
1. ${numTasks > 0 ? 'Unless complexity warrants otherwise' : 'Depending on the complexity'}, create ${numTasks > 0 ? 'exactly ' + numTasks : 'an appropriate number of'} tasks, numbered sequentially starting from ${nextId}
|
||||
2. Each task should be atomic and focused on a single responsibility following the most up to date best practices and standards
|
||||
3. Order tasks logically - consider dependencies and implementation sequence
|
||||
4. Early tasks should focus on setup, core functionality first, then advanced features
|
||||
5. Include clear validation/testing approach for each task
|
||||
6. Set appropriate dependency IDs (a task can only depend on tasks with lower IDs, potentially including existing tasks with IDs less than ${nextId} if applicable)
|
||||
7. Assign priority (high/medium/low) based on criticality and dependency order
|
||||
8. Include detailed implementation guidance in the "details" field${research ? ', with specific libraries and version recommendations based on your research' : ''}
|
||||
9. If the PRD contains specific requirements for libraries, database schemas, frameworks, tech stacks, or any other implementation details, STRICTLY ADHERE to these requirements in your task breakdown and do not discard them under any circumstance
|
||||
10. Focus on filling in any gaps left by the PRD or areas that aren't fully specified, while preserving all explicit requirements
|
||||
11. Always aim to provide the most direct path to implementation, avoiding over-engineering or roundabout approaches${research ? '\n12. For each task, include specific, actionable guidance based on current industry standards and best practices discovered through research' : ''}`;
|
||||
|
||||
// Build user prompt with PRD content
|
||||
const userPrompt = `Here's the Product Requirements Document (PRD) to break down into approximately ${numTasks > 0 ? 'approximately ' + numTasks : 'an appropriate number of'} tasks, starting IDs from ${nextId}:${research ? '\n\nRemember to thoroughly research current best practices and technologies before task breakdown to provide specific, actionable implementation details.' : ''}\n\n${prdContent}\n\n
|
||||
|
||||
Return your response in this format:
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Setup Project Repository",
|
||||
"description": "...",
|
||||
...
|
||||
},
|
||||
...
|
||||
],
|
||||
"metadata": {
|
||||
"projectName": "PRD Implementation",
|
||||
"totalTasks": {number of tasks},
|
||||
"sourceFile": "${prdPath}",
|
||||
"generatedAt": "YYYY-MM-DD"
|
||||
}
|
||||
}`;
|
||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||
'parse-prd',
|
||||
{
|
||||
research,
|
||||
numTasks,
|
||||
nextId,
|
||||
prdContent,
|
||||
prdPath,
|
||||
defaultTaskPriority
|
||||
}
|
||||
);
|
||||
|
||||
// Call the unified AI service
|
||||
report(
|
||||
@@ -420,11 +369,9 @@ Guidelines:
|
||||
// Use projectRoot for debug flag check
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
} else {
|
||||
throw error; // Re-throw for JSON output
|
||||
}
|
||||
|
||||
throw error; // Always re-throw for proper error handling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { highlight } from 'cli-highlight';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import {
|
||||
log as consoleLog,
|
||||
findProjectRoot,
|
||||
@@ -190,14 +191,26 @@ async function performResearch(
|
||||
const gatheredContext = contextResult.context;
|
||||
const tokenBreakdown = contextResult.tokenBreakdown;
|
||||
|
||||
// Build system prompt based on detail level
|
||||
const systemPrompt = buildResearchSystemPrompt(detailLevel, projectRoot);
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
// Build user prompt with context
|
||||
const userPrompt = buildResearchUserPrompt(
|
||||
query,
|
||||
gatheredContext,
|
||||
detailLevel
|
||||
const promptParams = {
|
||||
query: query,
|
||||
gatheredContext: gatheredContext || '',
|
||||
detailLevel: detailLevel,
|
||||
projectInfo: {
|
||||
root: projectRoot,
|
||||
taskCount: finalTaskIds.length,
|
||||
fileCount: filePaths.length
|
||||
}
|
||||
};
|
||||
|
||||
// Select variant based on detail level
|
||||
const variantKey = detailLevel; // 'low', 'medium', or 'high'
|
||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||
'research',
|
||||
promptParams,
|
||||
variantKey
|
||||
);
|
||||
|
||||
// Count tokens for system and user prompts
|
||||
@@ -349,94 +362,6 @@ async function performResearch(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build system prompt for research based on detail level
|
||||
* @param {string} detailLevel - Detail level: 'low', 'medium', 'high'
|
||||
* @param {string} projectRoot - Project root for context
|
||||
* @returns {string} System prompt
|
||||
*/
|
||||
function buildResearchSystemPrompt(detailLevel, projectRoot) {
|
||||
const basePrompt = `You are an expert AI research assistant helping with a software development project. You have access to project context including tasks, files, and project structure.
|
||||
|
||||
Your role is to provide comprehensive, accurate, and actionable research responses based on the user's query and the provided project context.`;
|
||||
|
||||
const detailInstructions = {
|
||||
low: `
|
||||
**Response Style: Concise & Direct**
|
||||
- Provide brief, focused answers (2-4 paragraphs maximum)
|
||||
- Focus on the most essential information
|
||||
- Use bullet points for key takeaways
|
||||
- Avoid lengthy explanations unless critical
|
||||
- Skip pleasantries, introductions, and conclusions
|
||||
- No phrases like "Based on your project context" or "I'll provide guidance"
|
||||
- No summary outros or alignment statements
|
||||
- Get straight to the actionable information
|
||||
- Use simple, direct language - users want info, not explanation`,
|
||||
|
||||
medium: `
|
||||
**Response Style: Balanced & Comprehensive**
|
||||
- Provide thorough but well-structured responses (4-8 paragraphs)
|
||||
- Include relevant examples and explanations
|
||||
- Balance depth with readability
|
||||
- Use headings and bullet points for organization`,
|
||||
|
||||
high: `
|
||||
**Response Style: Detailed & Exhaustive**
|
||||
- Provide comprehensive, in-depth analysis (8+ paragraphs)
|
||||
- Include multiple perspectives and approaches
|
||||
- Provide detailed examples, code snippets, and step-by-step guidance
|
||||
- Cover edge cases and potential pitfalls
|
||||
- Use clear structure with headings, subheadings, and lists`
|
||||
};
|
||||
|
||||
return `${basePrompt}
|
||||
|
||||
${detailInstructions[detailLevel]}
|
||||
|
||||
**Guidelines:**
|
||||
- Always consider the project context when formulating responses
|
||||
- Reference specific tasks, files, or project elements when relevant
|
||||
- Provide actionable insights that can be applied to the project
|
||||
- If the query relates to existing project tasks, suggest how the research applies to those tasks
|
||||
- Use markdown formatting for better readability
|
||||
- Be precise and avoid speculation unless clearly marked as such
|
||||
|
||||
**For LOW detail level specifically:**
|
||||
- Start immediately with the core information
|
||||
- No introductory phrases or context acknowledgments
|
||||
- No concluding summaries or project alignment statements
|
||||
- Focus purely on facts, steps, and actionable items`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build user prompt with query and context
|
||||
* @param {string} query - User's research query
|
||||
* @param {string} gatheredContext - Gathered project context
|
||||
* @param {string} detailLevel - Detail level for response guidance
|
||||
* @returns {string} Complete user prompt
|
||||
*/
|
||||
function buildResearchUserPrompt(query, gatheredContext, detailLevel) {
|
||||
let prompt = `# Research Query
|
||||
|
||||
${query}`;
|
||||
|
||||
if (gatheredContext && gatheredContext.trim()) {
|
||||
prompt += `
|
||||
|
||||
# Project Context
|
||||
|
||||
${gatheredContext}`;
|
||||
}
|
||||
|
||||
prompt += `
|
||||
|
||||
# Instructions
|
||||
|
||||
Please research and provide a ${detailLevel}-detail response to the query above. Consider the project context provided and make your response as relevant and actionable as possible for this specific project.`;
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display detailed token breakdown for context and prompts
|
||||
* @param {Object} tokenBreakdown - Token breakdown from context gatherer
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from '../utils.js';
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
|
||||
@@ -223,30 +224,25 @@ async function updateSubtaskById(
|
||||
}
|
||||
: null;
|
||||
|
||||
const contextString = `
|
||||
Parent Task: ${JSON.stringify(parentContext)}
|
||||
${prevSubtask ? `Previous Subtask: ${JSON.stringify(prevSubtask)}` : ''}
|
||||
${nextSubtask ? `Next Subtask: ${JSON.stringify(nextSubtask)}` : ''}
|
||||
Current Subtask Details (for context only):\n${subtask.details || '(No existing details)'}
|
||||
`;
|
||||
// Build prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
const systemPrompt = `You are an AI assistant helping to update a subtask. You will be provided with the subtask's existing details, context about its parent and sibling tasks, and a user request string.
|
||||
const promptParams = {
|
||||
parentTask: parentContext,
|
||||
prevSubtask: prevSubtask,
|
||||
nextSubtask: nextSubtask,
|
||||
currentDetails: subtask.details || '(No existing details)',
|
||||
updatePrompt: prompt,
|
||||
useResearch: useResearch,
|
||||
gatheredContext: gatheredContext || ''
|
||||
};
|
||||
|
||||
Your Goal: Based *only* on the user's request and all the provided context (including existing details if relevant to the request), GENERATE the new text content that should be added to the subtask's details.
|
||||
Focus *only* on generating the substance of the update.
|
||||
|
||||
Output Requirements:
|
||||
1. Return *only* the newly generated text content as a plain string. Do NOT return a JSON object or any other structured data.
|
||||
2. Your string response should NOT include any of the subtask's original details, unless the user's request explicitly asks to rephrase, summarize, or directly modify existing text.
|
||||
3. Do NOT include any timestamps, XML-like tags, markdown, or any other special formatting in your string response.
|
||||
4. Ensure the generated text is concise yet complete for the update based on the user request. Avoid conversational fillers or explanations about what you are doing (e.g., do not start with "Okay, here's the update...").`;
|
||||
|
||||
// Pass the existing subtask.details in the user prompt for the AI's context.
|
||||
let userPrompt = `Task Context:\n${contextString}\n\nUser Request: "${prompt}"\n\nBased on the User Request and all the Task Context (including current subtask details provided above), what is the new information or text that should be appended to this subtask's details? Return ONLY this new text as a plain string.`;
|
||||
|
||||
if (gatheredContext) {
|
||||
userPrompt += `\n\n# Additional Project Context\n\n${gatheredContext}`;
|
||||
}
|
||||
const variantKey = useResearch ? 'research' : 'default';
|
||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||
'update-subtask',
|
||||
promptParams,
|
||||
variantKey
|
||||
);
|
||||
|
||||
const role = useResearch ? 'research' : 'main';
|
||||
report('info', `Using AI text service with role: ${role}`);
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getDebugFlag, isApiKeySet } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import { ContextGatherer } from '../utils/contextGatherer.js';
|
||||
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
|
||||
|
||||
@@ -408,69 +409,61 @@ async function updateTaskById(
|
||||
);
|
||||
}
|
||||
|
||||
// --- Build Prompts (Different for append vs full update) ---
|
||||
// --- Build Prompts using PromptManager ---
|
||||
const promptManager = getPromptManager();
|
||||
|
||||
const promptParams = {
|
||||
task: taskToUpdate,
|
||||
taskJson: JSON.stringify(taskToUpdate, null, 2),
|
||||
updatePrompt: prompt,
|
||||
appendMode: appendMode,
|
||||
useResearch: useResearch,
|
||||
currentDetails: taskToUpdate.details || '(No existing details)',
|
||||
gatheredContext: gatheredContext || ''
|
||||
};
|
||||
|
||||
const variantKey = appendMode
|
||||
? 'append'
|
||||
: useResearch
|
||||
? 'research'
|
||||
: 'default';
|
||||
|
||||
report(
|
||||
'info',
|
||||
`Loading prompt template with variant: ${variantKey}, appendMode: ${appendMode}, useResearch: ${useResearch}`
|
||||
);
|
||||
|
||||
let systemPrompt;
|
||||
let userPrompt;
|
||||
try {
|
||||
const promptResult = await promptManager.loadPrompt(
|
||||
'update-task',
|
||||
promptParams,
|
||||
variantKey
|
||||
);
|
||||
report(
|
||||
'info',
|
||||
`Prompt result type: ${typeof promptResult}, keys: ${promptResult ? Object.keys(promptResult).join(', ') : 'null'}`
|
||||
);
|
||||
|
||||
if (appendMode) {
|
||||
// Append mode: generate new content to add to task details
|
||||
systemPrompt = `You are an AI assistant helping to append additional information to a software development task. You will be provided with the task's existing details, context, and a user request string.
|
||||
// Extract prompts - loadPrompt returns { systemPrompt, userPrompt, metadata }
|
||||
systemPrompt = promptResult.systemPrompt;
|
||||
userPrompt = promptResult.userPrompt;
|
||||
|
||||
Your Goal: Based *only* on the user's request and all the provided context (including existing details if relevant to the request), GENERATE the new text content that should be added to the task's details.
|
||||
Focus *only* on generating the substance of the update.
|
||||
report(
|
||||
'info',
|
||||
`Loaded prompts - systemPrompt length: ${systemPrompt?.length}, userPrompt length: ${userPrompt?.length}`
|
||||
);
|
||||
} catch (error) {
|
||||
report('error', `Failed to load prompt template: ${error.message}`);
|
||||
throw new Error(`Failed to load prompt template: ${error.message}`);
|
||||
}
|
||||
|
||||
Output Requirements:
|
||||
1. Return *only* the newly generated text content as a plain string. Do NOT return a JSON object or any other structured data.
|
||||
2. Your string response should NOT include any of the task's original details, unless the user's request explicitly asks to rephrase, summarize, or directly modify existing text.
|
||||
3. Do NOT include any timestamps, XML-like tags, markdown, or any other special formatting in your string response.
|
||||
4. Ensure the generated text is concise yet complete for the update based on the user request. Avoid conversational fillers or explanations about what you are doing (e.g., do not start with "Okay, here's the update...").`;
|
||||
|
||||
const taskContext = `
|
||||
Task: ${JSON.stringify({
|
||||
id: taskToUpdate.id,
|
||||
title: taskToUpdate.title,
|
||||
description: taskToUpdate.description,
|
||||
status: taskToUpdate.status
|
||||
})}
|
||||
Current Task Details (for context only):\n${taskToUpdate.details || '(No existing details)'}
|
||||
`;
|
||||
|
||||
userPrompt = `Task Context:\n${taskContext}\n\nUser Request: "${prompt}"\n\nBased on the User Request and all the Task Context (including current task details provided above), what is the new information or text that should be appended to this task's details? Return ONLY this new text as a plain string.`;
|
||||
|
||||
if (gatheredContext) {
|
||||
userPrompt += `\n\n# Additional Project Context\n\n${gatheredContext}`;
|
||||
}
|
||||
} else {
|
||||
// Full update mode: use original prompts
|
||||
systemPrompt = `You are an AI assistant helping to update a software development task based on new context.
|
||||
You will be given a task and a prompt describing changes or new implementation details.
|
||||
Your job is to update the task to reflect these changes, while preserving its basic structure.
|
||||
|
||||
Guidelines:
|
||||
1. VERY IMPORTANT: NEVER change the title of the task - keep it exactly as is
|
||||
2. Maintain the same ID, status, and dependencies unless specifically mentioned in the prompt
|
||||
3. Update the description, details, and test strategy to reflect the new information
|
||||
4. Do not change anything unnecessarily - just adapt what needs to change based on the prompt
|
||||
5. Return a complete valid JSON object representing the updated task
|
||||
6. VERY IMPORTANT: Preserve all subtasks marked as "done" or "completed" - do not modify their content
|
||||
7. For tasks with completed subtasks, build upon what has already been done rather than rewriting everything
|
||||
8. If an existing completed subtask needs to be changed/undone based on the new context, DO NOT modify it directly
|
||||
9. Instead, add a new subtask that clearly indicates what needs to be changed or replaced
|
||||
10. Use the existence of completed subtasks as an opportunity to make new subtasks more specific and targeted
|
||||
11. Ensure any new subtasks have unique IDs that don't conflict with existing ones
|
||||
12. CRITICAL: For subtask IDs, use ONLY numeric values (1, 2, 3, etc.) NOT strings ("1", "2", "3")
|
||||
13. CRITICAL: Subtask IDs should start from 1 and increment sequentially (1, 2, 3...) - do NOT use parent task ID as prefix
|
||||
|
||||
The changes described in the prompt should be thoughtfully applied to make the task more accurate and actionable.`;
|
||||
|
||||
const taskDataString = JSON.stringify(taskToUpdate, null, 2);
|
||||
userPrompt = `Here is the task to update:\n${taskDataString}\n\nPlease update this task based on the following new context:\n${prompt}\n\nIMPORTANT: In the task JSON above, any subtasks with "status": "done" or "status": "completed" should be preserved exactly as is. Build your changes around these completed items.`;
|
||||
|
||||
if (gatheredContext) {
|
||||
userPrompt += `\n\n# Project Context\n\n${gatheredContext}`;
|
||||
}
|
||||
|
||||
userPrompt += `\n\nReturn only the updated task as a valid JSON object.`;
|
||||
// If prompts are still not set, throw an error
|
||||
if (!systemPrompt || !userPrompt) {
|
||||
throw new Error(
|
||||
`Failed to load prompts: systemPrompt=${!!systemPrompt}, userPrompt=${!!userPrompt}`
|
||||
);
|
||||
}
|
||||
// --- End Build Prompts ---
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '../ui.js';
|
||||
|
||||
import { getDebugFlag } from '../config-manager.js';
|
||||
import { getPromptManager } from '../prompt-manager.js';
|
||||
import generateTaskFiles from './generate-task-files.js';
|
||||
import { generateTextService } from '../ai-services-unified.js';
|
||||
import { getModelConfiguration } from './models.js';
|
||||
@@ -368,35 +369,18 @@ async function updateTasks(
|
||||
}
|
||||
// --- End Display Tasks ---
|
||||
|
||||
// --- Build Prompts (Unchanged Core Logic) ---
|
||||
// Keep the original system prompt logic
|
||||
const systemPrompt = `You are an AI assistant helping to update software development tasks based on new context.
|
||||
You will be given a set of tasks and a prompt describing changes or new implementation details.
|
||||
Your job is to update the tasks to reflect these changes, while preserving their basic structure.
|
||||
|
||||
Guidelines:
|
||||
1. Maintain the same IDs, statuses, and dependencies unless specifically mentioned in the prompt
|
||||
2. Update titles, descriptions, details, and test strategies to reflect the new information
|
||||
3. Do not change anything unnecessarily - just adapt what needs to change based on the prompt
|
||||
4. You should return ALL the tasks in order, not just the modified ones
|
||||
5. Return a complete valid JSON object with the updated tasks array
|
||||
6. VERY IMPORTANT: Preserve all subtasks marked as "done" or "completed" - do not modify their content
|
||||
7. For tasks with completed subtasks, build upon what has already been done rather than rewriting everything
|
||||
8. If an existing completed subtask needs to be changed/undone based on the new context, DO NOT modify it directly
|
||||
9. Instead, add a new subtask that clearly indicates what needs to be changed or replaced
|
||||
10. Use the existence of completed subtasks as an opportunity to make new subtasks more specific and targeted
|
||||
|
||||
The changes described in the prompt should be applied to ALL tasks in the list.`;
|
||||
|
||||
// Keep the original user prompt logic
|
||||
const taskDataString = JSON.stringify(tasksToUpdate, null, 2);
|
||||
let userPrompt = `Here are the tasks to update:\n${taskDataString}\n\nPlease update these tasks based on the following new context:\n${prompt}\n\nIMPORTANT: In the tasks JSON above, any subtasks with "status": "done" or "status": "completed" should be preserved exactly as is. Build your changes around these completed items.`;
|
||||
|
||||
if (gatheredContext) {
|
||||
userPrompt += `\n\n# Project Context\n\n${gatheredContext}`;
|
||||
}
|
||||
|
||||
userPrompt += `\n\nReturn only the updated tasks as a valid JSON array.`;
|
||||
// --- Build Prompts (Using PromptManager) ---
|
||||
// Load prompts using PromptManager
|
||||
const promptManager = getPromptManager();
|
||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||
'update-tasks',
|
||||
{
|
||||
tasks: tasksToUpdate,
|
||||
updatePrompt: prompt,
|
||||
useResearch,
|
||||
projectContext: gatheredContext
|
||||
}
|
||||
);
|
||||
// --- End Build Prompts ---
|
||||
|
||||
// --- AI Call ---
|
||||
|
||||
Reference in New Issue
Block a user