commit 07648a141733888253f29361028455d88ff86ff7 Author: Eyal Toledano Date: Tue Mar 4 13:55:17 2025 -0500 initial version diff --git a/.cursor/rules/dev_workflow.mdc b/.cursor/rules/dev_workflow.mdc new file mode 100644 index 00000000..5b4af08a --- /dev/null +++ b/.cursor/rules/dev_workflow.mdc @@ -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=` 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= --prompt=\"\"` 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= --subtasks=` to generate detailed subtasks + 2. Optionally provide additional context with `--prompt=\"\"` 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= --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= --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: + # 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" diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..6d3209b9 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# API Keys (Required) +ANTHROPIC_API_KEY=your_anthropic_api_key_here + +# Model Configuration +MODEL=claude-3-7-sonnet-20250219 # Specify which Claude model to use +MAX_TOKENS=4000 # Maximum tokens for model responses +TEMPERATURE=0.7 # Temperature for model responses (0.0-1.0) + +# Logging Configuration +DEBUG=false # Enable debug logging (true/false) +LOG_LEVEL=info # Log level (debug, info, warn, error) + +# Task Generation Settings +DEFAULT_SUBTASKS=3 # Default number of subtasks when expanding +DEFAULT_PRIORITY=medium # Default priority for generated tasks (high, medium, low) + +# Project Metadata (Optional) +PROJECT_NAME=Your Project Name # Override default project name in tasks.json +PROJECT_VERSION=1.0.0 # Override default version in tasks.json \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..98c257ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Dependency directories +node_modules/ +jspm_packages/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Compiled binary addons +build/Release + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env.test + +# parcel-bundler cache +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Mac files +.DS_Store + +# Debug files +*.debug +init-debug.log +dev-debug.log + +# Project specific +tasks.json +tasks/ \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..fb130eab --- /dev/null +++ b/.npmignore @@ -0,0 +1,46 @@ +# Development files +.git +.github +.vscode +.idea +.DS_Store + +# Logs +logs +*.log +npm-debug.log* +dev-debug.log +init-debug.log + +# Source files not needed in the package +src +test +tests +docs +examples +.editorconfig +.eslintrc +.prettierrc +.travis.yml +.gitlab-ci.yml +tsconfig.json +jest.config.js + +# Original project files +tasks.json +tasks/ +prd.txt +scripts/prd.txt +.env + +# Temporary files +.tmp +.temp +*.swp +*.swo + +# Node modules +node_modules/ + +# Debug files +*.debug \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..fc152906 --- /dev/null +++ b/README.md @@ -0,0 +1,286 @@ +# Claude Task Master + +A task management system for AI-driven development with Claude, designed to work seamlessly with Cursor AI. + +## Installation + +```bash +npm install -g claude-task-master +``` + +## Usage + +### Initialize a new project + +```bash +# Navigate to your project directory +mkdir my-new-project +cd my-new-project + +# Initialize the project +claude-task-init +``` + +This will create the necessary file structure for your project, including: + +- `.cursor/rules/dev_workflow.mdc` - Cursor rules for AI-driven development +- `scripts/dev.js` - Task management script +- `scripts/README.md` - Documentation for the script +- `scripts/example_prd.txt` - Example PRD template +- `.env.example` - Example environment variables +- `.gitignore` - Git ignore file +- `package.json` - Project configuration +- `tasks.json` - Empty tasks file +- `tasks/` - Directory for task files + +## Integrating with Cursor AI + +Claude Task Master is designed to work seamlessly with [Cursor AI](https://www.cursor.so/), providing a structured workflow for AI-driven development. + +### Setup with Cursor + +1. After initializing your project, open it in Cursor +2. The `.cursor/rules/dev_workflow.mdc` file is automatically loaded by Cursor, providing the AI with knowledge about the task management system +3. Place your PRD document in the `scripts/` directory (e.g., `scripts/prd.txt`) +4. Open Cursor's AI chat and switch to Agent mode + +### Initial Task Generation + +In Cursor's AI chat, instruct the agent to generate tasks from your PRD: + +``` +Please use the dev.js script to parse my PRD and generate tasks. The PRD is located at scripts/prd.txt. +``` + +The agent will execute: +```bash +node scripts/dev.js parse-prd --input=scripts/prd.txt +``` + +This will: +- Parse your PRD document +- Generate a structured `tasks.json` file with tasks, dependencies, priorities, and test strategies +- The agent will understand this process due to the Cursor rules + +### Generate Individual Task Files + +Next, ask the agent to generate individual task files: + +``` +Please generate individual task files from tasks.json +``` + +The agent will execute: +```bash +node scripts/dev.js generate +``` + +This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks. + +## AI-Driven Development Workflow + +The Cursor agent is pre-configured (via the rules file) to follow this workflow: + +### 1. Task Discovery and Selection + +Ask the agent to list available tasks: + +``` +What tasks are available to work on next? +``` + +The agent will: +- Run `node scripts/dev.js list` to see all tasks +- Analyze dependencies to determine which tasks are ready to be worked on +- Prioritize tasks based on priority level and ID order +- Suggest the next task(s) to implement + +### 2. Task Implementation + +When implementing a task, the agent will: +- Reference the task's details section for implementation specifics +- Consider dependencies on previous tasks +- Follow the project's coding standards +- Create appropriate tests based on the task's testStrategy + +You can ask: +``` +Let's implement task 3. What does it involve? +``` + +### 3. Task Verification + +Before marking a task as complete, verify it according to: +- The task's specified testStrategy +- Any automated tests in the codebase +- Manual verification if required + +### 4. Task Completion + +When a task is completed, tell the agent: + +``` +Task 3 is now complete. Please update its status. +``` + +The agent will execute: +```bash +node scripts/dev.js set-status --id=3 --status=done +``` + +### 5. Handling Implementation Drift + +If during implementation, you discover that: +- The current approach differs significantly from what was planned +- Future tasks need to be modified due to current implementation choices +- New dependencies or requirements have emerged + +Tell the agent: +``` +We've changed our approach. We're now using Express instead of Fastify. Please update all future tasks to reflect this change. +``` + +The agent will execute: +```bash +node scripts/dev.js update --from=4 --prompt="Now we are using Express instead of Fastify." +``` + +This will rewrite or re-scope subsequent tasks in tasks.json while preserving completed work. + +### 6. Breaking Down Complex Tasks + +For complex tasks that need more granularity: + +``` +Task 5 seems complex. Can you break it down into subtasks? +``` + +The agent will execute: +```bash +node scripts/dev.js expand --id=5 --subtasks=3 +``` + +You can provide additional context: +``` +Please break down task 5 with a focus on security considerations. +``` + +The agent will execute: +```bash +node scripts/dev.js expand --id=5 --prompt="Focus on security aspects" +``` + +You can also expand all pending tasks: +``` +Please break down all pending tasks into subtasks. +``` + +The agent will execute: +```bash +node scripts/dev.js expand --all +``` + +## Manual Command Reference + +While the Cursor agent will handle most commands for you, you can also run them manually: + +### 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. + +## Example Cursor AI Interactions + +### Starting a new project +``` +I've just initialized a new project with Claude Task Master. I have a PRD at scripts/prd.txt. +Can you help me parse it and set up the initial tasks? +``` + +### Working on tasks +``` +What's the next task I should work on? Please consider dependencies and priorities. +``` + +### Implementing a specific task +``` +I'd like to implement task 4. Can you help me understand what needs to be done and how to approach it? +``` + +### Handling changes +``` +We've decided to use MongoDB instead of PostgreSQL. Can you update all future tasks to reflect this change? +``` + +### Completing work +``` +I've finished implementing the authentication system described in task 2. All tests are passing. +Please mark it as complete and tell me what I should work on next. +``` + +## Documentation + +For more detailed documentation on the scripts, see the [scripts/README.md](scripts/README.md) file in your initialized project. + +## License + +MIT \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 00000000..e50baf52 --- /dev/null +++ b/index.js @@ -0,0 +1,19 @@ +/** + * Claude Task Master + * A task management system for AI-driven development with Claude + */ + +// This file serves as the main entry point for the package +// The primary functionality is provided through the CLI commands + +// Export the path to the dev.js script for programmatic usage +exports.devScriptPath = require.resolve('./scripts/dev.js'); + +// Export a function to initialize a new project programmatically +exports.initProject = async (options = {}) => { + const init = require('./scripts/init'); + return init.initializeProject(options); +}; + +// Export version information +exports.version = require('./package.json').version; \ No newline at end of file diff --git a/package-README.md b/package-README.md new file mode 100644 index 00000000..d097cc5e --- /dev/null +++ b/package-README.md @@ -0,0 +1,42 @@ +# Claude Task Master + +A task management system for AI-driven development with Claude. + +## Installation + +```bash +npm install -g claude-task-master +``` + +## Usage + +### Initialize a new project + +```bash +# Navigate to your project directory +mkdir my-new-project +cd my-new-project + +# Initialize the project +claude-task-init +``` + +This will create the necessary file structure for your project, including: + +- `.cursor/rules/dev_workflow.mdc` - Cursor rules for AI-driven development +- `scripts/dev.js` - Task management script +- `scripts/README.md` - Documentation for the script +- `scripts/example_prd.txt` - Example PRD template +- `.env.example` - Example environment variables +- `.gitignore` - Git ignore file +- `package.json` - Project configuration +- `tasks.json` - Empty tasks file +- `tasks/` - Directory for task files + +## Documentation + +For more detailed documentation, see the README.md file in your initialized project. + +## License + +MIT \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ef29f0cb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,507 @@ +{ + "name": "mcp-saas", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mcp-saas", + "version": "1.0.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.16.0", + "dotenv": "^16.4.7" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.16.1.tgz", + "integrity": "sha512-vHgvfWEyFy5ktqam56Nrhv8MVa7EJthsRYNi+1OrFFfyrj9tR2/aji1QbVbQjYU/pPhPFaYrdCEC/MLPFrmKwA==", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "digest-fetch": "^1.3.0", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@types/node": { + "version": "18.19.79", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.79.tgz", + "integrity": "sha512-90K8Oayimbctc5zTPHPfZloc/lGVs7f3phUAAMcTgEPtg8kKquGZDERC8K4vkBYkQQh48msiYUslYtxTWvqcAg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/digest-fetch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", + "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", + "license": "ISC", + "dependencies": { + "base-64": "^0.1.0", + "md5": "^2.3.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..b013c944 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "claude-task-master", + "version": "1.0.1", + "description": "A task management system for AI-driven development with Claude", + "main": "index.js", + "bin": { + "claude-task-init": "./scripts/init.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "prepare-package": "node scripts/prepare-package.js" + }, + "keywords": [ + "claude", + "task", + "management", + "ai", + "development", + "cursor", + "anthropic", + "llm" + ], + "author": "Eyal Toledano", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.10.0", + "chalk": "^4.1.2", + "commander": "^11.1.0", + "dotenv": "^16.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/eyaltoledano/claude-task-master.git" + }, + "homepage": "https://github.com/eyaltoledano/claude-task-master#readme", + "bugs": { + "url": "https://github.com/eyaltoledano/claude-task-master/issues" + }, + "files": [ + "scripts/init.js", + "scripts/dev.js", + "templates/**", + "README.md" + ] +} diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..32c0be59 --- /dev/null +++ b/scripts/README.md @@ -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. diff --git a/scripts/dev.js b/scripts/dev.js new file mode 100755 index 00000000..0004e47b --- /dev/null +++ b/scripts/dev.js @@ -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); +}); \ No newline at end of file diff --git a/scripts/example_prd.txt b/scripts/example_prd.txt new file mode 100644 index 00000000..806bf1a3 --- /dev/null +++ b/scripts/example_prd.txt @@ -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> \ No newline at end of file diff --git a/scripts/init.js b/scripts/init.js new file mode 100755 index 00000000..c069bd74 --- /dev/null +++ b/scripts/init.js @@ -0,0 +1,249 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); +const readline = require('readline'); + +// Create readline interface for user input +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +// Define log levels and colors +const LOG_LEVELS = { + debug: 0, + info: 1, + warn: 2, + error: 3 +}; + +const COLORS = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m' +}; + +// Get log level from environment or default to info +const LOG_LEVEL = process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()] : LOG_LEVELS.info; + +// Logging function +function log(level, ...args) { + const levelValue = LOG_LEVELS[level.toLowerCase()]; + + if (levelValue >= LOG_LEVEL) { + const prefix = { + debug: `${COLORS.dim}[DEBUG]${COLORS.reset}`, + info: `${COLORS.blue}[INFO]${COLORS.reset}`, + warn: `${COLORS.yellow}[WARN]${COLORS.reset}`, + error: `${COLORS.red}[ERROR]${COLORS.reset}` + }[level.toLowerCase()]; + + console.log(prefix, ...args); + } + + // Write to debug log if DEBUG=true + if (process.env.DEBUG === 'true') { + const logMessage = `[${level.toUpperCase()}] ${args.join(' ')}\n`; + fs.appendFileSync('init-debug.log', logMessage); + } +} + +// Function to create directory if it doesn't exist +function ensureDirectoryExists(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + log('info', `Created directory: ${dirPath}`); + } +} + +// Function to copy a file from the package to the target directory +function copyTemplateFile(templateName, targetPath, replacements = {}) { + // Get the template content from the templates directory + const templatePath = path.join(__dirname, '..', 'templates', templateName); + let content = fs.readFileSync(templatePath, 'utf8'); + + // Replace placeholders with actual values + Object.entries(replacements).forEach(([key, value]) => { + const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g'); + content = content.replace(regex, value); + }); + + // Write the content to the target path + fs.writeFileSync(targetPath, content); + log('info', `Created file: ${targetPath}`); +} + +// Main function to initialize a new project +async function initializeProject(options = {}) { + return new Promise((resolve) => { + // If options are provided, use them directly + if (options.projectName && options.projectDescription) { + const projectName = options.projectName; + const projectDescription = options.projectDescription; + const projectVersion = options.projectVersion || '1.0.0'; + const authorName = options.authorName || ''; + + createProjectStructure(projectName, projectDescription, projectVersion, authorName); + resolve({ + projectName, + projectDescription, + projectVersion, + authorName + }); + } else { + // Otherwise, prompt the user for input + rl.question('Enter project name: ', (projectName) => { + rl.question('Enter project description: ', (projectDescription) => { + rl.question('Enter project version (default: 1.0.0): ', (projectVersion) => { + rl.question('Enter your name: ', (authorName) => { + // Set default version if not provided + if (!projectVersion.trim()) { + projectVersion = '1.0.0'; + } + + // Create the project structure + createProjectStructure(projectName, projectDescription, projectVersion, authorName); + + rl.close(); + resolve({ + projectName, + projectDescription, + projectVersion, + authorName + }); + }); + }); + }); + }); + } + }); +} + +// Function to create the project structure +function createProjectStructure(projectName, projectDescription, projectVersion, authorName) { + const targetDir = process.cwd(); + log('info', `Initializing project in ${targetDir}`); + + // Create directories + ensureDirectoryExists(path.join(targetDir, '.cursor', 'rules')); + ensureDirectoryExists(path.join(targetDir, 'scripts')); + ensureDirectoryExists(path.join(targetDir, 'tasks')); + + // Create package.json + const packageJson = { + name: projectName.toLowerCase().replace(/\s+/g, '-'), + version: projectVersion, + description: projectDescription, + author: authorName, + scripts: { + "dev": "node scripts/dev.js", + "list": "node scripts/dev.js list", + "generate": "node scripts/dev.js generate", + "parse-prd": "node scripts/dev.js parse-prd" + }, + dependencies: { + "@anthropic-ai/sdk": "^0.10.0", + "chalk": "^4.1.2", + "commander": "^11.1.0", + "dotenv": "^16.3.1" + } + }; + + fs.writeFileSync( + path.join(targetDir, 'package.json'), + JSON.stringify(packageJson, null, 2) + ); + log('info', 'Created package.json'); + + // Copy template files with replacements + const replacements = { + projectName, + projectDescription, + projectVersion, + authorName, + year: new Date().getFullYear() + }; + + // Copy .env.example + copyTemplateFile('env.example', path.join(targetDir, '.env.example'), replacements); + + // Copy .gitignore + copyTemplateFile('gitignore', path.join(targetDir, '.gitignore')); + + // Copy dev_workflow.mdc + copyTemplateFile('dev_workflow.mdc', path.join(targetDir, '.cursor', 'rules', 'dev_workflow.mdc')); + + // Copy scripts/dev.js + copyTemplateFile('dev.js', path.join(targetDir, 'scripts', 'dev.js')); + + // Copy scripts/README.md + copyTemplateFile('scripts_README.md', path.join(targetDir, 'scripts', 'README.md')); + + // Copy example_prd.txt + copyTemplateFile('example_prd.txt', path.join(targetDir, 'scripts', 'example_prd.txt')); + + // Create main README.md + copyTemplateFile('README.md', path.join(targetDir, 'README.md'), replacements); + + // Create empty tasks.json + const tasksJson = { + meta: { + name: projectName, + version: projectVersion, + description: projectDescription + }, + tasks: [] + }; + + fs.writeFileSync( + path.join(targetDir, 'tasks.json'), + JSON.stringify(tasksJson, null, 2) + ); + log('info', 'Created tasks.json'); + + // Initialize git repository if git is available + try { + if (!fs.existsSync(path.join(targetDir, '.git'))) { + execSync('git init', { stdio: 'ignore' }); + log('info', 'Initialized git repository'); + } + } catch (error) { + log('warn', 'Git not available, skipping repository initialization'); + } + + log('info', `${COLORS.green}${COLORS.bright}Project initialized successfully!${COLORS.reset}`); + log('info', ''); + log('info', 'Next steps:'); + log('info', '1. Run `npm install` to install dependencies'); + log('info', '2. Create a .env file with your ANTHROPIC_API_KEY (see .env.example)'); + log('info', '3. Add your PRD to the project'); + log('info', '4. Run `npm run parse-prd -- --input=<your-prd-file.txt>` to generate tasks'); + log('info', ''); +} + +// Run the initialization if this script is executed directly +if (require.main === module) { + (async function main() { + try { + await initializeProject(); + } catch (error) { + log('error', 'Failed to initialize project:', error); + process.exit(1); + } + })(); +} + +// Export functions for programmatic use +module.exports = { + initializeProject, + createProjectStructure, + log +}; \ No newline at end of file diff --git a/scripts/prd.txt b/scripts/prd.txt new file mode 100644 index 00000000..69d59650 --- /dev/null +++ b/scripts/prd.txt @@ -0,0 +1,350 @@ +<context> +# Overview +The MCP SaaS is a **hosted Model Context Protocol (MCP) server platform** that lets users spin up and customize MCP servers on demand. MCP is an open standard that provides a “USB-C port for AI” – a unified way to connect AI assistants to various data sources and tools ([Introduction - Model Context Protocol](https://modelcontextprotocol.io/introduction#:~:text=MCP%20is%20an%20open%20protocol,different%20data%20sources%20and%20tools)). Instead of running connectors locally or building custom integrations for each data source, developers can use MCP to expose data through standard servers and let AI applications (MCP clients) connect to them ([Introducing the Model Context Protocol \ Anthropic](https://www.anthropic.com/news/model-context-protocol#:~:text=The%20Model%20Context%20Protocol%20is,that%20connect%20to%20these%20servers)). Our service extends this concept by hosting these MCP servers in the cloud and offering a web interface for configuration. The value proposition is that **developers and teams can easily integrate their own data and tools with AI models (like Claude or IDE-based agents) without managing infrastructure** or writing boilerplate code. + +**Key Differentiators:** This platform distinguishes itself from the existing open-source MCP servers by focusing on ease of use, hosting, and expanded capabilities: + +- **No Self-Hosting Required:** Open-source MCP implementations typically run on a user’s local machine or server ([Introducing the Model Context Protocol \ Anthropic](https://www.anthropic.com/news/model-context-protocol#:~:text=Claude,to%20the%20Claude%20Desktop%20app)). Our service eliminates the need to set up or maintain servers – deployment is handled automatically on a global cloud platform. This means even non-dev users or those in restricted IT environments can use MCP tools remotely, **enabling cloud access** to what were previously local-only connectors. +- **Easy Configuration & Customization:** Instead of cloning repos and running command-line tools, users get a **friendly dashboard** to select from a library of MCP tools and configure them with a few clicks. This lowers the barrier to entry and speeds up integration. +- **Multiple Tools in One Server:** With open-source MCP, each server typically provides one capability or data source ([Introduction - Model Context Protocol](https://modelcontextprotocol.io/introduction#:~:text=At%20its%20core%2C%20MCP%20follows,can%20connect%20to%20multiple%20servers)). Our hosted solution will allow users to **combine multiple tools on a single MCP server instance** (if desired) by simply toggling them on/off. This creates a composite toolset accessible via one endpoint, which is unique compared to the one-tool-per-server model. +- **Premium Tool Library:** While Anthropic has open-sourced many connectors (Google Drive, Slack, GitHub, Git, Postgres, Puppeteer, etc. ([Introducing the Model Context Protocol \ Anthropic](https://www.anthropic.com/news/model-context-protocol#:~:text=Claude%203,GitHub%2C%20Git%2C%20Postgres%2C%20and%20Puppeteer))), our service curates and extends this library with **premium tools** not readily available elsewhere. These could include connectors to enterprise apps, enhanced versions of open tools with additional features, or brand-new integrations developed in-house. Subscribers get immediate access to these tools without hunting through GitHub repos. +- **Built-in Analytics and Management:** The platform provides **usage metrics, logs, and monitoring** out-of-the-box – capabilities that are not present in basic open-source MCP servers. Users can track how often their tools are called and monitor performance via the dashboard, helping them manage usage and debug issues. +- **Integration Ready Outputs:** Instead of figuring out how to run an MCP server and connect it, users receive ready-to-use endpoints (an `npx` command, JSON configuration snippet, or direct SSE URL) that plug into AI clients with minimal effort. This streamlines the process of hooking up the MCP server to Claude, Cursor IDE, or other MCP-compatible AI tools. + +By addressing the above, the service makes **MCP accessible as a hassle-free cloud service**. This drives our core value: **“Your custom AI tools, one click away”** – enabling rapid setup and integration of context-providing tools for AI assistants, while the platform handles the heavy lifting of hosting, scaling, and maintenance. + + ([What is Model Context Protocol?](https://portkey.ai/blog/model-context-protocol-for-llm-appls)) *Figure: General MCP architecture. An “MCP Host” (e.g. Claude, Cursor IDE, or another AI tool) can connect via the MCP protocol to one or more **MCP servers**, each interfacing with a specific resource or service. In a typical setup, MCP servers might run on *your local machine* to expose local files, databases, or APIs ([Introduction - Model Context Protocol](https://modelcontextprotocol.io/introduction#:~:text=At%20its%20core%2C%20MCP%20follows,can%20connect%20to%20multiple%20servers)). Our product moves this into the cloud – hosting those MCP servers for you – so the AI assistant can reach your tools from anywhere via a secure internet endpoint.* + +# Core Features + +**User Authentication (GitHub OAuth):** The platform will use GitHub OAuth for sign-in and sign-up. Users can log in with their GitHub credentials, streamlining onboarding for developers. OAuth ensures we don’t handle raw passwords and can easily fetch basic profile info (username, email) to create the user account. Upon first login, a new user profile is created in our system linked to their GitHub ID. This also sets the stage for future integrations (e.g. pulling GitHub repos as data sources, or verifying student/hobby status via GitHub). The PRD priority is to implement GitHub OAuth, but the system will be designed to allow adding other OAuth providers later (e.g. Google, Microsoft) for broader enterprise appeal. + +**Dashboard for Selecting and Configuring Tools:** A core part of the user experience is a **web dashboard** where users can create and manage their hosted MCP servers. Key elements of the dashboard include: + +- **MCP Server List:** A home screen showing all MCP instances the user has created, with status (running/stopped), name, and key details (number of tools, last active time, etc.). From here, users can click “Create New Server.” +- **Tool Library Browser:** When creating or editing an MCP server, users are presented with a **catalog of available tools** (the library of MCP connectors). Each tool listing includes a name, description, and possibly an icon or category. Users can search or filter (e.g. by “file system”, “database”, “API integration”, etc.). For MVP, this library is curated (initially we’ll include popular connectors like file access, GitHub, Slack, databases, web browser automation, etc.). +- **Add/Remove Tools UI:** Users can add a tool to their MCP instance by selecting it from the library. Upon adding, the tool might require configuration – for example, the Slack tool might need an API token, or the Google Drive tool might need OAuth credentials. The dashboard will provide a form for each tool’s required settings (with field validations and help text). Users can also remove tools from the config with a click (which will update the server on redeploy). +- **Configuration Management:** In addition to tool-specific settings, the server itself may have configurations: a name, a description, or global environment variables that multiple tools might use. The dashboard allows editing these. For example, if multiple tools need an API key for the same service, the user could set it once globally. +- **One-Click Deployment:** A prominent **“Deploy” or “Save & Deploy” button** will provision or update the hosted MCP server with the selected tools. This triggers the backend automation (see Technical Architecture) to either create a new Cloudflare Worker or update an existing one with the new tool set. Feedback (like a loading spinner and status messages) is provided during deployment. In MVP, deployments should complete within a few seconds. After deployment, the dashboard will show the server’s **connection info (endpoints and commands)** for the user to integrate. + +**Add/Remove Tools from a Hosted MCP Instance:** Users are not locked into their initial choices; they can modify their MCP server’s capabilities post-creation. This feature means: + +- **Dynamic Tool Management:** From the dashboard, selecting an existing MCP instance allows users to see which tools are currently enabled. They can add new ones or remove some and then re-deploy. The backend will handle updating the running instance (which may involve restarting it with a new config). This dynamic configurability encourages experimentation – users can start with a minimal toolset and grow it over time. +- **Hot Reload vs. Restart:** (For MVP, a full restart on config change is acceptable.) In future iterations, we might support hot-swapping tools without downtime. For now, after a user updates the tool list and redeploys, the platform will restart that MCP server instance with the new configuration. Any connected AI clients may need to reconnect (we will document this). +- **Versioning (Future Consideration):** The system will keep track of tool versions or last-updated time. In MVP, we assume using latest stable versions of each tool library. Later, we might let users pin a specific version of a tool connector or roll back changes if a new tool config causes issues. + +**Automated Deployment on Cloudflare Workers AI:** The hosting backbone of the product is **Cloudflare Workers** (with Workers AI capabilities if needed). Each MCP server instance is essentially a serverless function (or set of functions) running on Cloudflare’s global network, close to users. Key requirements and behaviors: + +- **Deployment Automation:** When the user hits deploy, our backend uses Cloudflare’s API to either upload a Cloudflare Worker script or create a new instance of a pre-built worker with configuration. The Worker contains the logic for the selected MCP tools. Cloudflare’s environment runs the code in a serverless manner – we benefit from automatic scaling, low-latency global access, and not having to manage VM or container infrastructure. +- **Isolated Instances:** Each MCP server runs in isolation (sandboxed by Cloudflare’s architecture per script/instance). This ensures that one user’s server (and data/API keys) isn’t accessible to another. We leverage Workers **Namespaces or environment bindings** to pass each instance its config securely. For example, if a user’s MCP server includes a database password or API token, that will be stored as an encrypted secret and bound to their Worker instance only. +- **Workers AI Compatibility:** While our primary use-case is running connector logic (which might just be network calls or file I/O), using Cloudflare **Workers AI** means we have the option to also execute ML models at the edge if needed. This isn’t a core MVP feature, but it’s a forward-looking choice – e.g., if a tool involves vector embeddings or running a small model, it could leverage Workers AI’s GPU support. For now, **the focus is on connectors**; we simply ensure the platform can deploy to the Workers runtime environment successfully. (Cloudflare Workers provides the needed compute and networking for MCP servers just like running them locally, but in a serverless way.) +- **Scaling and Performance:** Because Workers scale automatically and are **pay-as-you-go ([Workers AI: serverless GPU-powered inference on Cloudflare’s global network](https://blog.cloudflare.com/workers-ai/#:~:text=That%27s%20why%20we%20are%20excited,and%20it%27s%20built%20from%20the))**, each MCP server can handle multiple concurrent requests or SSE streams without manual intervention. The service should impose sensible limits (through pricing tiers) but not require the user to worry about load – if their usage grows, Cloudflare will seamlessly handle more requests up to our set quotas. This is a major advantage over self-hosting, where the user would need to deploy to a server or cloud instance themselves. + +**Output Formats for Integration:** Once a user’s MCP server is deployed, the platform provides **multiple ways to integrate it with AI tools**. Different users have different workflows, so we support: + +- **NPX Command:** An `npx` command is provided for users who want a quick CLI invocation. For example, after deployment the dashboard might show a command like `npx mcp-client@latest --server-url https://<user>.ourservice.dev --api-key $KEY`. Running this command locally would launch an MCP client that connects to the hosted server (perhaps using the official MCP client SDK under the hood). This is useful for tools or environments that can run local commands (for instance, if Claude’s desktop app or another IDE expects a local process, the npx script can act as a local proxy to the remote server). It also serves as a quick test: users can run the npx command in a terminal to verify their server responds as expected. +- **JSON Configuration:** For developer tools like Cursor IDE or other IDEs that support MCP, we provide a JSON config snippet that the user can drop into their settings. This JSON includes details such as the server name, transport type (`sse` or `stdio`), and the endpoint URL. For example: + ```json + { + "name": "My MCP Server", + "transport": "sse", + "url": "https://<user>.ourservice.dev/sse", + "api_key": "<KEY>" + } + ``` + A format like this can be placed in Cursor’s `.cursor/mcp.json` or in an application’s config file to inform the client about the custom tool. We will document how to use this JSON for various AI clients. Providing this ready-made configuration saves users from manually typing details, reducing integration friction. +- **SSE Endpoint URL:** For direct integration (especially with Claude or any system that allows a URL), we give the **Secure SSE URL** of the hosted MCP server. For example: `https://<instance-id>.ourservice.dev/sse?key=<secret>`. This endpoint implements the MCP protocol over Server-Sent Events – the standard way to connect remote MCP servers ([Cursor – Model Context Protocol](https://docs.cursor.com/context/model-context-protocol#:~:text=Cursor%20implements%20an%20MCP%20client%2C,transports)) ([Cursor – Model Context Protocol](https://docs.cursor.com/context/model-context-protocol#:~:text=assuming%20it%20is%20running%20locally,8765)). A user can input this URL into an interface like “Add MCP Server” in Cursor (choosing SSE transport) or in future Claude interfaces that accept remote URLs. The SSE endpoint streams events and data between the AI assistant and the server in real-time. In MVP, we’ll support SSE (which covers most remote use-cases); STDIO is mainly for local, so we won’t need to support a remote stdio beyond the npx local proxy. + +In all cases, **authentication and security** are considered. The npx command and SSE URL include a secret API key (or use an auth header) so that only the rightful user (and their AI client) can access the MCP server. The JSON config will mention how to include the API key as well. This multi-format output ensures that whether the user is technical or not, and whichever AI tool they use, they have a straightforward way to plug in their new MCP server. + +**User Analytics & Monitoring:** To help users understand and control their usage, the platform will include analytics features: + +- **Dashboard Metrics:** For each MCP server instance, the dashboard will display key metrics such as number of API calls made (this could be overall requests or broken down by tool), data transferred, and active time. For example, a user might see “**Calls this month: 850**” and “**Active connections: 0 (idle)**” for an instance. This information updates periodically (possibly with a refresh button or live updates via WebSocket). +- **Usage Graphs:** A simple chart (e.g. line graph) could show usage over time – e.g. daily request count in the last 30 days – especially for paid tiers where usage limits apply. MVP can use a basic library to plot calls per day. If real-time plotting is too much, at least a summary count and last active timestamp will be provided. +- **Logs (MVP Limited):** While a fully featured log viewer might be a later addition, the MVP will capture basic event logs for each server (e.g. “Tool X called with query Y,” “Error: failed to fetch from API Z”). In the dashboard, users can view recent log entries or errors to debug their tools’ behavior. We might limit the log history to last N events or last 24 hours for performance. +- **Alerts and Notifications:** (Future) The system can send an email or dashboard alert if the user approaches their usage limits or if an instance encounters repeated errors. This is not required at launch, but designing the analytics with hooks for alerts in mind will help scalability. + +All analytics are accessible through the secure dashboard. Internally, we’ll collect this data via our API gateway or within the Worker (for lightweight metrics) and store it in a database or analytics service. The goal is to provide transparency so users can **monitor their MCP servers’ health and usage**, making the product feel reliable and professional for production use. + +# Pricing Strategy +Our pricing model will be **freemium with tiered subscriptions**, designed to accommodate individual tinkerers up to enterprise teams. We will have **Free, Paid, and High-Tier (Enterprise)** plans, with multiple dimensions for scaling revenue as users grow. Key pricing dimensions include: the number of tools enabled, usage volume (API calls), number of active instances, and advanced enterprise needs. + +**Free Tier (Developer Hobby Plan):** This tier lowers the barrier to entry and encourages viral adoption, while imposing limits that naturally lead serious users to upgrade. Features and limits of the Free plan: + +- **Limited Tools per Server:** A free user can enable a small number of tools on any single MCP server (e.g. *up to 2 tools* per instance). This allows trying out a couple of integrations (for example, connecting to a local filesystem and one API), but for richer servers with many capabilities, an upgrade is required. +- **1 Active MCP Instance:** The free tier might allow only one active server at a time (or possibly 1 concurrent and up to 2 total configured, to let users experiment). This ensures heavy users who need multiple separate agents (e.g. different projects or contexts) will consider paying. +- **Usage Cap:** We will include a generous but limited number of API calls or events per month (for instance, *5,000 calls per month* free). This is enough for small projects and testing, but if the user starts relying on the service in earnest (e.g. daily use with an AI coding assistant), they’ll likely hit the limit. We can also rate-limit the free usage (like X calls/minute) to prevent abuse. +- **Community Support:** Free users have access to documentation and community forums for support. Direct support response or SLA is not guaranteed at this tier. +- **No Cost (Freemium):** As the name implies, this tier is $0. It’s aimed at students, hobbyists, or professionals prototyping an idea. By offering meaningful functionality for free, we hope to drive adoption and word-of-mouth (e.g. developers sharing the tool with colleagues, or writing blog posts about using it). + +**Paid Tier (Pro / Team Plan):** The paid tier will likely have a fixed monthly subscription (for example, **$X per month** for Pro) which unlocks higher limits and possibly additional features. We also consider usage-based billing for overages. Key attributes: + +- **More Tools per Server:** A higher allowance on how many tools can be combined in one MCP instance (e.g. *up to 5 or 10 tools* on Pro). This encourages users to build powerful composite connectors on one endpoint, which is valuable for complex use cases. +- **Multiple Instances:** Pro users can run more simultaneous MCP servers – for instance, *up to 3 active instances*. This is useful if a small team has different projects (one MCP server for codebase access, another for a database, etc.) or if one user wants to separate concerns. +- **Increased API Call Quota:** The monthly call limit is higher (e.g. *100,000 calls per month included*). If a user exceeds the included calls, we may charge an overage fee per 1,000 calls (or suggest upgrading to a higher plan). We will also lift any strict rate limits, allowing bursty usage as long as it stays within monthly allotment. +- **Premium Tools Access:** Certain “premium” connectors (especially those that might incur cost or require special maintenance) could be reserved for paid plans. For example, an integration to a proprietary enterprise software or a high-compute tool might only be available to paying users. Pro users get access to the full library of standard tools and these premium ones. (We must be transparent about which those are in the library UI with a lock icon or similar.) +- **Analytics & Support:** Paid users get more advanced analytics (longer log retention, more detailed usage breakdown) and priority support (email support with 24-48h response). While the MVP might treat all users the same initially, the plan is to eventually offer better support to subscribers. Possibly, Pro users could also share access with a small team (e.g. invite 2-3 collaborators to view the dashboard or manage servers – though team features might be an enterprise feature later). +- **Revenue Expansion:** The Pro plan not only has a subscription fee but also provides avenues for expansion revenue. If a team uses significantly more than the included quota, we’ll have clear overage pricing. Additionally, if they need more instances or tools beyond the plan limits, we might allow purchasing add-ons (e.g. “Extra 2 instances for $Y” or “additional 50k calls”). Initially, however, we’ll keep it simple with just the base Pro plan and encourage upgrade to Enterprise for big needs. + +**High-Tier / Enterprise Plan:** For organizations with large scale or specific requirements, we will offer an Enterprise tier (likely custom-priced, sales-driven). This caters to companies that might use the service in production for many users or across departments. Features might include: + +- **Unlimited or Negotiated Limits:** Enterprise customers could have *custom limits* (e.g. they might need 10+ MCP instances, or >1 million calls per month). We would negotiate a contract that fits their usage, possibly with volume discounts. Essentially, this tier removes the friction of quotas – the service can scale to the organization’s needs with pricing scaled accordingly. +- **Enterprise-Only Features:** This could include single sign-on (SSO) integration for their team, dedicated account management, and the ability to create sub-accounts or team roles (e.g. admin, developer, viewer roles in the dashboard). We might also allow **on-premise or virtual private deployment** for enterprises that have compliance restrictions – e.g. deploying our MCP hosting stack into their cloud, or offering a region-specific deployment if they need data residency. (These are future possibilities once the core product is stable.) +- **Security and SLA:** Enterprise plan would come with a **custom SLA (uptime guarantee)** and priority support (perhaps 24/7 support or a dedicated support engineer contact). Security features like audit logs, encryption options, and compliance (HIPAA, GDPR assurances, etc.) would be packaged here to satisfy enterprise IT requirements. +- **Pricing Model:** Likely a yearly contract or monthly minimum, plus usage. For example, an enterprise might pay a base fee (to cover up to X usage) and then tiered pricing for overages or additional instances beyond that. Because multiple dimensions are in play (tools, instances, calls), we’ll remain flexible. One approach is to have an **enterprise platform fee** that unlocks everything (unlimited tools, many instances), and then purely usage-based billing for API calls beyond a certain threshold. This way, enterprise customers essentially pay for what they use, but with guarantees of service and support. +- **Potential Add-On Services:** We could upsell services like custom tool development (our team building an MCP connector that the enterprise specifically needs), training sessions, or integration assistance as part of a professional services package. + +**Revenue Expansion Considerations:** We have multiple axes to grow revenue per user: if a user needs more tools per server, more servers, or more throughput, they can either move to the next tier or pay add-ons. Over time, we might introduce ala carte add-ons even for Pro (like “enable 2 more tools on your instance for $5/mo” if that proves viable). However, the initial strategy is to keep plans straightforward to avoid analysis paralysis. The free tier drives adoption, the Pro tier converts power users and small teams with a predictable monthly price, and the Enterprise tier captures large customers with a scalable, custom approach. This tiered model, combined with usage-based components, should support **logarithmic MRR growth** – as customers succeed and use the service more, their spend increases in a way that feels natural and value-aligned. + +# User Flow + +This section describes the end-to-end **user journey**, from first visiting the site to running an MCP server and using it. Ensuring a frictionless, intuitive flow is key to onboarding and retention. + +**1. Sign-Up and Onboarding:** A new user arrives at our landing page, which highlights the value (“Host your own AI tool server in minutes,” etc.). They can click **“Get Started – It’s Free”**, which brings them to the GitHub OAuth login. After OAuth, the user lands in the application. If it’s their first time, we present a brief onboarding sequence: for example, a welcome message and a quick tour of the dashboard. We might highlight where to create a server and how to access docs. *MVP detail:* The tour can be as simple as tooltips or a one-time modal with instructions (“1. Create a server, 2. Add tools, 3. Connect to Claude/Cursor…”). The user’s account is now created (Free tier by default), and they may have 0 servers initially. We could also automatically create a **“Default” MCP server** with no tools (in stopped state) to prompt them to configure it, but this is optional. The key is that after sign-up, the user knows how to proceed to create their first MCP instance. + +**2. Creating a New MCP Server:** The user clicks **“Create New MCP Server”** (a prominent button on the dashboard). A dialog or dedicated page appears to set up the new server. They will input: + - A **Name** for the server (e.g. “My Project Tools” or “Salesforce Connector”). This helps identify it later, and could be used in integration settings as a display name. + - (Optional) a short **Description** or notes, especially if they have multiple servers (not critical for MVP, but nice for clarity). + - Possibly, choose a **base template** (if we offer any presets). MVP might not have templates, but in future, we might show template options like “💻 Codebase Assistant – includes Git + Filesystem tools” or “📊 Data Assistant – includes Google Drive + Spreadsheet,” to jump-start configuration. In MVP, likely the user will build from scratch, so this step is minimal. + - Click **“Create”**, which leads them into the configuration interface for the new server. + +**3. Selecting Tools and Integrations:** Now on the MCP server config page (or modal), the user sees the library of tools (as described in Core Features – Dashboard). They can browse or search for a tool to add. For each tool: + - The user clicks “Add” (or a toggle switch) to include that tool. Immediately, that tool might expand a configuration pane. For example, if they add **Google Drive**, we display fields to enter Google API credentials or initiate an OAuth flow. Or if they add **Filesystem**, maybe a path or permission setting is needed (though for a cloud-hosted service, filesystem access might be simulated or limited to a cloud storage bucket – this detail will be covered in technical design). + - The user provides required config for each selected tool. We will validate inputs (e.g. check token formats, required fields not empty). If any tool has optional settings, those can be left default. + - They can repeat this for multiple tools. We’ll ensure that adding a tool does not conflict with others. (In general, MCP servers can have multiple tools and the protocol distinguishes them by name when the AI calls them, so it should be fine). If there are any incompatible combinations, the UI should prevent or warn, though none are expected in MVP. + - The UI may assign a default **port or endpoint name** for each tool behind the scenes. (For instance, MCP might identify tools by name; if needed, we ensure unique names or aliases, but likely the tools come with predefined identifiers that the client will see.) We abstract this detail from the user, they just care that those capabilities are present. + +**4. Deploying the MCP Server & Receiving Credentials:** Once satisfied with the selected tools and their configuration, the user hits **“Deploy”**. The UI then transitions to a deployment status: showing a spinner or progress bar with messages like “Deploying your server…”, “Provisioning on Cloudflare…”, etc. During this time, the backend is packaging the code and deploying to Cloudflare Workers (as detailed in Technical Architecture). On success (which ideally takes just a few seconds), the status updates to **“Your MCP server is live!”** and presents the integration info. The user will see: + + - **Server URL / SSE Endpoint:** e.g. `https://john123.mcp.example.com/sse` (the exact format TBD, but likely a subdomain or unique path). Alongside this, a copyable **API Key** or token (if we use query param or require an `Authorization` header). We will show a copy button for convenience. We might display the full curl example like `curl <URL>?key=XYZ` for advanced users to test connectivity. + - **NPX Command:** e.g. ``npx @ourservice/client -s john123.mcp.example.com -k YOUR_KEY`` or a similar one-liner. This is shown in a code block with another copy button. Possibly we also allow the user to pick a specific client command – for instance, if we have an official NPM package, that’s one, but if their environment is Python, we might show a `pip install ourservice && python -m ourservice_client ...` command instead. MVP can start with just the npx (Node) option since Node is common. Documentation can cover other methods. + - **JSON Config:** We present a pre-filled JSON snippet as described earlier. The user can toggle between, say, “Cursor config” and “Generic JSON” if needed, but likely one format works for most (transport + URL + key). This snippet is in a text area for copying. We will specifically note in the UI: “Use this in tools like Cursor IDE (paste into .cursor/mcp.json)”. + - **Summary of Tools:** We’ll also list which tools are running on this server (for confirmation). E.g. “Tools enabled: Slack, Google Drive”. This helps the user remember what this endpoint includes, which is useful if they set up multiple servers. + + The user can now proceed to integrate this with their AI assistant of choice. + +**5. Integrating with AI Tools (Claude, Cursor, etc.):** This step happens outside our platform but is crucial to document. We assume once the user has the connection info, they will: + + - **In Cursor IDE:** go to Settings -> MCP -> “Add New MCP Server”. They will choose **Type: SSE**, give it a nickname (e.g. “My Tools”), and paste the URL we gave (including the `/sse` and perhaps the key if required in the URL forma ([Cursor – Model Context Protocol](https://docs.cursor.com/context/model-context-protocol#:~:text=For%20SSE%20servers%2C%20the%20URL,http%3A%2F%2Fexample.com%3A8000%2Fsse))4】. They save, and the Cursor client might test the connection. Once added, the tools should appear in Cursor’s interface (the agent will list the tool names). We should ensure that the tools’ identifiers we provide are descriptive for the user. For example, if Slack tool is added, Cursor might show an ability like “Slack: channel messaging” – this comes from the MCP server advertising its tools. Our service will make sure the server properly reports the tool names so that clients display them. + - **In Claude (Anthropic’s Claude Desktop):** Currently, Claude Desktop supports local MCP servers via its UI. For remote, users might run the npx command we gave in a terminal on their machine. That npx command effectively connects our cloud MCP server to Claude as if it were local (likely by piping Claude’s requests to our cloud via SSE under the hood). Alternatively, if Claude introduces a way to enter an SSE URL, the user can use that directly. We will provide guidance in our documentation for Claude users. (For MVP, we may test and document the npx approach if that’s the primary method). The user experience would be: run `npx ...` before asking Claude to use the tool, and Claude will detect the MCP server connection through its local interface. + - **Testing:** The user might then ask Claude or the Cursor agent to perform an action using the new tool (e.g. “Find files in my Drive about project X” or “Post a message on Slack channel Y”). They should see the AI utilizing the tool, and our backend would log the interaction (which the user can see in their dashboard logs). This successful round-trip will validate the setup. + +**6. Managing and Monitoring via Dashboard:** After initial setup, the user can always return to our web dashboard to manage their MCP servers. Typical actions and flows: + + - **View Status:** On the dashboard, each server might show a green “Running” status. If needed, we might allow pausing/stopping an instance to conserve usage (though since Workers are event-driven, an idle server doesn’t cost, but stopping could be logically disabling access). MVP might not require a stop function, but could be a nice control (with a play/stop button per instance). + - **Edit Configuration:** The user can click on a server to edit it. This brings back the tool selection UI. They can add a new tool or remove one, or update credentials (say their API key changed). After making changes, they deploy again. We ensure they know that the endpoint might restart briefly. Once updated, they can immediately use the new tools (or see that removed ones are no longer available to the AI). + - **Rotate Credentials:** If the user fears a key leak or just wants to rotate their server’s URL/key, we’ll provide a **Regenerate Key** option. This will issue a new secret and update the endpoint (the old one becomes invalid). The dashboard will then show the updated info for them to reintegrate on the client side. (This is more of a security feature, possibly not mandatory for MVP, but good to include if time permits.) + - **Monitor Usage:** The user can navigate to an analytics section (or within each server’s detail page) to see the usage stats. For example, they open their server and see charts or counts of how many calls were made this week. If they are on a paid plan, this helps them gauge if they’re nearing limits. If on free, it shows how soon they might need to upgrade. + - **Upgrade Prompt:** If a user on free tier hits a limit (e.g. tries to add a 3rd tool), the UI should guide them to upgrade. For instance, when they click “Add” on a third tool, we can show a modal: “Upgrade to Pro to enable more than 2 tools on one server.” This way, the flow itself drives the upsell at natural points. Similarly, if their usage is maxed out, an alert or banner in the dashboard can say “You’ve reached the free plan limit – upgrade to continue uninterrupted service.” + - **Multiple Servers Management:** If the user has several MCP servers, they can manage each one separately. The dashboard list view allows them to select which server to view or edit. If on Free plan with only one server, this part is simple. On Pro, managing multiple might involve a tabbed interface or a list with selection. MVP can keep it basic (a list of names and clicking one loads its details). + +Throughout the user flow, we emphasize **clarity and simplicity**. Each step should be as guided as possible: e.g., tooltips or help icons near complex concepts (like what SSE means, or how to use the npx command). We will also maintain a **Documentation section** or links to a docs site with step-by-step guides (including screenshots) for these flows. A smooth user flow not only helps first-time users succeed but also reduces support requests and increases conversion (a user who easily sets up a working integration is more likely to become a paying customer). + +# Technical Architecture + +The system architecture is designed for **scalability, security, and ease of deployment** across all components – from the web app to the MCP servers running on Cloudflare. Below is an overview of each major component and how they interact: + +**1. Component Overview:** + - **Web Frontend (Dashboard):** A single-page application (SPA) or modern web app (built with React/Vue or similar) that users interact with. It communicates with our backend via RESTful or GraphQL APIs. It handles the UI for login, configuring servers, and displaying analytics. + - **Backend API & Orchestration:** A server (or set of serverless functions) that handles all application logic: user authentication callbacks (GitHub OAuth processing), CRUD operations for MCP server configurations, triggering deployments to Cloudflare, and serving usage data. This can be built with Node.js (Express/Fastify) or Python (FastAPI) etc., depending on team expertise. We might host this on a cloud platform or even Cloudflare Workers if we go serverless all-in. The backend has access to a **database** and third-party APIs (Cloudflare API, etc.). + - **Database:** A persistent store for user data. Likely a PostgreSQL or a serverless DB (Cloudflare D1 if sticking to CF, or Firebase/Firestore, etc.). It stores users, their MCP server configs (which tools enabled, any stored credentials or settings), usage logs, and subscription info. Security is paramount: any sensitive info (like API tokens for tools) should be encrypted at rest. + - **Cloudflare Workers (MCP Instances):** Each user’s MCP server runs as code deployed to Cloudflare’s Workers infrastructure (which is global, serverless). We either deploy one Worker **per MCP instance** or use a multi-tenant approach with one Worker handling multiple instances keyed by URL subpaths. For isolation and simplicity, one-per-instance is preferred in MVP. These Workers contain the logic for the selected tools (essentially they include the MCP server libraries for those tools). When an AI client connects via SSE or when the npx client calls, the Worker engages the tool logic and communicates over the MCP protocol. + - **API Gateway / Reverse Proxy:** (If needed) We might have an API gateway layer that fronts the Workers. However, Cloudflare Workers themselves can directly receive HTTP requests on unique routes or subdomains. We will likely use Cloudflare’s routing (e.g., workers.dev subdomains or custom domain routing) to direct requests for a given instance to the correct Worker. We will incorporate **rate limiting** and API key checking at this layer (either within the Worker code or via Cloudflare’s API Gateway rules) to enforce our usage policies. + - **Analytics & Monitoring Service:** To collect usage data, we might use Cloudflare’s built-in analytics (they have some for Workers), or instrument our code to log events to our database or a separate analytics pipeline (like Segment or a simple logging DB table). This service isn’t user-facing but feeds the Dashboard metrics. It could be as simple as writing an entry to a “usage” table every time a request is handled, then aggregating by user. + +**2. MCP Server Deployment Process:** +When a user hits “Deploy”, here’s what happens under the hood (MVP approach): + - The frontend calls our **Backend API** (e.g., `POST /deploy`) with the user’s desired config (selected tools and their settings). + - The backend validates that config (ensures user is allowed that many tools, checks config completeness). Then, it prepares the code bundle for the MCP server. We likely have a template or library for each tool. For example, we maintain an NPM package or a set of modules for each official tool connector (possibly leveraging the open-source implementatio ([Introducing the Model Context Protocol \ Anthropic](https://www.anthropic.com/news/model-context-protocol#:~:text=Claude%203,GitHub%2C%20Git%2C%20Postgres%2C%20and%20Puppeteer))4】). The backend will **dynamically build a Worker script** that includes only the selected tools. This could be done via a build script or by assembling a JS code string. + - Using Cloudflare’s API (or SDK), the backend will **upload the script** as a Cloudflare Worker. If it’s a new server, we create a new worker with an ID tied to the user’s instance (e.g. user `uid123-server1`). If updating, we update that existing worker script. Cloudflare Workers can be deployed via a REST API where we send the JS/WASM bundle and some metadata. We’ll also set any **Environment Variables or Secrets** for that worker at this time – for example, if the user provided a Slack token, we add it as a secret env var accessible to the Worker. We also include an env var for the API key the worker should require for incoming requests, generated by us for this instance. + - Cloudflare deploys this globally. The backend then configures a route: e.g. `john123.mcp.example.com/*` or similar to map to this Worker. Alternatively, we use Cloudflare’s subdomain per worker feature (workers can be addressed at `<worker-name>.<subdomain>.workers.dev`). We’ll provide the user with that route as the SSE endpoint. + - The backend returns success to the frontend with the connection details (the same ones we show the user). We store the mapping of instance -> endpoint, tools, etc. in our DB as well. + +This process leverages **serverless deployment** so we don’t manage servers ourselves. It’s important that build and deployment are fast; if building a custom bundle per deploy is too slow, an alternative is deploying a single “universal” worker that has all tools and just reads the config on each request. However, that might be heavier and less secure (all user configs in one runtime). MVP will try per-instance deployment, and we can optimize as needed (e.g., caching built bundles for identical tool sets, though that’s an edge case). + +**3. Security Considerations:** + - **API Keys & Auth:** Every request from an AI client to an MCP server must include a valid API key or token that we issued to the user. We will likely use a long random token (e.g. UUID4 or similar) per instance. The Cloudflare Worker checks this token on each request (for SSE, it checks at connection start). If missing or invalid, it rejects the connection. This prevents others from hitting the endpoint if they discover the URL. Communication can be over HTTPS (always, since Cloudflare provides SSL termination), so the token is not exposed in plain text. + - **User Credential Storage:** Any credentials the user provides for tools (like a database password, API tokens for third-party services) will be stored encrypted in our database and only decrypted when deploying to Cloudflare (then set as env vars). We’ll use encryption keys managed by our service (or KMS if available). The frontend will transmit those securely (HTTPS, and possibly we never log them). On Cloudflare’s side, environment secrets are also encrypted at rest. We ensure that when a user deletes a server or a credential, we remove it from our storage and can also remove it from Cloudflare Worker (via their API). + - **Isolation:** By using separate Worker instances, each MCP server is isolated from others. There’s no risk of data leakage across users. Even if multiple instances run on the same physical machine in Cloudflare’s network, their memory and env are separated by the runtime. + - **API Gateway & Rate Limiting:** We will implement rate limiting rules – for instance, using Cloudflare’s **Workers KV or Durable Object** to count requests per API key. If a user exceeds their quota, the Worker can start returning an error (or a notice event) indicating they are over limit. We might also integrate Cloudflare’s own **API Gateway** product or use a middleware in the Worker to throttle. The decision is between simplicity (do it in-code with counters) vs. using Cloudflare config. MVP might do a simple in-memory count + periodic flush to DB for persistence of usage counts. (Given Workers may spawn in many locations, a Durable Object or centralized counter service might be needed for global accuracy – this is a technical challenge to solve in implementation). + - **Audit & Logging:** All actions on the backend (login, create server, deploy, delete) will be logged in an audit log (internal) with timestamp and user ID. This is useful for debugging and security audits. For enterprise especially, we’ll want a record of changes. The Workers can also log accesses (time, which tool was invoked) to a log store. Cloudflare Workers can use a service like **Workers Analytics Engine** or simply send log events back to our API. MVP will capture essential events (e.g. “user X called tool Y at T time”) to enable our analytics and possibly to troubleshoot issues. + +**4. Performance & Scalability:** + - The choice of Cloudflare Workers means the **MCP servers scale automatically** with incoming load – Cloudflare will run as many isolates as needed to handle concurrent SSE streams or tool requests. We need to ensure our code for each tool is efficient (but since many tools just call out to an API or perform I/O, the compute per request is small). + - Our **backend API** (for the dashboard) should also be scalable. It will see far less load than the Workers since the heavy traffic is AI agent <-> MCP server communications which go direct to Workers. The backend mainly handles user actions and periodic polling from the dashboard. We can host the backend on a scalable platform or even as a Cloudflare Worker (though likely we’ll use a traditional app server for easier development initially). + - The database should handle potentially many small writes (if logging every request for analytics). We may introduce a buffer or batching for analytics writes to not bottleneck on the DB. Using a time-series DB or an analytics service might be prudent if usage grows (future consideration). + - **Global Access:** Since Cloudflare’s network is worldwide, no additional CDN is needed – users in Europe, Americas, Asia will all have low latency connecting to their MCP endpoint. This makes using the tools feel responsive, which is critical for a good UX in AI assistants (nobody wants a tool that responds slowly). + - If any tool involves heavy processing (like large file reading), we will test that within the constraints of Workers (CPU time, memory). Cloudflare has limits (e.g. 50ms CPU time per request by default, can be higher on paid plans, and memory ~128MB by default). We should note that and possibly restrict certain tools (like “heavy data processing”) or use streaming properly so as not to hit limits. If necessary, for extremely heavy tasks we could integrate with another service or queue (beyond MVP scope). + +**5. Secure API Key Management:** + - Each user (or each of their MCP instances) will have an **API key or token** generated by us. We might generate one key per user that can access all their instances (with an instance identifier in requests), or simpler: one unique key per instance. The latter is more secure if the user wants to share one server’s access with someone without exposing others. MVP will issue one key per instance, shown in the dashboard. + - The keys will be long random strings (e.g. 32+ chars) and stored hashed in our database (for security, similar to how passwords are stored). The actual key is only shown to the user when created. If they lose it, they can regenerate a new one (which invalidates the old). + - When an AI client connects (via SSE or uses the npx client), it must provide this key. The Cloudflare Worker for that instance will have the expected hash or key in its environment (set at deploy time). It authenticates on each connection. If auth fails, it doesn’t serve the tool functions. + - Additionally, our backend API (for managing the account) will have its own authentication – since we use OAuth, we’ll issue a session token or JWT to the frontend to include in subsequent API calls. Standard web security (HTTPS, CSRF protection if needed, etc.) will be followed for the dashboard APIs. + - We will enforce that sensitive operations (like deleting an MCP server, or viewing a sensitive config value) require an authenticated session. Possibly re-auth (OAuth) if high security needed, though probably not necessary here. + - Overall, the architecture is designed such that even if one component is compromised, other user data remains safe. For example, even if someone gets the list of deployed Worker scripts, they can’t use them without keys; if someone breaches the database, user’s third-party API creds are encrypted; if someone steals a user’s dashboard session, they still can’t call the MCP tools without also having the API key, etc. Defense in depth will be applied. + +In summary, the technical architecture leverages modern serverless components to deliver a robust, scalable service. By using Cloudflare Workers for execution, we offload a lot of ops overhead and get global performance. The backend and dashboard handle the user experience and orchestration, with security measures at every step (OAuth for identity, API keys for tool access, rate limits for fairness). This setup is designed to support an MVP launch with a single developer or small team in mind, but can scale to many users and be expanded in functionality over time. + +# Go-to-Market & Viral Growth Strategy + +Launching a developer-focused micro SaaS requires careful planning to drive adoption and conversion. Our go-to-market approach will emphasize **frictionless onboarding, community engagement, and a freemium model that encourages upgrades**. We will also build virality and word-of-mouth into the product experience. Key strategies include: + +**Frictionless Onboarding:** From first impression to first success, we minimize barriers: +- **One-Click Sign Up:** Using GitHub OAuth (as described) means in two clicks a user is in the app. No lengthy forms or credit card required for the free tier. We explicitly allow users to explore without entering payment info upfront. This encourages more trial signups. +- **Instant Value (Time-to-First-Tool):** Our onboarding flow will aim to have users deploy their first MCP server within ~5 minutes of signing up. We provide guided tutorials or even an interactive setup wizard. For instance, a new user could be prompted: “What do you want to connect to your AI first? [GitHub Repo] [Slack] [Custom...]” – they pick one, and we walk them through adding that tool and hitting deploy. Achieving a quick “wow it works” moment (like seeing Claude retrieve data from their chosen source) greatly increases the chance they stick around. +- **Default Templates:** To make it even easier, we might offer a few **pre-configured server templates** on sign-up (especially if we detect their use case). E.g., “Connect your codebase to Claude” as a one-click that sets up the Git + Filesystem tools with sane defaults. The user only needs to provide a repo link or token. These templates could be showcased on the landing page and in-app, making it clear that very useful scenarios can be achieved with minimal setup. +- **Educational Content:** We will prepare short **YouTube videos, GIFs, or docs** that show how to integrate with Cursor or Claude. Possibly a 2-minute demo of someone using our service to, say, have Claude answer questions from a private Notion (via our connector). These materials will be linked in the app and shared on social media to attract interest. A smooth onboarding backed by helpful content reduces drop-offs. + +**Community and Viral Loop:** As an extension of an open-source protocol, we will tap into the existing community and foster new sharing mechanisms: +- **Integration with Anthropic/Cursor Community:** We will actively participate in forums (like Reddit, Discord, or the Cursor community forum) where MCP early adopters discuss. By solving a real pain point (hosting), we can get word-of-mouth among developers who are trying MCP locally. For example, if someone asks “How can I run MCP server in the cloud for my team?”, our solution can be the answer – leading to organic adoption. We might even collaborate with Anthropic to be listed as a resource for MCP (if they’re open to third-party services). +- **Referral Incentives:** We can implement a referral program: users who invite others (and those who join) get a bonus, such as extra free usage or a discount on upgrade. E.g., “Get 20% more free calls for 3 months for each friend who signs up with your link.” Developers are often enthusiastic to share useful new tools, especially if there’s a perk. This can drive viral growth through personal networks or developer communities. +- **Social Sharing of Achievements:** Whenever a user successfully sets up a useful integration, encourage them to share. The app could have a subtle “Share your setup” prompt – perhaps generating a sanitized summary (not leaking keys) of “I just connected Slack and GitHub to my AI using [ProductName]!” with a link. On the landing site, highlight cool use cases (“See how @devMike is using it to…”) to create buzz. +- **Marketplace & Templates (Future Viral Loop):** As mentioned in the roadmap, a marketplace of user-contributed MCP servers could be huge for virality. Users might publish interesting connectors or combinations, making the platform a go-to for discovering AI tools. While not MVP, laying the groundwork for this (and hinting it’s “coming soon”) can excite early adopters. Users who publish content are likely to share it, bringing others to the platform. + +**Logarithmic MRR Growth Strategy:** We plan for revenue to increase with both **new customer acquisition and expansion within existing customers**: +- **High Conversion Freemium:** The free tier is generous enough to attract users and let them prove the value on a small scale, but it’s intentionally capped so that any serious usage triggers an upgrade. We will monitor usage patterns – e.g., if 25% of free users hit a limit within a month, that indicates a healthy funnel to upsell. Our job is then to prompt them with timely upgrade offers (“You’re 90% of your free quota – upgrade now to avoid interruption”). Because the tool naturally becomes part of a workflow (AI assistant), users will have a strong incentive to keep it running smoothly, which encourages converting to paid before hitting a wall. This should lead to a high conversion rate from free to paid relative to typical SaaS, as long as we demonstrate clear value. +- **Expansion in Accounts:** For Pro (or higher) subscribers, we look for opportunities to increase their usage. This is aided by our pricing model’s multiple dimensions. For instance, a small team might start on Pro with one project, then realize they can use the service for another project – now they need another instance or more tools, which might bump them toward Enterprise or buying an add-on. We will reach out (or automate prompts) when usage is climbing: “Looks like your team is getting great use out of the platform! Did you know you can add another server for just $X or upgrade to Enterprise for unlimited…”. Good customer success can drive upsells. +- **Partnerships and Integrations:** We will seek partnerships with complementary platforms (for example, IDEs like Cursor or code hosting like Replit) to feature our service as “MCP hosting partner” or similar. If, say, a dev platform suggests our service for connecting their environment to AI, that funnel can bring high-value users. Partner deals might include revenue share, but more importantly they accelerate user acquisition. +- **Content Marketing & SEO:** We’ll create content (blog posts, tutorials) around keywords like “Claude MCP hosting”, “AI tool integration”, “connect [X] to LLM with MCP”. This will capture search traffic as interest in MCP grows (being an emerging tech from late 20 ([Introducing the Model Context Protocol \ Anthropic](https://www.anthropic.com/news/model-context-protocol#:~:text=The%20Model%20Context%20Protocol%20is,that%20connect%20to%20these%20servers))5】, people will seek info). By being early with content, we can establish domain authority and get consistent sign-ups through organic channels. Over time this reduces paid marketing needs and produces steady growth – a logarithmic curve as cumulative content drives compounding traffic. + +**Freemium Model to Drive High Conversion:** +Our freemium is designed such that using the product more deeply also increases its stickiness. For example, a single MCP server with one tool might be “nice to have”, but when a user configures 3-4 tools and integrates it into their daily AI workflow, it becomes an integral part of their productivity. At that point, paying a modest fee for reliability and capacity is a no-brainer. We also ensure that the paid plans have clear **added value** beyond just higher limits, like the premium connectors and better support. This way, users don’t feel “forced” to pay – they feel they *want* to pay to get the full experience. High conversion is further supported by: + +- **Trust and Reliability:** From the get-go, we’ll emphasize that this is a reliable hosted solution (perhaps showcasing uptime or testimonials). Users are more willing to pay if they trust the service for mission-critical use. Any outage or major bug could hurt that trust, so part of go-to-market is ensuring the MVP is stable and communicating transparently about status (maybe a status page). +- **Customer Feedback Loop:** Early on, we will personally reach out to active free users to ask about their experience, and what would make it worth paying for. This not only builds goodwill (they feel heard) but also gives us insight to refine pricing or features. Satisfied early users can become evangelists. +- **Conversion CTAs:** Within the app, strategic call-to-action prompts will remind users of the benefits of upgrading. For instance, on the analytics page we might show “On Pro, you’d also see detailed per-tool usage” or if they try to add a premium tool on free, it’ll show a lock icon and “Available on Pro plan”. These nudges, without being too naggy, keep the upgrade option in view. + +By combining a smooth onboarding (to drive sign-ups), community/viral hooks (to multiply sign-ups), and a thoughtfully crafted freemium funnel (to turn sign-ups into revenue), we aim to grow MRR steadily. The strategy focuses on **developer happiness and empowerment** – if we make users feel empowered by the product, they will naturally promote it and invest in it. As usage of large-language-model tools like Claude grows (and MCP as a standard grows), our service should ride that wave and capture a significant user base by being the easiest way to deploy MCP. Success will be measured by conversion rates, retention rates, and referrals, all of which this strategy seeks to maximize. + +# Future Roadmap + +After a successful MVP launch, we have a broad vision for expanding the product’s capabilities and market reach. We will prioritize features that enhance the platform’s usefulness, create network effects, and cater to more demanding use cases (enterprise, advanced developers). Below are key items on our future roadmap: + +**Marketplace for User-Created MCP Servers:** One of the most exciting opportunities is to turn the platform into not just a host, but a hub for MCP tools. In the future, we plan to allow users to **publish their own MCP server configurations or custom tools** to a marketplace/discoverability section. This could work as follows: advanced users can upload code for a new tool (or entire MCP server) that they developed – for example, a connector to a less common SaaS or a specialized data source. We would sandbox and review these for security, then allow them to be one-click installable by other users. This creates a **network effect**: the more tools the community contributes, the more valuable the platform becomes to every user. We might also introduce a rating/review system so the best community-contributed tools rise to the top. In terms of monetization, we could let creators offer tools for free or for a price (taking a commission), effectively opening a **developer marketplace** similar to app stores. This not only drives viral growth (people share their creations) as mentioned, but also can generate additional revenue and engagement. This is a longer-term project as it involves vetting third-party code and possibly providing sandboxing beyond what Cloudflare offers, but even a curated “recipes library” of configurations could be a stepping stone (e.g. users sharing JSON configs for certain combinations which others can import). + +**Expansion of Premium Tools Library:** We will continuously grow the built-in library of tools, especially focusing on **high-value integrations**. Some examples on the roadmap: connectors for enterprise software like Salesforce, databases like Oracle or Microsoft SQL (for legacy systems), knowledge bases like Confluence or SharePoint, or even hardware/IoT integrations for specialized use. Many of these may not be available in the open-source MCP repo or might not be trivial for users to set up by themselves. By providing them, we increase the reasons users come to our platform. Premium tools might also include *composite tools* – for instance, a “Quick Analytics” tool that, behind the scenes, uses a combination of database + spreadsheet logic to let an AI do data analysis. These complex scenarios can be packaged as easy-to-add tools on our platform, which users would otherwise struggle to implement solo. Each new premium tool can be a marketing opportunity (“Now supporting X!”) to attract users who specifically need that. Our development team might partner with API providers (say, partner with Notion or Airtable to make an MCP tool for their product). Over time, our library could become the most comprehensive collection of AI integration tools available. We will also update existing tools to improve them (e.g. if Slack releases new APIs, we update our Slack connector with more features) – ensuring our paying customers always have cutting-edge capabilities. + +**Enterprise-Focused Offerings:** As we gain traction, likely larger companies will show interest. Beyond the Enterprise plan features discussed in pricing, there are specific roadmap items to better serve enterprise clients: +- **Team Administration:** Develop features for org admins to manage multiple users under one billing. This includes creating organization accounts where several developers can collaborate on MCP servers, share access to instances, and have role-based permissions. For example, an admin can create an MCP server and a teammate can use it or view logs. This is crucial for adoption in a team setting and for enterprises that want to manage everything centrally. +- **Single Sign-On (SSO):** Implement SSO integration with providers like Okta, Azure AD, or Google Workspace. Many enterprises require that employees authenticate through their centralized system. This not only smooths login for users but also satisfies security/compliance requirements. +- **On-Premise/Private Deployment:** While our core offering is cloud (multi-tenant), some enterprise customers might demand a self-hosted version (due to strict data policies). A future option is a **self-hosted appliance** or a dedicated instance of our service on their cloud. This could be delivered as a container or as a managed single-tenant deployment (perhaps using their Cloudflare account or Workers on their behalf). It’s a non-trivial addition, but it could unlock contracts with big clients (e.g., a bank that wants to run everything in-house). We would likely only pursue this for significant deals, and it would come with premium pricing. +- **Compliance and Certifications:** Over time we will work on obtaining relevant certifications (SOC 2, ISO 27001, etc.) to assure enterprises that our service meets security standards. This isn’t a feature per se, but part of the roadmap to being enterprise-ready by addressing legal/infosec hurdles that large companies have for vendors. +- **Enhanced Analytics & Reporting:** Enterprises might want detailed reports of usage, perhaps by user or project, and integration with their monitoring tools. We could build an **export or API** for usage data, so enterprises can pull MCP usage into their internal dashboards. Also, audit logs of who accessed what tool could be provided for compliance tracking. + +**AI Model Integrations and Evolution:** The AI landscape is fast-moving. We will keep an eye on how MCP itself evolves and how other AI systems might integrate: +- If OpenAI or other LLM providers start supporting a similar plugin protocol, we could adapt our platform to serve those as well. For example, if a future version of ChatGPT allowed external tools (like plugins, but standardized), our service could host those connectors. Because MCP is open, it might become widely adopted, or it could converge with other standards. Our architecture being flexible means we can tweak the interfaces or add new output formats as needed (e.g., maybe a direct integration with LangChain or other agent frameworks as a client). +- We might also incorporate **Function Calling** as a concept: some AI models prefer function call interfaces to tools. If relevant, we could present our tools to models via a function-calling gateway. This is speculative, but essentially staying flexible to serve as the “glue” between any AI assistant and any data/tool. +- **Performance and ML features:** In the future, we might integrate more AI logic into the platform – e.g., optimizing how context is fetched (maybe pre-fetching or caching results from tools to answer queries faster). We could build a layer that intelligently routes tool requests or caches common queries (helpful for performance-sensitive enterprise use). + +**Improved User Experience and Features:** As we gather user feedback post-MVP, we’ll continuously improve the core experience: +- A likely request is **more robust debugging tools** for MCP servers. We could enhance the “log viewer” into an interactive console where users can simulate tool calls or inspect the internal state of their server. This would help developers test their configurations. Possibly integrate with the open-source MCP Inspector to ([Introduction - Model Context Protocol](https://modelcontextprotocol.io/introduction#:~:text=Building%20MCP%20with%20LLMs%20Learn,with%20our%20interactive%20debugging%20tool))7】 in our UI. +- Another area is **mobile access or notifications** – e.g., an admin wants to know if their server is down or hit an error. We might create a simple mobile-friendly dashboard or push notifications for certain events (like “Your instance hit an error, click to view logs”). +- **Internationalization**: If the tool gains global traction, having the dashboard in multiple languages could be on the roadmap to capture non-English speaking developer communities (especially since AI is worldwide). +- **UI/UX polish**: Post-MVP, invest in making the UI more intuitive based on analytics (where do users drop off in the flow?) and feedback. This includes everything from better forms for config to perhaps a “dark mode” for the developer-friendly vibe. + +**Scalability and Technical Enhancements:** As usage grows, we’ll revisit the architecture to ensure it scales cost-effectively: +- We might develop a **smart deployment manager** that batches or optimizes Cloudflare Worker usage. For example, if thousands of instances are running, we’ll work closely with Cloudflare or consider alternate execution methods to keep costs manageable. Cloudflare may introduce new features (like a higher-level MCP service) – if so, we’ll integrate rather than compete (maybe our service becomes a management layer on top of such features). +- Implementation of **Durable Objects or Shared Processes:** If many instances use the same tool, perhaps we can load one copy of code and serve multiple, to save memory. These are low-level optimizations that might reduce per-instance overhead, allowing more users on the platform cheaply. The roadmap includes continuous performance profiling and cost analysis to refine the backend. +- **AI/Automation for Support:** Eventually, we could use an AI agent (perhaps powered by our own MCP setup!) to help support and guide users in-app. For instance, a chatbot that can answer “Why is my server not responding?” by checking their logs or configuration. This leverages our love for AI within the product and provides quicker help. + +</context> + +<PRD> +1. Overview +Project Name: MCP SaaS MVP +Objective: Provide a web-based platform where users can host customizable Model Context Protocol (MCP) servers in the cloud (on Cloudflare). Users can sign up with GitHub OAuth, pick from a library of “tools,” configure their own MCP server, and receive a secure SSE endpoint or npx command to integrate with AI assistants (e.g., Claude, Cursor IDE). The platform will track usage, enforce plan limits, and provide analytics. + +1.1 Key Goals +Simplify MCP Hosting: Users can deploy MCP servers without managing infrastructure. +Curated Tools Library: Provide a built-in catalog of connectors (Slack, GitHub, Postgres, etc.). +Plan Tiers: Freemium approach with usage-based or tool-based constraints. +Analytics: Users can see basic usage stats (call counts) in a web dashboard. +Scalability: Cloudflare Workers for hosting, scaling automatically on demand. +2. Core Features +User Authentication + +GitHub OAuth sign-in. +Store user profiles (plan tier, usage) in a Cloudflare D1 database. +MCP Server Creation + +Create/Edit servers in a dashboard UI. +Assign a unique API key and SSE endpoint. +Manage multiple tools per server, each with user-provided config (API tokens, etc.). +Deployment + +On creation or update, compile a Worker script that includes selected tools. +Programmatically deploy via Cloudflare’s API. +Provide user-friendly output: SSE URL, or an npx command for local bridging. +Tool Integrations (MVP) + +Slack (send/read messages) +GitHub (list repos/files) +Possibly a DB connector (Postgres) or a minimal placeholders for future expansions. +Usage Analytics + +Log each tool usage request for a given MCP server. +Show monthly call counts in a simple chart or numeric table. +Plan Enforcement + +Free Tier: e.g., 1 server, 2 tools, limited monthly calls. +Paid Tier: higher limits, premium tools, more calls. +Enforcement logic blocks creation if user tries to exceed their plan constraints. +Meta-Development Scripts + +A set of Node.js scripts to generate tasks, update them based on new prompts, and produce individual files for AI-driven development. (See “tasks.json” below.) +3. Technical Architecture +Frontend (React) + +Hosted on Cloudflare Pages or as static assets in a Worker. +Communicates with a backend (Wrangler-based or separate Worker) via REST/GraphQL for server CRUD, usage analytics, etc. +Backend + +Cloudflare Workers (Node.js environment). +Handles user OAuth, CRUD for MCP servers, triggers Worker deployment for each server instance. +Database: Cloudflare D1 + +users (id, github_id, plan, created_at, etc.) +mcp_servers (id, user_id, name, api_key, config, etc.) +server_tools (server_id, tool_name, config_json, etc.) +usage_logs (server_id, timestamp, usage_count, etc.) +Worker Deployment + +Each MCP server is either a dedicated Worker or a single multi-tenant Worker that checks the server’s config on requests. +SSE endpoint (/sse) implements the MCP protocol for AI assistants. +Security + +OAuth tokens and user credentials stored securely in D1. +Each MCP server uses a unique API key. +Rate limits or usage checks at Worker runtime. +4. MVP Scope vs. Future Enhancements +In Scope (MVP) + +Basic Slack and GitHub tool connectors. +Simple plan limits (free vs. paid placeholders). +Basic usage analytics. +Minimal admin features (e.g., toggle a user to paid in DB). +Dashboard to create/edit servers and see usage stats. +Out of Scope (Future) + +Payment processing integration (Stripe). +Marketplace for community-contributed MCP servers. +Advanced enterprise features (SSO, compliance). +Team management (multiple logins per org). +5. Go-To-Market Strategy (Brief) +Freemium: Users can sign up instantly with GitHub OAuth, get a free tier. +Upsell: Show usage limits and prompt users to upgrade upon hitting them. +Developer Community: Provide easy instructions for integration with Claude or Cursor, emphasize no-code/low-code setup. +Future: Offer enterprise-tier with custom usage agreements. +6. Development Plan (High-Level) +Phase A: Project scaffolding (repo, Wrangler, D1 setup). +Phase B: User authentication (GitHub OAuth) and plan logic. +Phase C: MCP server CRUD, generating API keys. +Phase D: Tool library management and UI to add/remove tools. +Phase E: Worker deployment logic, SSE endpoint. +Phase F: Implement Slack, GitHub tools with usage logging. +Phase G: Analytics & usage display in dashboard. +Phase H: Final polish, plan enforcement, better UI. +Phase I: Meta scripts for tasks and continuous AI-driven dev. + +</PRD> \ No newline at end of file diff --git a/scripts/prepare-package.js b/scripts/prepare-package.js new file mode 100755 index 00000000..306ce5cf --- /dev/null +++ b/scripts/prepare-package.js @@ -0,0 +1,148 @@ +#!/usr/bin/env node + +/** + * This script prepares the package for publication to NPM. + * It ensures all necessary files are included and properly configured. + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// Define colors for console output +const COLORS = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m' +}; + +// Log function with color support +function log(level, ...args) { + const prefix = { + info: `${COLORS.blue}[INFO]${COLORS.reset}`, + warn: `${COLORS.yellow}[WARN]${COLORS.reset}`, + error: `${COLORS.red}[ERROR]${COLORS.reset}`, + success: `${COLORS.green}[SUCCESS]${COLORS.reset}` + }[level.toLowerCase()]; + + console.log(prefix, ...args); +} + +// Function to check if a file exists +function fileExists(filePath) { + return fs.existsSync(filePath); +} + +// Function to ensure a file is executable +function ensureExecutable(filePath) { + try { + fs.chmodSync(filePath, '755'); + log('info', `Made ${filePath} executable`); + } catch (error) { + log('error', `Failed to make ${filePath} executable:`, error.message); + return false; + } + return true; +} + +// Main function to prepare the package +function preparePackage() { + const rootDir = path.join(__dirname, '..'); + log('info', `Preparing package in ${rootDir}`); + + // Check for required files + const requiredFiles = [ + 'package.json', + 'README.md', + 'index.js', + 'scripts/init.js', + 'scripts/dev.js' + ]; + + let allFilesExist = true; + for (const file of requiredFiles) { + const filePath = path.join(rootDir, file); + if (!fileExists(filePath)) { + log('error', `Required file ${file} does not exist`); + allFilesExist = false; + } + } + + if (!allFilesExist) { + log('error', 'Some required files are missing. Package preparation failed.'); + process.exit(1); + } + + // Ensure scripts are executable + const executableScripts = [ + 'scripts/init.js', + 'scripts/dev.js' + ]; + + let allScriptsExecutable = true; + for (const script of executableScripts) { + const scriptPath = path.join(rootDir, script); + if (!ensureExecutable(scriptPath)) { + allScriptsExecutable = false; + } + } + + if (!allScriptsExecutable) { + log('warn', 'Some scripts could not be made executable. This may cause issues.'); + } + + // Check templates directory + const templatesDir = path.join(rootDir, 'templates'); + if (!fileExists(templatesDir)) { + log('error', 'Templates directory does not exist'); + process.exit(1); + } + + // Check template files + const requiredTemplates = [ + 'README.md', + 'env.example', + 'gitignore', + 'dev_workflow.mdc', + 'dev.js', + 'scripts_README.md', + 'example_prd.txt' + ]; + + let allTemplatesExist = true; + for (const template of requiredTemplates) { + const templatePath = path.join(templatesDir, template); + if (!fileExists(templatePath)) { + log('error', `Required template ${template} does not exist`); + allTemplatesExist = false; + } + } + + if (!allTemplatesExist) { + log('error', 'Some required templates are missing. Package preparation failed.'); + process.exit(1); + } + + // Run npm pack to test package creation + try { + log('info', 'Running npm pack to test package creation...'); + const output = execSync('npm pack --dry-run', { cwd: rootDir }).toString(); + log('info', output); + } catch (error) { + log('error', 'Failed to run npm pack:', error.message); + process.exit(1); + } + + log('success', 'Package preparation completed successfully!'); + log('info', 'You can now publish the package with:'); + log('info', ' npm publish'); +} + +// Run the preparation +preparePackage(); \ No newline at end of file diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 00000000..75304ee9 --- /dev/null +++ b/templates/README.md @@ -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}} \ No newline at end of file diff --git a/templates/dev.js b/templates/dev.js new file mode 100644 index 00000000..0004e47b --- /dev/null +++ b/templates/dev.js @@ -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); +}); \ No newline at end of file diff --git a/templates/dev_workflow.mdc b/templates/dev_workflow.mdc new file mode 100644 index 00000000..5b4af08a --- /dev/null +++ b/templates/dev_workflow.mdc @@ -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" diff --git a/templates/env.example b/templates/env.example new file mode 100644 index 00000000..8cca52cc --- /dev/null +++ b/templates/env.example @@ -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}} \ No newline at end of file diff --git a/templates/example_prd.txt b/templates/example_prd.txt new file mode 100644 index 00000000..806bf1a3 --- /dev/null +++ b/templates/example_prd.txt @@ -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> \ No newline at end of file diff --git a/templates/gitignore b/templates/gitignore new file mode 100644 index 00000000..bea8734a --- /dev/null +++ b/templates/gitignore @@ -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/ \ No newline at end of file diff --git a/templates/scripts_README.md b/templates/scripts_README.md new file mode 100644 index 00000000..32c0be59 --- /dev/null +++ b/templates/scripts_README.md @@ -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.