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:
Joe Danziger
2025-07-10 03:52:11 -04:00
committed by GitHub
parent 4bc8029080
commit a65ad0a47c
36 changed files with 6180 additions and 9034 deletions

View File

@@ -48,7 +48,8 @@ jest.unstable_mockModule(
'../../../../../scripts/modules/config-manager.js',
() => ({
getDebugFlag: jest.fn(() => false),
getDefaultNumTasks: jest.fn(() => 10)
getDefaultNumTasks: jest.fn(() => 10),
getDefaultPriority: jest.fn(() => 'medium')
})
);
@@ -70,6 +71,30 @@ jest.unstable_mockModule(
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/prompt-manager.js',
() => ({
getPromptManager: jest.fn().mockReturnValue({
loadPrompt: jest.fn().mockImplementation((templateName, params) => {
// Create dynamic mock prompts based on the parameters
const { numTasks } = params || {};
let numTasksText = '';
if (numTasks > 0) {
numTasksText = `approximately ${numTasks}`;
} else {
numTasksText = 'an appropriate number of';
}
return Promise.resolve({
systemPrompt: 'Mocked system prompt for parse-prd',
userPrompt: `Generate ${numTasksText} top-level development tasks from the PRD content.`
});
})
})
})
);
// Mock fs module
jest.unstable_mockModule('fs', () => ({
default: {
@@ -348,33 +373,23 @@ describe('parsePRD', () => {
expect(fs.default.writeFileSync).not.toHaveBeenCalled();
});
test('should call process.exit when tasks in tag exist without force flag in CLI mode', async () => {
test('should throw error when tasks in tag exist without force flag in CLI mode', async () => {
// Setup mocks to simulate tasks.json already exists with tasks in the target tag
fs.default.existsSync.mockReturnValue(true);
fs.default.readFileSync.mockReturnValueOnce(
JSON.stringify(existingTasksData)
);
// Mock process.exit for this specific test
const mockProcessExit = jest
.spyOn(process, 'exit')
.mockImplementation((code) => {
throw new Error(`process.exit: ${code}`);
});
// Call the function without mcpLog (CLI mode) and expect it to throw due to mocked process.exit
// Call the function without mcpLog (CLI mode) and expect it to throw an error
// In test environment, process.exit is prevented and error is thrown instead
await expect(
parsePRD('path/to/prd.txt', 'tasks/tasks.json', 3)
).rejects.toThrow('process.exit: 1');
// Verify process.exit was called with code 1
expect(mockProcessExit).toHaveBeenCalledWith(1);
).rejects.toThrow(
"Tag 'master' already contains 2 tasks. Use --force to overwrite or --append to add to existing tasks."
);
// Verify the file was NOT written
expect(fs.default.writeFileSync).not.toHaveBeenCalled();
// Restore the mock
mockProcessExit.mockRestore();
});
test('should append new tasks when append option is true', async () => {