initial version
This commit is contained in:
182
templates/README.md
Normal file
182
templates/README.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# {{projectName}}
|
||||
|
||||
{{projectDescription}}
|
||||
|
||||
A task management system for AI-driven development with Claude, designed to work seamlessly with Cursor AI.
|
||||
|
||||
## Overview
|
||||
|
||||
This project uses the Claude Task Master system to manage development tasks in an AI-driven workflow. The system revolves around a `tasks.json` file, which holds an up-to-date list of development tasks.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Set up your environment:
|
||||
- Copy `.env.example` to `.env`
|
||||
- Add your Anthropic API key to the `.env` file
|
||||
|
||||
3. Parse your PRD to generate tasks:
|
||||
```bash
|
||||
npm run parse-prd -- --input=your-prd-file.txt
|
||||
```
|
||||
|
||||
4. View current tasks:
|
||||
```bash
|
||||
npm run list
|
||||
```
|
||||
|
||||
5. Generate task files:
|
||||
```bash
|
||||
npm run generate
|
||||
```
|
||||
|
||||
## Integrating with Cursor AI
|
||||
|
||||
This project includes Cursor AI integration through the `.cursor/rules/dev_workflow.mdc` file, which provides the AI with knowledge about the task management system.
|
||||
|
||||
### Using Cursor Agent Mode
|
||||
|
||||
1. Open this project in Cursor
|
||||
2. Open Cursor's AI chat and switch to Agent mode
|
||||
3. The agent will automatically understand the task management workflow
|
||||
|
||||
### Working with the Agent
|
||||
|
||||
You can ask the Cursor agent to:
|
||||
|
||||
- **Generate tasks**: "Please parse my PRD at scripts/prd.txt and generate tasks"
|
||||
- **List tasks**: "What tasks are available to work on next?"
|
||||
- **Implement tasks**: "Let's implement task 3. What does it involve?"
|
||||
- **Update task status**: "Task 3 is now complete. Please update its status"
|
||||
- **Handle changes**: "We're now using Express instead of Fastify. Update future tasks"
|
||||
- **Break down tasks**: "Task 5 seems complex. Can you break it down into subtasks?"
|
||||
|
||||
The agent will execute the appropriate commands and guide you through the development process.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
The development workflow follows these steps:
|
||||
|
||||
1. **Initial Setup**: If starting a new project with a PRD document, run `npm run parse-prd -- --input=<prd-file.txt>` to generate the initial tasks.json file.
|
||||
|
||||
2. **Task Discovery**: When a coding session begins, run `npm run list` to see the current tasks, their status, and IDs.
|
||||
|
||||
3. **Task Selection**: Select the next pending task based on these criteria:
|
||||
- Dependencies: Only select tasks whose dependencies are marked as 'done'
|
||||
- Priority: Choose higher priority tasks first ('high' > 'medium' > 'low')
|
||||
- ID order: When priorities are equal, select the task with the lowest ID
|
||||
|
||||
4. **Task Clarification**: If a task description is unclear or lacks detail:
|
||||
- Check if a corresponding task file exists in the tasks/ directory
|
||||
- If more information is needed, ask for clarification
|
||||
- If architectural changes have occurred, run `npm run dev -- update --from=<id> --prompt="<new architectural context>"` to update the task and all subsequent tasks
|
||||
|
||||
5. **Task Breakdown**: For complex tasks that need to be broken down into smaller steps:
|
||||
- Use `npm run dev -- expand --id=<id> --subtasks=<number>` to generate detailed subtasks
|
||||
- Optionally provide additional context with `--prompt="<context>"` to guide subtask generation
|
||||
|
||||
6. **Task Implementation**: Implement the code necessary for the chosen task.
|
||||
|
||||
7. **Task Verification**: Before marking a task as done, verify it according to the task's specified 'testStrategy'.
|
||||
|
||||
8. **Task Completion**: When a task is completed and verified, run `npm run dev -- set-status --id=<id> --status=done` to mark it as done.
|
||||
|
||||
9. **Implementation Drift Handling**: If during implementation, you discover that the approach differs significantly from what was planned, call `npm run dev -- update --from=<futureTaskId> --prompt="Detailed explanation of changes..."` to rewrite subsequent tasks.
|
||||
|
||||
10. **Task File Generation**: After any updates to tasks.json, run `npm run generate` to regenerate the individual task files.
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Parse PRD
|
||||
```bash
|
||||
npm run parse-prd -- --input=<prd-file.txt>
|
||||
```
|
||||
|
||||
### List Tasks
|
||||
```bash
|
||||
npm run list
|
||||
```
|
||||
|
||||
### Update Tasks
|
||||
```bash
|
||||
npm run dev -- update --from=<id> --prompt="<prompt>"
|
||||
```
|
||||
|
||||
### Generate Task Files
|
||||
```bash
|
||||
npm run generate
|
||||
```
|
||||
|
||||
### Set Task Status
|
||||
```bash
|
||||
npm run dev -- set-status --id=<id> --status=<status>
|
||||
```
|
||||
|
||||
### Expand Tasks
|
||||
```bash
|
||||
npm run dev -- expand --id=<id> --subtasks=<number> --prompt="<context>"
|
||||
```
|
||||
or
|
||||
```bash
|
||||
npm run dev -- expand --all
|
||||
```
|
||||
|
||||
## Task Structure
|
||||
|
||||
Tasks in tasks.json have the following structure:
|
||||
|
||||
- `id`: Unique identifier for the task
|
||||
- `title`: Brief, descriptive title of the task
|
||||
- `description`: Concise description of what the task involves
|
||||
- `status`: Current state of the task (pending, done, deferred)
|
||||
- `dependencies`: IDs of tasks that must be completed before this task
|
||||
- `priority`: Importance level of the task (high, medium, low)
|
||||
- `details`: In-depth instructions for implementing the task
|
||||
- `testStrategy`: Approach for verifying the task has been completed correctly
|
||||
- `subtasks`: List of smaller, more specific tasks that make up the main task
|
||||
|
||||
## Best Practices for AI-Driven Development
|
||||
|
||||
1. **Start with a detailed PRD**: The more detailed your PRD, the better the generated tasks will be.
|
||||
|
||||
2. **Review generated tasks**: After parsing the PRD, review the tasks to ensure they make sense and have appropriate dependencies.
|
||||
|
||||
3. **Follow the dependency chain**: Always respect task dependencies - the Cursor agent will help with this.
|
||||
|
||||
4. **Update as you go**: If your implementation diverges from the plan, use the update command to keep future tasks aligned with your current approach.
|
||||
|
||||
5. **Break down complex tasks**: Use the expand command to break down complex tasks into manageable subtasks.
|
||||
|
||||
6. **Regenerate task files**: After any updates to tasks.json, regenerate the task files to keep them in sync.
|
||||
|
||||
7. **Communicate context to the agent**: When asking the Cursor agent to help with a task, provide context about what you're trying to achieve.
|
||||
|
||||
## Configuration
|
||||
|
||||
The system can be configured through environment variables in a `.env` file:
|
||||
|
||||
### Required Configuration
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key for Claude
|
||||
|
||||
### Optional Configuration
|
||||
- `MODEL`: Specify which Claude model to use (default: "claude-3-7-sonnet-20250219")
|
||||
- `MAX_TOKENS`: Maximum tokens for model responses (default: 4000)
|
||||
- `TEMPERATURE`: Temperature for model responses (default: 0.7)
|
||||
- `DEBUG`: Enable debug logging (default: false)
|
||||
- `LOG_LEVEL`: Log level - debug, info, warn, error (default: info)
|
||||
- `DEFAULT_SUBTASKS`: Default number of subtasks when expanding (default: 3)
|
||||
- `DEFAULT_PRIORITY`: Default priority for generated tasks (default: medium)
|
||||
- `PROJECT_NAME`: Override default project name in tasks.json
|
||||
- `PROJECT_VERSION`: Override default version in tasks.json
|
||||
|
||||
## Additional Documentation
|
||||
|
||||
For more detailed documentation on the scripts, see the [scripts/README.md](scripts/README.md) file.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) {{year}} {{authorName}}
|
||||
762
templates/dev.js
Normal file
762
templates/dev.js
Normal file
@@ -0,0 +1,762 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* dev.js
|
||||
*
|
||||
* Subcommands:
|
||||
* 1) parse-prd --input=some-prd.txt [--tasks=10]
|
||||
* -> Creates/overwrites tasks.json with a set of tasks (naive or LLM-based).
|
||||
* -> Optional --tasks parameter limits the number of tasks generated.
|
||||
*
|
||||
* 2) update --from=5 --prompt="We changed from Slack to Discord."
|
||||
* -> Regenerates tasks from ID >= 5 using the provided prompt (or naive approach).
|
||||
*
|
||||
* 3) generate
|
||||
* -> Generates per-task files (e.g., task_001.txt) from tasks.json
|
||||
*
|
||||
* 4) set-status --id=4 --status=done
|
||||
* -> Updates a single task's status to done (or pending, deferred, etc.).
|
||||
*
|
||||
* 5) list
|
||||
* -> Lists tasks in a brief console view (ID, title, status).
|
||||
*
|
||||
* 6) expand --id=3 --subtasks=5 [--prompt="Additional context"]
|
||||
* -> Expands a task with subtasks for more detailed implementation.
|
||||
* -> Use --all instead of --id to expand all tasks.
|
||||
* -> Optional --subtasks parameter controls number of subtasks (default: 3).
|
||||
* -> Add --force when using --all to regenerate subtasks for tasks that already have them.
|
||||
* -> Note: Tasks marked as 'done' or 'completed' are always skipped.
|
||||
*
|
||||
* Usage examples:
|
||||
* node dev.js parse-prd --input=sample-prd.txt
|
||||
* node dev.js parse-prd --input=sample-prd.txt --tasks=10
|
||||
* node dev.js update --from=4 --prompt="Refactor tasks from ID 4 onward"
|
||||
* node dev.js generate
|
||||
* node dev.js set-status --id=3 --status=done
|
||||
* node dev.js list
|
||||
* node dev.js expand --id=3 --subtasks=5
|
||||
* node dev.js expand --all
|
||||
* node dev.js expand --all --force
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Load environment variables from .env file
|
||||
dotenv.config();
|
||||
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
// Set up configuration with environment variables or defaults
|
||||
const CONFIG = {
|
||||
model: process.env.MODEL || "claude-3-7-sonnet-20250219",
|
||||
maxTokens: parseInt(process.env.MAX_TOKENS || "4000"),
|
||||
temperature: parseFloat(process.env.TEMPERATURE || "0.7"),
|
||||
debug: process.env.DEBUG === "true",
|
||||
logLevel: process.env.LOG_LEVEL || "info",
|
||||
defaultSubtasks: parseInt(process.env.DEFAULT_SUBTASKS || "3"),
|
||||
defaultPriority: process.env.DEFAULT_PRIORITY || "medium",
|
||||
projectName: process.env.PROJECT_NAME || "MCP SaaS MVP",
|
||||
projectVersion: process.env.PROJECT_VERSION || "1.0.0"
|
||||
};
|
||||
|
||||
// Set up logging based on log level
|
||||
const LOG_LEVELS = {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
warn: 2,
|
||||
error: 3
|
||||
};
|
||||
|
||||
function log(level, ...args) {
|
||||
if (LOG_LEVELS[level] >= LOG_LEVELS[CONFIG.logLevel]) {
|
||||
if (level === 'error') {
|
||||
console.error(...args);
|
||||
} else if (level === 'warn') {
|
||||
console.warn(...args);
|
||||
} else {
|
||||
console.log(...args);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional debug logging to file if debug mode is enabled
|
||||
if (CONFIG.debug && level === 'debug') {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logMessage = `${timestamp} [DEBUG] ${args.join(' ')}\n`;
|
||||
fs.appendFileSync('dev-debug.log', logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
});
|
||||
|
||||
function readJSON(filepath) {
|
||||
if (!fs.existsSync(filepath)) return null;
|
||||
const content = fs.readFileSync(filepath, 'utf8');
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
function writeJSON(filepath, data) {
|
||||
fs.writeFileSync(filepath, JSON.stringify(data, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
async function callClaude(prdContent, prdPath, numTasks) {
|
||||
log('info', `Starting Claude API call to process PRD from ${prdPath}...`);
|
||||
log('debug', `PRD content length: ${prdContent.length} characters`);
|
||||
|
||||
const TASKS_JSON_TEMPLATE = `
|
||||
{
|
||||
"meta": {
|
||||
"projectName": "${CONFIG.projectName}",
|
||||
"version": "${CONFIG.projectVersion}",
|
||||
"source": "${prdPath}",
|
||||
"description": "Tasks generated from ${prdPath.split('/').pop()}"
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Set up project scaffolding",
|
||||
"description": "Initialize repository structure with Wrangler configuration for Cloudflare Workers, set up D1 database schema, and configure development environment.",
|
||||
"status": "pending",
|
||||
"dependencies": [],
|
||||
"priority": "high",
|
||||
"details": "Create the initial project structure including:\n- Wrangler configuration for Cloudflare Workers\n- D1 database schema setup\n- Development environment configuration\n- Basic folder structure for the project",
|
||||
"testStrategy": "Verify that the project structure is set up correctly and that the development environment can be started without errors."
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Implement GitHub OAuth flow",
|
||||
"description": "Create authentication system using GitHub OAuth for user sign-up and login, storing authenticated user profiles in D1 database.",
|
||||
"status": "pending",
|
||||
"dependencies": [1],
|
||||
"priority": "${CONFIG.defaultPriority}",
|
||||
"details": "Implement the GitHub OAuth flow for user authentication:\n- Create OAuth application in GitHub\n- Implement OAuth callback endpoint\n- Store user profiles in D1 database\n- Create session management",
|
||||
"testStrategy": "Test the complete OAuth flow from login to callback to session creation. Verify user data is correctly stored in the database."
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
let systemPrompt = "You are a helpful assistant that generates tasks from a PRD using the following template: " + TASKS_JSON_TEMPLATE + "ONLY RETURN THE JSON, NOTHING ELSE.";
|
||||
|
||||
// Add instruction about the number of tasks if specified
|
||||
if (numTasks) {
|
||||
systemPrompt += ` Generate exactly ${numTasks} tasks.`;
|
||||
} else {
|
||||
systemPrompt += " Generate a comprehensive set of tasks that covers all requirements in the PRD.";
|
||||
}
|
||||
|
||||
log('debug', "System prompt:", systemPrompt);
|
||||
log('info', "Sending request to Claude API...");
|
||||
|
||||
const response = await anthropic.messages.create({
|
||||
max_tokens: CONFIG.maxTokens,
|
||||
model: CONFIG.model,
|
||||
temperature: CONFIG.temperature,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: prdContent
|
||||
}
|
||||
],
|
||||
system: systemPrompt
|
||||
});
|
||||
log('info', "Received response from Claude API!");
|
||||
|
||||
// Extract the text content from the response
|
||||
const textContent = response.content[0].text;
|
||||
log('debug', `Response length: ${textContent.length} characters`);
|
||||
|
||||
try {
|
||||
// Try to parse the response as JSON
|
||||
log('info', "Parsing response as JSON...");
|
||||
const parsedJson = JSON.parse(textContent);
|
||||
log('info', `Successfully parsed JSON with ${parsedJson.tasks?.length || 0} tasks`);
|
||||
return parsedJson;
|
||||
} catch (error) {
|
||||
log('error', "Failed to parse Claude's response as JSON:", error);
|
||||
log('debug', "Raw response:", textContent);
|
||||
throw new Error("Failed to parse Claude's response as JSON. See console for details.");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 1) parse-prd
|
||||
//
|
||||
async function parsePRD(prdPath, tasksPath, numTasks) {
|
||||
if (!fs.existsSync(prdPath)) {
|
||||
log('error', `PRD file not found: ${prdPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log('info', `Reading PRD file from: ${prdPath}`);
|
||||
const prdContent = fs.readFileSync(prdPath, 'utf8');
|
||||
log('info', `PRD file read successfully. Content length: ${prdContent.length} characters`);
|
||||
|
||||
// call claude to generate the tasks.json
|
||||
log('info', "Calling Claude to generate tasks from PRD...");
|
||||
const claudeResponse = await callClaude(prdContent, prdPath, numTasks);
|
||||
let tasks = claudeResponse.tasks || [];
|
||||
log('info', `Claude generated ${tasks.length} tasks from the PRD`);
|
||||
|
||||
// Limit the number of tasks if specified
|
||||
if (numTasks && numTasks > 0 && numTasks < tasks.length) {
|
||||
log('info', `Limiting to the first ${numTasks} tasks as specified`);
|
||||
tasks = tasks.slice(0, numTasks);
|
||||
}
|
||||
|
||||
log('info', "Creating tasks.json data structure...");
|
||||
const data = {
|
||||
meta: {
|
||||
projectName: CONFIG.projectName,
|
||||
version: CONFIG.projectVersion,
|
||||
source: prdPath,
|
||||
description: "Tasks generated from PRD",
|
||||
totalTasksGenerated: claudeResponse.tasks?.length || 0,
|
||||
tasksIncluded: tasks.length
|
||||
},
|
||||
tasks
|
||||
};
|
||||
|
||||
log('info', `Writing ${tasks.length} tasks to ${tasksPath}...`);
|
||||
writeJSON(tasksPath, data);
|
||||
log('info', `Parsed PRD from '${prdPath}' -> wrote ${tasks.length} tasks to '${tasksPath}'.`);
|
||||
}
|
||||
|
||||
//
|
||||
// 2) update
|
||||
//
|
||||
async function updateTasks(tasksPath, fromId, prompt) {
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "Invalid or missing tasks.json.");
|
||||
process.exit(1);
|
||||
}
|
||||
log('info', `Updating tasks from ID >= ${fromId} with prompt: ${prompt}`);
|
||||
|
||||
// In real usage, you'd feed data.tasks + prompt to an LLM. We'll just do a naive approach:
|
||||
data.tasks.forEach(task => {
|
||||
if (task.id >= fromId && task.status !== "done") {
|
||||
task.description += ` [UPDATED: ${prompt}]`;
|
||||
}
|
||||
});
|
||||
|
||||
writeJSON(tasksPath, data);
|
||||
log('info', "Tasks updated successfully.");
|
||||
}
|
||||
|
||||
//
|
||||
// 3) generate
|
||||
//
|
||||
function generateTaskFiles(tasksPath, outputDir) {
|
||||
log('info', `Reading tasks from ${tasksPath}...`);
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "No valid tasks to generate. Please run parse-prd first.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log('info', `Found ${data.tasks.length} tasks to generate files for.`);
|
||||
|
||||
// The outputDir is now the same directory as tasksPath, so we don't need to check if it exists
|
||||
// since we already did that in the main function
|
||||
|
||||
log('info', "Generating individual task files...");
|
||||
data.tasks.forEach(task => {
|
||||
const filename = `task_${String(task.id).padStart(3, '0')}.txt`;
|
||||
const filepath = path.join(outputDir, filename);
|
||||
|
||||
const content = [
|
||||
`# Task ID: ${task.id}`,
|
||||
`# Title: ${task.title}`,
|
||||
`# Status: ${task.status}`,
|
||||
`# Dependencies: ${task.dependencies.join(", ")}`,
|
||||
`# Priority: ${task.priority}`,
|
||||
`# Description: ${task.description}`,
|
||||
`# Details:\n${task.details}\n`,
|
||||
`# Test Strategy:`,
|
||||
`${task.testStrategy}\n`
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(filepath, content, 'utf8');
|
||||
log('info', `Generated: ${filename}`);
|
||||
});
|
||||
|
||||
log('info', `All ${data.tasks.length} tasks have been generated into '${outputDir}'.`);
|
||||
}
|
||||
|
||||
//
|
||||
// 4) set-status
|
||||
//
|
||||
function setTaskStatus(tasksPath, taskId, newStatus) {
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "No valid tasks found.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const task = data.tasks.find(t => t.id === taskId);
|
||||
if (!task) {
|
||||
log('error', `Task with ID=${taskId} not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const oldStatus = task.status;
|
||||
task.status = newStatus;
|
||||
writeJSON(tasksPath, data);
|
||||
log('info', `Task ID=${taskId} status changed from '${oldStatus}' to '${newStatus}'.`);
|
||||
}
|
||||
|
||||
//
|
||||
// 5) list tasks
|
||||
//
|
||||
function listTasks(tasksPath) {
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "No valid tasks found.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log('info', `Tasks in ${tasksPath}:`);
|
||||
data.tasks.forEach(t => {
|
||||
log('info', `- ID=${t.id}, [${t.status}] ${t.title}`);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// 6) expand task with subtasks
|
||||
//
|
||||
async function expandTask(tasksPath, taskId, numSubtasks, additionalContext = '') {
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "No valid tasks found.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Use default subtasks count from config if not specified
|
||||
numSubtasks = numSubtasks || CONFIG.defaultSubtasks;
|
||||
|
||||
const task = data.tasks.find(t => t.id === taskId);
|
||||
if (!task) {
|
||||
log('error', `Task with ID=${taskId} not found.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Skip tasks that are already completed
|
||||
if (task.status === 'done' || task.status === 'completed') {
|
||||
log('info', `Skipping task ID=${taskId} "${task.title}" - task is already marked as ${task.status}.`);
|
||||
log('info', `Use set-status command to change the status if you want to modify this task.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
log('info', `Expanding task: ${task.title}`);
|
||||
|
||||
// Initialize subtasks array if it doesn't exist
|
||||
if (!task.subtasks) {
|
||||
task.subtasks = [];
|
||||
}
|
||||
|
||||
// Calculate next subtask ID
|
||||
const nextSubtaskId = task.subtasks.length > 0
|
||||
? Math.max(...task.subtasks.map(st => st.id)) + 1
|
||||
: 1;
|
||||
|
||||
// Generate subtasks using Claude
|
||||
const subtasks = await generateSubtasks(task, numSubtasks, nextSubtaskId, additionalContext);
|
||||
|
||||
// Add new subtasks to the task
|
||||
task.subtasks = [...task.subtasks, ...subtasks];
|
||||
|
||||
// Update tasks.json
|
||||
writeJSON(tasksPath, data);
|
||||
log('info', `Added ${subtasks.length} subtasks to task ID=${taskId}.`);
|
||||
|
||||
// Print the new subtasks
|
||||
log('info', "New subtasks:");
|
||||
subtasks.forEach(st => {
|
||||
log('info', `- ${st.id}. ${st.title}`);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Expand all tasks with subtasks
|
||||
//
|
||||
async function expandAllTasks(tasksPath, numSubtasks, additionalContext = '', forceRegenerate = false) {
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
log('error', "No valid tasks found.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log('info', `Expanding all ${data.tasks.length} tasks with subtasks...`);
|
||||
|
||||
let tasksExpanded = 0;
|
||||
let tasksSkipped = 0;
|
||||
let tasksCompleted = 0;
|
||||
|
||||
// Process each task sequentially to avoid overwhelming the API
|
||||
for (const task of data.tasks) {
|
||||
// Skip tasks that are already completed
|
||||
if (task.status === 'done' || task.status === 'completed') {
|
||||
log('info', `Skipping task ID=${task.id} "${task.title}" - task is already marked as ${task.status}.`);
|
||||
tasksCompleted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip tasks that already have subtasks unless force regeneration is enabled
|
||||
if (!forceRegenerate && task.subtasks && task.subtasks.length > 0) {
|
||||
log('info', `Skipping task ID=${task.id} "${task.title}" - already has ${task.subtasks.length} subtasks`);
|
||||
tasksSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const success = await expandTask(tasksPath, task.id, numSubtasks, additionalContext);
|
||||
if (success) {
|
||||
tasksExpanded++;
|
||||
}
|
||||
}
|
||||
|
||||
log('info', `Expansion complete: ${tasksExpanded} tasks expanded, ${tasksSkipped} tasks skipped (already had subtasks), ${tasksCompleted} tasks skipped (already completed).`);
|
||||
|
||||
if (tasksSkipped > 0) {
|
||||
log('info', `Tip: Use --force flag to regenerate subtasks for all tasks, including those that already have subtasks.`);
|
||||
}
|
||||
|
||||
if (tasksCompleted > 0) {
|
||||
log('info', `Note: Completed tasks are always skipped. Use set-status command to change task status if needed.`);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Generate subtasks using Claude
|
||||
//
|
||||
async function generateSubtasks(task, numSubtasks, nextSubtaskId, additionalContext = '') {
|
||||
log('info', `Generating ${numSubtasks} subtasks for task: ${task.title}`);
|
||||
|
||||
const existingSubtasksText = task.subtasks && task.subtasks.length > 0
|
||||
? `\nExisting subtasks:\n${task.subtasks.map(st => `${st.id}. ${st.title}: ${st.description}`).join('\n')}`
|
||||
: '';
|
||||
|
||||
const prompt = `
|
||||
Task Title: ${task.title}
|
||||
Task Description: ${task.description}
|
||||
Task Details: ${task.details || ''}
|
||||
${existingSubtasksText}
|
||||
${additionalContext ? `\nAdditional Context: ${additionalContext}` : ''}
|
||||
|
||||
Please generate ${numSubtasks} detailed subtasks for this task. Each subtask should be specific, actionable, and help accomplish the main task. The subtasks should cover different aspects of the main task and provide clear guidance on implementation.
|
||||
|
||||
For each subtask, provide:
|
||||
1. A concise title
|
||||
2. A detailed description
|
||||
3. Dependencies (if any)
|
||||
4. Acceptance criteria
|
||||
|
||||
Format each subtask as follows:
|
||||
|
||||
Subtask ${nextSubtaskId}: [Title]
|
||||
Description: [Detailed description]
|
||||
Dependencies: [List any dependencies by ID, or "None" if there are no dependencies]
|
||||
Acceptance Criteria: [List specific criteria that must be met for this subtask to be considered complete]
|
||||
|
||||
Then continue with Subtask ${nextSubtaskId + 1}, and so on.
|
||||
`;
|
||||
|
||||
log('info', "Calling Claude to generate subtasks...");
|
||||
|
||||
const response = await anthropic.messages.create({
|
||||
max_tokens: CONFIG.maxTokens,
|
||||
model: CONFIG.model,
|
||||
temperature: CONFIG.temperature,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: prompt
|
||||
}
|
||||
],
|
||||
system: "You are a helpful assistant that generates detailed subtasks for software development tasks. Your subtasks should be specific, actionable, and help accomplish the main task. Format each subtask with a title, description, dependencies, and acceptance criteria."
|
||||
});
|
||||
|
||||
log('info', "Received response from Claude API!");
|
||||
|
||||
// Extract the text content from the response
|
||||
const textContent = response.content[0].text;
|
||||
|
||||
// Log the first part of the response for debugging
|
||||
log('debug', "Response preview:", textContent.substring(0, 200) + "...");
|
||||
|
||||
// Parse the subtasks from the text response
|
||||
const subtasks = parseSubtasksFromText(textContent, nextSubtaskId, numSubtasks);
|
||||
|
||||
return subtasks;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse subtasks from Claude's text response
|
||||
//
|
||||
function parseSubtasksFromText(text, startId, expectedCount) {
|
||||
log('info', "Parsing subtasks from Claude's response...");
|
||||
|
||||
const subtasks = [];
|
||||
|
||||
// Try to extract subtasks using regex patterns
|
||||
// Looking for patterns like "Subtask 1: Title" or "Subtask 1 - Title"
|
||||
const subtaskRegex = /Subtask\s+(\d+)(?::|-)?\s+([^\n]+)(?:\n|$)(?:Description:?\s*)?([^]*?)(?:(?:\n|^)Dependencies:?\s*([^]*?))?(?:(?:\n|^)Acceptance Criteria:?\s*([^]*?))?(?=(?:\n\s*Subtask\s+\d+|$))/gi;
|
||||
|
||||
let match;
|
||||
while ((match = subtaskRegex.exec(text)) !== null) {
|
||||
const [_, idStr, title, descriptionRaw, dependenciesRaw, acceptanceCriteriaRaw] = match;
|
||||
|
||||
// Clean up the description
|
||||
let description = descriptionRaw ? descriptionRaw.trim() : '';
|
||||
|
||||
// Extract dependencies
|
||||
let dependencies = [];
|
||||
if (dependenciesRaw) {
|
||||
const depText = dependenciesRaw.trim();
|
||||
if (depText && !depText.toLowerCase().includes('none')) {
|
||||
// Extract numbers from dependencies text
|
||||
const depNumbers = depText.match(/\d+/g);
|
||||
if (depNumbers) {
|
||||
dependencies = depNumbers.map(n => parseInt(n, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract acceptance criteria
|
||||
let acceptanceCriteria = acceptanceCriteriaRaw ? acceptanceCriteriaRaw.trim() : '';
|
||||
|
||||
// Create the subtask object
|
||||
const subtask = {
|
||||
id: startId + subtasks.length,
|
||||
title: title.trim(),
|
||||
description: description,
|
||||
status: "pending",
|
||||
dependencies: dependencies,
|
||||
acceptanceCriteria: acceptanceCriteria
|
||||
};
|
||||
|
||||
subtasks.push(subtask);
|
||||
|
||||
// Break if we've found the expected number of subtasks
|
||||
if (subtasks.length >= expectedCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If regex parsing failed or didn't find enough subtasks, try a different approach
|
||||
if (subtasks.length < expectedCount) {
|
||||
log('info', `Regex parsing found only ${subtasks.length} subtasks, trying alternative parsing...`);
|
||||
|
||||
// Split by "Subtask X" headers
|
||||
const subtaskSections = text.split(/\n\s*Subtask\s+\d+/i);
|
||||
|
||||
// Skip the first section (before the first "Subtask X" header)
|
||||
for (let i = 1; i < subtaskSections.length && subtasks.length < expectedCount; i++) {
|
||||
const section = subtaskSections[i];
|
||||
|
||||
// Extract title
|
||||
const titleMatch = section.match(/^(?::|-)?\s*([^\n]+)/);
|
||||
const title = titleMatch ? titleMatch[1].trim() : `Subtask ${startId + subtasks.length}`;
|
||||
|
||||
// Extract description
|
||||
let description = '';
|
||||
const descMatch = section.match(/Description:?\s*([^]*?)(?:Dependencies|Acceptance Criteria|$)/i);
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim();
|
||||
} else {
|
||||
// If no "Description:" label, use everything until Dependencies or Acceptance Criteria
|
||||
const contentMatch = section.match(/^(?::|-)?\s*[^\n]+\n([^]*?)(?:Dependencies|Acceptance Criteria|$)/i);
|
||||
if (contentMatch) {
|
||||
description = contentMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract dependencies
|
||||
let dependencies = [];
|
||||
const depMatch = section.match(/Dependencies:?\s*([^]*?)(?:Acceptance Criteria|$)/i);
|
||||
if (depMatch) {
|
||||
const depText = depMatch[1].trim();
|
||||
if (depText && !depText.toLowerCase().includes('none')) {
|
||||
const depNumbers = depText.match(/\d+/g);
|
||||
if (depNumbers) {
|
||||
dependencies = depNumbers.map(n => parseInt(n, 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract acceptance criteria
|
||||
let acceptanceCriteria = '';
|
||||
const acMatch = section.match(/Acceptance Criteria:?\s*([^]*?)$/i);
|
||||
if (acMatch) {
|
||||
acceptanceCriteria = acMatch[1].trim();
|
||||
}
|
||||
|
||||
// Create the subtask object
|
||||
const subtask = {
|
||||
id: startId + subtasks.length,
|
||||
title: title,
|
||||
description: description,
|
||||
status: "pending",
|
||||
dependencies: dependencies,
|
||||
acceptanceCriteria: acceptanceCriteria
|
||||
};
|
||||
|
||||
subtasks.push(subtask);
|
||||
}
|
||||
}
|
||||
|
||||
// If we still don't have enough subtasks, create generic ones
|
||||
if (subtasks.length < expectedCount) {
|
||||
log('info', `Parsing found only ${subtasks.length} subtasks, creating generic ones to reach ${expectedCount}...`);
|
||||
|
||||
for (let i = subtasks.length; i < expectedCount; i++) {
|
||||
subtasks.push({
|
||||
id: startId + i,
|
||||
title: `Subtask ${startId + i}`,
|
||||
description: "Auto-generated subtask. Please update with specific details.",
|
||||
status: "pending",
|
||||
dependencies: [],
|
||||
acceptanceCriteria: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
log('info', `Successfully parsed ${subtasks.length} subtasks.`);
|
||||
return subtasks;
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Main CLI
|
||||
// ------------------------------------------
|
||||
(async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
const outputDir = path.resolve(process.cwd(), 'tasks');
|
||||
// Update tasksPath to be inside the tasks directory
|
||||
const tasksPath = path.resolve(outputDir, 'tasks.json');
|
||||
|
||||
const inputArg = (args.find(a => a.startsWith('--input=')) || '').split('=')[1] || 'sample-prd.txt';
|
||||
const fromArg = (args.find(a => a.startsWith('--from=')) || '').split('=')[1];
|
||||
const promptArg = (args.find(a => a.startsWith('--prompt=')) || '').split('=')[1] || '';
|
||||
const idArg = (args.find(a => a.startsWith('--id=')) || '').split('=')[1];
|
||||
const statusArg = (args.find(a => a.startsWith('--status=')) || '').split('=')[1] || '';
|
||||
const tasksCountArg = (args.find(a => a.startsWith('--tasks=')) || '').split('=')[1];
|
||||
const numTasks = tasksCountArg ? parseInt(tasksCountArg, 10) : undefined;
|
||||
const subtasksArg = (args.find(a => a.startsWith('--subtasks=')) || '').split('=')[1];
|
||||
const numSubtasks = subtasksArg ? parseInt(subtasksArg, 10) : 3; // Default to 3 subtasks if not specified
|
||||
const forceFlag = args.includes('--force'); // Check if --force flag is present
|
||||
|
||||
log('info', `Executing command: ${command}`);
|
||||
|
||||
// Make sure the tasks directory exists
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
log('info', `Creating tasks directory: ${outputDir}`);
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case 'parse-prd':
|
||||
log('info', `Parsing PRD from ${inputArg} to generate tasks.json...`);
|
||||
if (numTasks) {
|
||||
log('info', `Limiting to ${numTasks} tasks as specified`);
|
||||
}
|
||||
await parsePRD(inputArg, tasksPath, numTasks);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
if (!fromArg) {
|
||||
log('error', "Please specify --from=<id>. e.g. node dev.js update --from=3 --prompt='Changes...'");
|
||||
process.exit(1);
|
||||
}
|
||||
log('info', `Updating tasks from ID ${fromArg} based on prompt...`);
|
||||
await updateTasks(tasksPath, parseInt(fromArg, 10), promptArg);
|
||||
break;
|
||||
|
||||
case 'generate':
|
||||
log('info', `Generating individual task files from ${tasksPath} to ${outputDir}...`);
|
||||
generateTaskFiles(tasksPath, outputDir);
|
||||
break;
|
||||
|
||||
case 'set-status':
|
||||
if (!idArg) {
|
||||
log('error', "Missing --id=<taskId> argument.");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!statusArg) {
|
||||
log('error', "Missing --status=<newStatus> argument (e.g. done, pending, deferred).");
|
||||
process.exit(1);
|
||||
}
|
||||
log('info', `Setting task ${idArg} status to "${statusArg}"...`);
|
||||
setTaskStatus(tasksPath, parseInt(idArg, 10), statusArg);
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
log('info', `Listing tasks from ${tasksPath}...`);
|
||||
listTasks(tasksPath);
|
||||
break;
|
||||
|
||||
case 'expand':
|
||||
if (args.includes('--all')) {
|
||||
// Expand all tasks
|
||||
log('info', `Expanding all tasks with ${numSubtasks} subtasks each...`);
|
||||
await expandAllTasks(tasksPath, numSubtasks, promptArg, forceFlag);
|
||||
} else if (idArg) {
|
||||
// Expand a specific task
|
||||
log('info', `Expanding task ${idArg} with ${numSubtasks} subtasks...`);
|
||||
await expandTask(tasksPath, parseInt(idArg, 10), numSubtasks, promptArg);
|
||||
} else {
|
||||
log('error', "Error: Please specify a task ID with --id=<id> or use --all to expand all tasks.");
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log('info', `
|
||||
Dev.js - Task Management Script
|
||||
|
||||
Subcommands:
|
||||
1) parse-prd --input=some-prd.txt [--tasks=10]
|
||||
-> Creates/overwrites tasks.json with a set of tasks.
|
||||
-> Optional --tasks parameter limits the number of tasks generated.
|
||||
|
||||
2) update --from=5 --prompt="We changed from Slack to Discord."
|
||||
-> Regenerates tasks from ID >= 5 using the provided prompt.
|
||||
|
||||
3) generate
|
||||
-> Generates per-task files (e.g., task_001.txt) from tasks.json
|
||||
|
||||
4) set-status --id=4 --status=done
|
||||
-> Updates a single task's status to done (or pending, deferred, etc.).
|
||||
|
||||
5) list
|
||||
-> Lists tasks in a brief console view (ID, title, status).
|
||||
|
||||
6) expand --id=3 --subtasks=5 [--prompt="Additional context"]
|
||||
-> Expands a task with subtasks for more detailed implementation.
|
||||
-> Use --all instead of --id to expand all tasks.
|
||||
-> Optional --subtasks parameter controls number of subtasks (default: 3).
|
||||
-> Add --force when using --all to regenerate subtasks for tasks that already have them.
|
||||
-> Note: Tasks marked as 'done' or 'completed' are always skipped.
|
||||
|
||||
Usage examples:
|
||||
node dev.js parse-prd --input=scripts/prd.txt
|
||||
node dev.js parse-prd --input=scripts/prd.txt --tasks=10
|
||||
node dev.js update --from=4 --prompt="Refactor tasks from ID 4 onward"
|
||||
node dev.js generate
|
||||
node dev.js set-status --id=3 --status=done
|
||||
node dev.js list
|
||||
node dev.js expand --id=3 --subtasks=5
|
||||
node dev.js expand --all
|
||||
node dev.js expand --all --force
|
||||
`);
|
||||
break;
|
||||
}
|
||||
})().catch(err => {
|
||||
log('error', err);
|
||||
process.exit(1);
|
||||
});
|
||||
267
templates/dev_workflow.mdc
Normal file
267
templates/dev_workflow.mdc
Normal file
@@ -0,0 +1,267 @@
|
||||
---
|
||||
description: guide the Cursor Agent in using the meta-development script (scripts/dev.js). It also defines the overall workflow for reading, updating, and generating tasks during AI-driven development.
|
||||
globs: scripts/dev.js, tasks.json, tasks/*.txt
|
||||
alwaysApply: true
|
||||
---
|
||||
rules:
|
||||
- name: "Meta Development Workflow for Cursor Agent"
|
||||
description: >
|
||||
Provides comprehensive guidelines on how the agent (Cursor) should coordinate
|
||||
with the meta task script in scripts/dev.js. The agent will call
|
||||
these commands at various points in the coding process to keep
|
||||
tasks.json up to date and maintain a single source of truth for development tasks.
|
||||
triggers:
|
||||
# Potential triggers or states in Cursor where these rules apply.
|
||||
# You may list relevant event names, e.g., "onTaskCompletion" or "onUserCommand"
|
||||
- always
|
||||
steps:
|
||||
- "**Initial Setup**: If starting a new project with a PRD document, run `node scripts/dev.js parse-prd --input=<prd-file.txt>` to generate the initial tasks.json file. This will create a structured task list with IDs, titles, descriptions, dependencies, priorities, and test strategies."
|
||||
|
||||
- "**Task Discovery**: When a coding session begins, call `node scripts/dev.js list` to see the current tasks, their status, and IDs. This provides a quick overview of all tasks and their current states (pending, done, deferred)."
|
||||
|
||||
- "**Task Selection**: Select the next pending task based on these criteria:
|
||||
1. Dependencies: Only select tasks whose dependencies are marked as 'done'
|
||||
2. Priority: Choose higher priority tasks first ('high' > 'medium' > 'low')
|
||||
3. ID order: When priorities are equal, select the task with the lowest ID
|
||||
If multiple tasks are eligible, present options to the user for selection."
|
||||
|
||||
- "**Task Clarification**: If a task description is unclear or lacks detail:
|
||||
1. Check if a corresponding task file exists in the tasks/ directory (e.g., task_001.txt)
|
||||
2. If more information is needed, ask the user for clarification
|
||||
3. If architectural changes have occurred, run `node scripts/dev.js update --from=<id> --prompt=\"<new architectural context>\"` to update the task and all subsequent tasks"
|
||||
|
||||
- "**Task Breakdown**: For complex tasks that need to be broken down into smaller steps:
|
||||
1. Use `node scripts/dev.js expand --id=<id> --subtasks=<number>` to generate detailed subtasks
|
||||
2. Optionally provide additional context with `--prompt=\"<context>\"` to guide subtask generation
|
||||
3. Review the generated subtasks and adjust if necessary
|
||||
4. For multiple tasks, use `--all` flag to expand all pending tasks that don't have subtasks"
|
||||
|
||||
- "**Task Implementation**: Implement the code necessary for the chosen task. Follow these guidelines:
|
||||
1. Reference the task's 'details' section for implementation specifics
|
||||
2. Consider dependencies on previous tasks when implementing
|
||||
3. Follow the project's coding standards and patterns
|
||||
4. Create appropriate tests based on the task's 'testStrategy' field"
|
||||
|
||||
- "**Task Verification**: Before marking a task as done, verify it according to:
|
||||
1. The task's specified 'testStrategy'
|
||||
2. Any automated tests in the codebase
|
||||
3. Manual verification if required
|
||||
4. Code quality standards (linting, formatting, etc.)"
|
||||
|
||||
- "**Task Completion**: When a task is completed and verified, run `node scripts/dev.js set-status --id=<id> --status=done` to mark it as done in tasks.json. This ensures the task tracking remains accurate."
|
||||
|
||||
- "**Implementation Drift Handling**: If during implementation, you discover that:
|
||||
1. The current approach differs significantly from what was planned
|
||||
2. Future tasks need to be modified due to current implementation choices
|
||||
3. New dependencies or requirements have emerged
|
||||
|
||||
Then call `node scripts/dev.js update --from=<futureTaskId> --prompt=\"Detailed explanation of architectural or implementation changes...\"` to rewrite or re-scope subsequent tasks in tasks.json."
|
||||
|
||||
- "**Task File Generation**: After any updates to tasks.json (status changes, task updates), run `node scripts/dev.js generate` to regenerate the individual task_XXX.txt files in the tasks/ folder. This ensures that task files are always in sync with tasks.json."
|
||||
|
||||
- "**Task Status Management**: Use appropriate status values when updating tasks:
|
||||
1. 'pending': Tasks that are ready to be worked on
|
||||
2. 'done': Tasks that have been completed and verified
|
||||
3. 'deferred': Tasks that have been postponed to a later time
|
||||
4. Any other custom status that might be relevant to the project"
|
||||
|
||||
- "**Dependency Management**: When selecting tasks, always respect the dependency chain:
|
||||
1. Never start a task whose dependencies are not marked as 'done'
|
||||
2. If a dependency task is deferred, consider whether dependent tasks should also be deferred
|
||||
3. If dependency relationships change during development, update tasks.json accordingly"
|
||||
|
||||
- "**Progress Reporting**: Periodically (at the beginning of sessions or after completing significant tasks), run `node scripts/dev.js list` to provide the user with an updated view of project progress."
|
||||
|
||||
- "**Task File Format**: When reading task files, understand they follow this structure:
|
||||
```
|
||||
# Task ID: <id>
|
||||
# Title: <title>
|
||||
# Status: <status>
|
||||
# Dependencies: <comma-separated list of dependency IDs>
|
||||
# Priority: <priority>
|
||||
# Description: <brief description>
|
||||
# Details:
|
||||
<detailed implementation notes>
|
||||
|
||||
# Test Strategy:
|
||||
<verification approach>
|
||||
```"
|
||||
|
||||
- "**Continuous Workflow**: Repeat this process until all tasks relevant to the current development phase are completed. Always maintain tasks.json as the single source of truth for development progress."
|
||||
|
||||
- name: "Meta-Development Script Command Reference"
|
||||
description: >
|
||||
Detailed reference for all commands available in the scripts/dev.js meta-development script.
|
||||
This helps the agent understand the full capabilities of the script and use it effectively.
|
||||
triggers:
|
||||
- always
|
||||
commands:
|
||||
- name: "parse-prd"
|
||||
syntax: "node scripts/dev.js parse-prd --input=<prd-file.txt>"
|
||||
description: "Parses a PRD document and generates a tasks.json file with structured tasks. This initializes the task tracking system."
|
||||
parameters:
|
||||
- "--input=<file>: Path to the PRD text file (default: sample-prd.txt)"
|
||||
example: "node scripts/dev.js parse-prd --input=requirements.txt"
|
||||
notes: "This will overwrite any existing tasks.json file. Use with caution on established projects."
|
||||
|
||||
- name: "update"
|
||||
syntax: "node scripts/dev.js update --from=<id> --prompt=\"<prompt>\""
|
||||
description: "Updates tasks with ID >= the specified ID based on the provided prompt. Useful for handling implementation drift or architectural changes."
|
||||
parameters:
|
||||
- "--from=<id>: The task ID from which to start updating (required)"
|
||||
- "--prompt=\"<text>\": The prompt explaining the changes or new context (required)"
|
||||
example: "node scripts/dev.js update --from=4 --prompt=\"Now we are using Express instead of Fastify.\""
|
||||
notes: "Only updates tasks that aren't marked as 'done'. Completed tasks remain unchanged."
|
||||
|
||||
- name: "generate"
|
||||
syntax: "node scripts/dev.js generate"
|
||||
description: "Generates individual task files in the tasks/ directory based on the current state of tasks.json."
|
||||
parameters: "None"
|
||||
example: "node scripts/dev.js generate"
|
||||
notes: "Overwrites existing task files. Creates the tasks/ directory if it doesn't exist."
|
||||
|
||||
- name: "set-status"
|
||||
syntax: "node scripts/dev.js set-status --id=<id> --status=<status>"
|
||||
description: "Updates the status of a specific task in tasks.json."
|
||||
parameters:
|
||||
- "--id=<id>: The ID of the task to update (required)"
|
||||
- "--status=<status>: The new status (e.g., 'done', 'pending', 'deferred') (required)"
|
||||
example: "node scripts/dev.js set-status --id=3 --status=done"
|
||||
notes: "Common status values are 'done', 'pending', and 'deferred', but any string is accepted."
|
||||
|
||||
- name: "list"
|
||||
syntax: "node scripts/dev.js list"
|
||||
description: "Lists all tasks in tasks.json with their IDs, titles, and current status."
|
||||
parameters: "None"
|
||||
example: "node scripts/dev.js list"
|
||||
notes: "Provides a quick overview of project progress. Use this at the start of coding sessions."
|
||||
|
||||
- name: "expand"
|
||||
syntax: "node scripts/dev.js expand --id=<id> [--subtasks=<number>] [--prompt=\"<context>\"]"
|
||||
description: "Expands a task with subtasks for more detailed implementation. Can also expand all tasks with the --all flag."
|
||||
parameters:
|
||||
- "--id=<id>: The ID of the task to expand (required unless using --all)"
|
||||
- "--all: Expand all pending tasks that don't have subtasks"
|
||||
- "--subtasks=<number>: Number of subtasks to generate (default: 3)"
|
||||
- "--prompt=\"<text>\": Additional context to guide subtask generation"
|
||||
- "--force: When used with --all, regenerates subtasks even for tasks that already have them"
|
||||
example: "node scripts/dev.js expand --id=3 --subtasks=5 --prompt=\"Focus on security aspects\""
|
||||
notes: "Tasks marked as 'done' or 'completed' are always skipped. By default, tasks that already have subtasks are skipped unless --force is used."
|
||||
|
||||
- name: "Task Structure Reference"
|
||||
description: >
|
||||
Details the structure of tasks in tasks.json to help the agent understand
|
||||
and work with the task data effectively.
|
||||
triggers:
|
||||
- always
|
||||
task_fields:
|
||||
- name: "id"
|
||||
type: "number"
|
||||
description: "Unique identifier for the task. Used in commands and for tracking dependencies."
|
||||
example: "1"
|
||||
|
||||
- name: "title"
|
||||
type: "string"
|
||||
description: "Brief, descriptive title of the task."
|
||||
example: "Initialize Repo"
|
||||
|
||||
- name: "description"
|
||||
type: "string"
|
||||
description: "Concise description of what the task involves."
|
||||
example: "Create a new repository, set up initial structure."
|
||||
|
||||
- name: "status"
|
||||
type: "string"
|
||||
description: "Current state of the task. Common values: 'pending', 'done', 'deferred'."
|
||||
example: "pending"
|
||||
|
||||
- name: "dependencies"
|
||||
type: "array of numbers"
|
||||
description: "IDs of tasks that must be completed before this task can be started."
|
||||
example: "[1, 2]"
|
||||
|
||||
- name: "priority"
|
||||
type: "string"
|
||||
description: "Importance level of the task. Common values: 'high', 'medium', 'low'."
|
||||
example: "high"
|
||||
|
||||
- name: "details"
|
||||
type: "string"
|
||||
description: "In-depth instructions, references, or context for implementing the task."
|
||||
example: "Use GitHub client ID/secret, handle callback, set session token."
|
||||
|
||||
- name: "testStrategy"
|
||||
type: "string"
|
||||
description: "Approach for verifying the task has been completed correctly."
|
||||
example: "Deploy and call endpoint to confirm 'Hello World' response."
|
||||
|
||||
- name: "subtasks"
|
||||
type: "array of objects"
|
||||
description: "List of smaller, more specific tasks that make up the main task."
|
||||
example: "[{\"id\": 1, \"title\": \"Configure OAuth\", \"description\": \"...\", \"status\": \"pending\", \"dependencies\": [], \"acceptanceCriteria\": \"...\"}]"
|
||||
|
||||
- name: "Environment Variables Reference"
|
||||
description: >
|
||||
Details the environment variables that can be used to configure the dev.js script.
|
||||
These variables should be set in a .env file at the root of the project.
|
||||
triggers:
|
||||
- always
|
||||
variables:
|
||||
- name: "ANTHROPIC_API_KEY"
|
||||
required: true
|
||||
description: "Your Anthropic API key for Claude. Required for task generation and expansion."
|
||||
example: "ANTHROPIC_API_KEY=sk-ant-api03-..."
|
||||
|
||||
- name: "MODEL"
|
||||
required: false
|
||||
default: "claude-3-7-sonnet-20250219"
|
||||
description: "Specify which Claude model to use for task generation and expansion."
|
||||
example: "MODEL=claude-3-opus-20240229"
|
||||
|
||||
- name: "MAX_TOKENS"
|
||||
required: false
|
||||
default: "4000"
|
||||
description: "Maximum tokens for model responses. Higher values allow for more detailed task generation."
|
||||
example: "MAX_TOKENS=8000"
|
||||
|
||||
- name: "TEMPERATURE"
|
||||
required: false
|
||||
default: "0.7"
|
||||
description: "Temperature for model responses. Higher values (0.0-1.0) increase creativity but may reduce consistency."
|
||||
example: "TEMPERATURE=0.5"
|
||||
|
||||
- name: "DEBUG"
|
||||
required: false
|
||||
default: "false"
|
||||
description: "Enable debug logging. When true, detailed logs are written to dev-debug.log."
|
||||
example: "DEBUG=true"
|
||||
|
||||
- name: "LOG_LEVEL"
|
||||
required: false
|
||||
default: "info"
|
||||
description: "Log level for console output. Options: debug, info, warn, error."
|
||||
example: "LOG_LEVEL=debug"
|
||||
|
||||
- name: "DEFAULT_SUBTASKS"
|
||||
required: false
|
||||
default: "3"
|
||||
description: "Default number of subtasks when expanding a task."
|
||||
example: "DEFAULT_SUBTASKS=5"
|
||||
|
||||
- name: "DEFAULT_PRIORITY"
|
||||
required: false
|
||||
default: "medium"
|
||||
description: "Default priority for generated tasks. Options: high, medium, low."
|
||||
example: "DEFAULT_PRIORITY=high"
|
||||
|
||||
- name: "PROJECT_NAME"
|
||||
required: false
|
||||
default: "MCP SaaS MVP"
|
||||
description: "Override default project name in tasks.json metadata."
|
||||
example: "PROJECT_NAME=My Awesome Project"
|
||||
|
||||
- name: "PROJECT_VERSION"
|
||||
required: false
|
||||
default: "1.0.0"
|
||||
description: "Override default version in tasks.json metadata."
|
||||
example: "PROJECT_VERSION=2.1.0"
|
||||
13
templates/env.example
Normal file
13
templates/env.example
Normal file
@@ -0,0 +1,13 @@
|
||||
# Required
|
||||
ANTHROPIC_API_KEY=your-api-key-here
|
||||
|
||||
# Optional - defaults shown
|
||||
MODEL=claude-3-7-sonnet-20250219
|
||||
MAX_TOKENS=4000
|
||||
TEMPERATURE=0.7
|
||||
DEBUG=false
|
||||
LOG_LEVEL=info
|
||||
DEFAULT_SUBTASKS=3
|
||||
DEFAULT_PRIORITY=medium
|
||||
PROJECT_NAME={{projectName}}
|
||||
PROJECT_VERSION={{projectVersion}}
|
||||
47
templates/example_prd.txt
Normal file
47
templates/example_prd.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
<context>
|
||||
# Overview
|
||||
[Provide a high-level overview of your product here. Explain what problem it solves, who it's for, and why it's valuable.]
|
||||
|
||||
# Core Features
|
||||
[List and describe the main features of your product. For each feature, include:
|
||||
- What it does
|
||||
- Why it's important
|
||||
- How it works at a high level]
|
||||
|
||||
# User Experience
|
||||
[Describe the user journey and experience. Include:
|
||||
- User personas
|
||||
- Key user flows
|
||||
- UI/UX considerations]
|
||||
|
||||
# Technical Architecture
|
||||
[Outline the technical implementation details:
|
||||
- System components
|
||||
- Data models
|
||||
- APIs and integrations
|
||||
- Infrastructure requirements]
|
||||
|
||||
# Development Roadmap
|
||||
[Break down the development process into phases:
|
||||
- MVP requirements
|
||||
- Future enhancements
|
||||
- Timeline estimates]
|
||||
|
||||
# Success Metrics
|
||||
[Define how success will be measured:
|
||||
- Key performance indicators
|
||||
- User adoption metrics
|
||||
- Business goals]
|
||||
|
||||
# Risks and Mitigations
|
||||
[Identify potential risks and how they'll be addressed:
|
||||
- Technical challenges
|
||||
- Market risks
|
||||
- Resource constraints]
|
||||
|
||||
# Appendix
|
||||
[Include any additional information:
|
||||
- Research findings
|
||||
- Competitive analysis
|
||||
- Technical specifications]
|
||||
</context>
|
||||
29
templates/gitignore
Normal file
29
templates/gitignore
Normal file
@@ -0,0 +1,29 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
dev-debug.log
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# OS specific
|
||||
.DS_Store
|
||||
|
||||
# Task files
|
||||
tasks.json
|
||||
tasks/
|
||||
93
templates/scripts_README.md
Normal file
93
templates/scripts_README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Meta-Development Script
|
||||
|
||||
This folder contains a **meta-development script** (`dev.js`) and related utilities that manage tasks for an AI-driven or traditional software development workflow. The script revolves around a `tasks.json` file, which holds an up-to-date list of development tasks.
|
||||
|
||||
## Overview
|
||||
|
||||
In an AI-driven development process—particularly with tools like [Cursor](https://www.cursor.so/)—it's beneficial to have a **single source of truth** for tasks. This script allows you to:
|
||||
|
||||
1. **Parse** a PRD or requirements document (`.txt`) to initialize a set of tasks (`tasks.json`).
|
||||
2. **List** all existing tasks (IDs, statuses, titles).
|
||||
3. **Update** tasks to accommodate new prompts or architecture changes (useful if you discover "implementation drift").
|
||||
4. **Generate** individual task files (e.g., `task_001.txt`) for easy reference or to feed into an AI coding workflow.
|
||||
5. **Set task status**—mark tasks as `done`, `pending`, or `deferred` based on progress.
|
||||
6. **Expand** tasks with subtasks—break down complex tasks into smaller, more manageable subtasks.
|
||||
|
||||
## Configuration
|
||||
|
||||
The script can be configured through environment variables in a `.env` file at the root of the project:
|
||||
|
||||
### Required Configuration
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key for Claude
|
||||
|
||||
### Optional Configuration
|
||||
- `MODEL`: Specify which Claude model to use (default: "claude-3-7-sonnet-20250219")
|
||||
- `MAX_TOKENS`: Maximum tokens for model responses (default: 4000)
|
||||
- `TEMPERATURE`: Temperature for model responses (default: 0.7)
|
||||
- `DEBUG`: Enable debug logging (default: false)
|
||||
- `LOG_LEVEL`: Log level - debug, info, warn, error (default: info)
|
||||
- `DEFAULT_SUBTASKS`: Default number of subtasks when expanding (default: 3)
|
||||
- `DEFAULT_PRIORITY`: Default priority for generated tasks (default: medium)
|
||||
- `PROJECT_NAME`: Override default project name in tasks.json
|
||||
- `PROJECT_VERSION`: Override default version in tasks.json
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **`tasks.json`**:
|
||||
- A JSON file at the project root containing an array of tasks (each with `id`, `title`, `description`, `status`, etc.).
|
||||
- The `meta` field can store additional info like the project's name, version, or reference to the PRD.
|
||||
- Tasks can have `subtasks` for more detailed implementation steps.
|
||||
|
||||
2. **Script Commands**
|
||||
You can run the script via:
|
||||
|
||||
```bash
|
||||
node scripts/dev.js [command] [options]
|
||||
```
|
||||
|
||||
Available commands:
|
||||
|
||||
- `parse-prd`: Generate tasks from a PRD document
|
||||
- `list`: Display all tasks with their status
|
||||
- `update`: Update tasks based on new information
|
||||
- `generate`: Create individual task files
|
||||
- `set-status`: Change a task's status
|
||||
- `expand`: Add subtasks to a task or all tasks
|
||||
|
||||
Run `node scripts/dev.js` without arguments to see detailed usage information.
|
||||
|
||||
## Expanding Tasks
|
||||
|
||||
The `expand` command allows you to break down tasks into subtasks for more detailed implementation:
|
||||
|
||||
```bash
|
||||
# Expand a specific task with 3 subtasks (default)
|
||||
node scripts/dev.js expand --id=3
|
||||
|
||||
# Expand a specific task with 5 subtasks
|
||||
node scripts/dev.js expand --id=3 --subtasks=5
|
||||
|
||||
# Expand a task with additional context
|
||||
node scripts/dev.js expand --id=3 --prompt="Focus on security aspects"
|
||||
|
||||
# Expand all pending tasks that don't have subtasks
|
||||
node scripts/dev.js expand --all
|
||||
|
||||
# Force regeneration of subtasks for all pending tasks
|
||||
node scripts/dev.js expand --all --force
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Tasks marked as 'done' or 'completed' are always skipped
|
||||
- By default, tasks that already have subtasks are skipped unless `--force` is used
|
||||
- Subtasks include title, description, dependencies, and acceptance criteria
|
||||
|
||||
## Logging
|
||||
|
||||
The script supports different logging levels controlled by the `LOG_LEVEL` environment variable:
|
||||
- `debug`: Detailed information, typically useful for troubleshooting
|
||||
- `info`: Confirmation that things are working as expected (default)
|
||||
- `warn`: Warning messages that don't prevent execution
|
||||
- `error`: Error messages that might prevent execution
|
||||
|
||||
When `DEBUG=true` is set, debug logs are also written to a `dev-debug.log` file in the project root.
|
||||
Reference in New Issue
Block a user