diff --git a/.npmignore b/.npmignore index 8e00e3d..723879e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,19 +1,19 @@ -# Exclude user-generated spec files -specs/ -specs--completed/ - -# Exclude development files -.husky/ -.git/ -.gitignore -CLAUDE.md - -# Exclude generated files (installer generates dist/ dynamically) -dist/ - -# Exclude dependencies (installer will run npm install if needed) -node_modules/ -package-lock.json -plan2code-loop/node_modules/ -plan2code-loop/package-lock.json -plan2code-loop/dist/ +# Exclude user-generated spec files +specs/ +specs--completed/ + +# Exclude development files +.husky/ +.git/ +.gitignore +CLAUDE.md + +# Exclude generated files (installer generates dist/ dynamically) +dist/ + +# Exclude dependencies (installer will run npm install if needed) +node_modules/ +package-lock.json +plan2code-loop/node_modules/ +plan2code-loop/package-lock.json +plan2code-loop/dist/ diff --git a/AGENTS.md b/AGENTS.md index 9a7d568..105a065 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,27 +100,9 @@ The LLM must output one of these formats: # Install dev dependencies (sets up husky pre-commit hooks) npm install -# Run the interactive installer +# Run the interactive installer (always interactive — any CLI args are silently ignored) node install.js -# Install to specific platform only -node install.js --platform claude -node install.js --platform cursor -node install.js --platform copilot -node install.js --platform continue -node install.js --platform windsurf -node install.js --platform codeium -node install.js --platform vscode-copilot - -# Preview changes without installing -node install.js --dry-run - -# Show local (per-project) installation instructions -node install.js --local - -# Uninstall all Plan2Code files -node install.js --uninstall - # Plan2Code Loop cd plan2code-loop && npm install # First time setup cd plan2code-loop && npm run build # Build the CLI @@ -128,13 +110,22 @@ cd plan2code-loop && npm run build # Build the CLI ### Installer Menu Options +**Main menu:** + +| Option | Action | +|--------|--------| +| `I` | Install Plan2Code for all platforms + loop CLI | +| `U` | Uninstall Plan2Code files + loop CLI (confirmation required) | +| `C` | Open CUSTOM sub-menu | +| `Q` | Quit | + +**CUSTOM sub-menu (`C`):** + | Option | Action | |--------|--------| -| `A` | Install to ALL platforms + build & link loop CLI | -| `O` | Build & link plan2code-loop CLI only | -| `U` | Uninstall prompts from all platforms + unlink loop CLI | | `L` | Show local (per-project) install instructions | -| `1-7` | Install to specific platform | +| `O` | Install plan2code-loop CLI only | +| `Q` | Return to main menu | ## How the Installer Works @@ -145,15 +136,19 @@ cd plan2code-loop && npm run build # Build the CLI ### Platform-Specific File Formats -| Platform | Extension | Header | -|----------|-----------|--------| -| Claude Code | `.md` | None | -| Cursor | `.md` | None | -| Copilot CLI | `.md` | YAML frontmatter | -| Continue | `.prompt.md` | YAML frontmatter | -| Windsurf | `.md` | YAML frontmatter | -| VS Code Copilot | `.prompt.md` | YAML frontmatter | -| Codeium | `.md` | YAML frontmatter | +| Platform | Extension / File | Local Dir | Global Dir | Header | +|----------|-----------------|-----------|------------|--------| +| Claude Code | `.md` | — | — | None | +| Cursor | `.md` | — | — | None | +| Copilot CLI | `.md` | — | — | YAML frontmatter | +| Continue | `.prompt.md` | — | — | YAML frontmatter | +| Windsurf | `.md` | — | — | YAML frontmatter | +| VS Code Copilot | `.prompt.md` | — | — | YAML frontmatter | +| Codeium | `.md` | — | — | YAML frontmatter | +| Claude Code (Skills) | `SKILL.md` in subdir | `.claude/skills//` | `~/.claude/skills//` | YAML frontmatter + `disable-model-invocation: true` | +| Agent Skills (Amp · Gemini CLI · OpenCode) | `SKILL.md` in subdir | `.agents/skills//` | `~/.agents/skills//` | YAML frontmatter (no disable flag) | +| Crush | `SKILL.md` in subdir | — (global only) | `~/.config/crush/skills//` (Unix) / `%LOCALAPPDATA%\crush\skills\\` (Windows) | YAML frontmatter | +| Gemini CLI (TOML) | `.toml` | `.gemini/commands/` | `~/.gemini/commands/` | None (TOML fields: `description`, `prompt`) | ## Naming Convention @@ -184,7 +179,7 @@ When modifying workflow prompts in `src/`: ## Gotchas/Pitfalls -- **Version sync:** When adding a new version to `CHANGELOG.md`, also update `version.json` to match. The installer displays the version from `version.json` in its header. +- **Version sync:** When adding a new version to `CHANGELOG.md`, also update `version.json` and `package.json` to match. The installer displays the version from `version.json` in its header. - **Loop `.gitignore` setup:** `ensureGitignore()` runs at startup in `Controller.run()` as a pre-flight step, not just inside `createTaskCommit()`. This is critical for phase mode where the Node controller doesn't handle commits — without it, `git add -A` would stage spec files. - **Workflow file character limit:** All `src/plan2code-*.md` files must be ≤ 11,000 characters. A husky pre-commit hook enforces this. The 11,000 limit leaves buffer for platform-specific YAML headers (106-142 chars) to stay under Windsurf's 12,000 char limit. @@ -199,11 +194,15 @@ When modifying workflow prompts in `src/`: ## Mascot -The project has a mascot - an ASCII art robot that appears in installer output and workflow prompts. Mascot variants are defined in `MASCOT` constant in `install.js` and appear in workflow markdown files. +The project has a mascot called "Planny" - an ASCII art robot that appears in installer output and workflow prompts. Mascot variants are defined in `MASCOT` constant in `install.js` and appear in workflow markdown files. ``` + o o + ╲ ╱ ╭───╮ │ ● │ │ ◡ │ + ├───┤ + │ · │ ╰───╯ ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 239c046..ac91d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,42 @@ All notable changes to Plan2Code will be documented in this file. +## v1.7.0 + +### ✨ Added + +- **4 new platform targets** — Gemini CLI, Crush, Amp, and OpenCode now supported + - Gemini CLI installs as TOML commands (`.gemini/commands/plan2code-*.toml`) + - Crush installs as skill subdirs (`~/.config/crush/skills/` on Unix, `%LOCALAPPDATA%\crush\skills\` on Windows) + - Amp and OpenCode covered via shared Agent Skills target (`.agents/skills/`) +- **Claude Code Skills format** — Migrated from flat `.claude/commands/*.md` to `.claude/skills//SKILL.md` with `disable-model-invocation: true` frontmatter +- **Agent Skills cross-tool target** — Single `.agents/skills/` install covers Amp, Gemini CLI, and OpenCode simultaneously +- **Legacy cleanup** — Old `.claude/commands/plan2code-*.md` files automatically removed on install and uninstall +- **TOML generation** — New `generateTomlContent()` produces Gemini CLI command files using TOML literal multi-line strings + +### 🔧 Changed + +- `AGENTS.md` Platform-Specific File Formats table expanded to 5 columns with 4 new platform rows +- `docs/index.html` hero section updated with 4 new platform pills +- `README.md` Supported Platforms list updated with 4 new platforms + +## v1.6.2 + +### 🔧 Changed + +- **Installer menu simplified** — Replaced the 7-platform picker with a clean 4-option menu (I/U/C/Q) + - `I` — Install Plan2Code for all platforms + loop CLI + - `U` — Uninstall (with confirmation prompt) + - `C` — CUSTOM sub-menu: `L` (local install instructions), `O` (loop CLI only), `Q` (back) + - `Q` — Quit + - Any CLI arguments (e.g. `--dry-run`, `--platform`) are now silently ignored; installer always runs interactively +- **README installation section** — npx install method promoted to primary recommended install path; updated menu example + +### 🗑️ Removed + +- CLI flags `--dry-run`, `--platform`, `--local`, `--uninstall`, `--help`, `--loop`, `--uninstall-loop` (all removed; installer is always interactive) +- `displayHelp()` function removed from `install.js` + ## v1.6.1 ### ✨ Added diff --git a/QUICK-REFERENCE.md b/QUICK-REFERENCE.md index c8a70cc..38f3cf8 100644 --- a/QUICK-REFERENCE.md +++ b/QUICK-REFERENCE.md @@ -2,8 +2,8 @@ ## Commands -| Step | Command | Input | Output | -| ------ | ----------------------------- | --------------- | ----------------------------------- | +| Step | Command | Input | Output | +| ------ | ------------------------------- | --------------- | ----------------------------------- | | Init | /plan2code---init | None | AGENTS.md file | | Update | /plan2code---init-update | AGENTS.md | Updated AGENTS.md | | 0 | /plan2code---quick-task | Requirements | Conversational plan | @@ -60,10 +60,10 @@ When phases have no file conflicts or dependencies, they can run simultaneously: | ---------------------- | ------------------------------------------------- | | Lost context mid-phase | Attach spec files, say "resume from Task X.Y" | | Wrong phase started | Say "abort", start correct phase | -| Need to change plan | Use `/plan2code-1b--revise-plan` | +| Need to change plan | Use `/plan2code-1b--revise-plan` | | Multiple spec folders | Specify which: "Continue with specs/user-auth/" | -| Need AGENTS.md file | Use `/plan2code---init` to generate one | -| Update AGENTS.md | Use `/plan2code---init-update` after sessions | +| Need AGENTS.md file | Use `/plan2code---init` to generate one | +| Update AGENTS.md | Use `/plan2code---init-update` after sessions | | Run phases in parallel | Check Parallel Execution Groups in overview.md | ## Workflow Decision diff --git a/README.md b/README.md index 7bfe079..92d71fb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A structured 4-step workflow for developing features and projects with AI assistance. This methodology emphasizes thorough planning before implementation, ensuring well-documented, maintainable code. -Plan2Code Workflow +Plan2Code Workflow ## Overview @@ -15,16 +15,16 @@ A structured 4-step workflow for developing features and projects with AI assist New Chat New Chat New Chat (per phase) New Chat ``` -| Command | Use When | -|---------|----------| -| `/plan2code---init` | Generate AGENTS.md file for new/existing projects | -| `/plan2code---init-update` | Update AGENTS.md with new learnings from coding sessions | -| `/plan2code---quick-task` | Small, quick tasks that don't need full workflow | -| `/plan2code-1--plan` | Starting a new feature (full planning) | -| `/plan2code-1b--revise-plan` | Requirements change mid-implementation | -| `/plan2code-2--document` | After planning, create implementation specs | -| `/plan2code-3--implement` | Execute implementation (one phase per conversation) | -| `/plan2code-4--finalize` | All phases complete, ready to archive | +| Command | When to Use | +|----------------------------------|----------------------------------------------------------| +| `/plan2code---init` | Generate AGENTS.md file for new/existing projects | +| `/plan2code---init-update` | Update AGENTS.md with new learnings from coding sessions | +| `/plan2code---quick-task` | Small, quick tasks that don't need full workflow | +| `/plan2code-1--plan` | Starting a new feature (full planning) | +| `/plan2code-1b--revise-plan` | Requirements change mid-implementation | +| `/plan2code-2--document` | After planning, create implementation specs | +| `/plan2code-3--implement` | Execute implementation (one phase per conversation) | +| `/plan2code-4--finalize` | All phases complete, ready to archive | **Key Rules:** - Start NEW conversation for each step (and each implementation phase) @@ -40,13 +40,13 @@ See [QUICK-REFERENCE.md](QUICK-REFERENCE.md) for full reference card. Plan2Code includes an interactive installer that generates and installs workflow files for all major AI coding assistants. - + ### Prerequisites The install script requires **Node.js** (v14 or later). If you don't have Node.js installed: -1. Download from [nodejs.org](https://nodejs.org/) +1. Download from [nodejs.org](https://nodejs.org/) (recommended) 2. Or use a package manager: - **macOS:** `brew install node` - **Windows:** `winget install OpenJS.NodeJS` or `choco install nodejs` @@ -61,8 +61,28 @@ The install script requires **Node.js** (v14 or later). If you don't have Node.j - Codeium (IntelliJ) - GitHub Copilot CLI - VS Code GitHub Copilot +- Gemini CLI +- Crush +- Amp +- OpenCode -### Quick Start +### Install via npx (Recommended — No Clone Required) + +Run the interactive installer directly using `npx` with your preferred GitHub authentication method: + +**If you use SSH keys:** +```bash +npx git+ssh://git@github.com/jparkerweb/plan2code.git +``` + +**If you use HTTPS authentication:** +```bash +npx git+https://github.com/jparkerweb/plan2code.git +``` + +This downloads the installer to a temporary location, runs it, installs the workflow files to your machine, and cleans up automatically. The installed workflows remain on your system and work independently. To update or reinstall, simply run the command again. + +### Standard Installation (Clone Method) #### No Clone Required @@ -84,7 +104,7 @@ This downloads the installer to a temporary location, runs it, installs the work ```bash # Clone the repository -git clone https://github.com/plan/plan2code.git +git clone https://github.com/jparkerweb/plan2code.git cd plan2code # Run the interactive installer @@ -94,30 +114,21 @@ node install.js npm install ``` -The installer will display an interactive menu: +The installer displays an interactive menu: ``` -Available platforms: +╔════════════════════════════════════════════════════════════════╗ +║ INSTALL PLAN2CODE ║ +╠════════════════════════════════════════════════════════════════╣ +║ I. INSTALL Install Plan2Code for all platforms ║ +║ U. UNINSTALL Remove Plan2Code files ║ +║ C. CUSTOM Advanced options ║ +║ Q. QUIT Exit ║ +╚════════════════════════════════════════════════════════════════╝ - 1. Claude Code (~/.claude/commands/) - 2. Copilot CLI (~/.copilot/agents/) - 3. Cursor (~/.cursor/commands/) - 4. Continue (~/.continue/prompts/) - 5. Windsurf (~/.codeium/windsurf/global_workflows/) - 6. Codeium (IJ) (~/.codeium/global_workflows/) - 7. VS Code Copilot (%APPDATA%\Code\User\prompts\) - - A. Install ALL platforms + loop CLI - O. Build/link plan2code-loop CLI only - L. Show local (project) install instructions - U. Uninstall Plan2Code files + unlink loop CLI - Q. Quit - -Enter choice (1-7, A, O, L, U, Q, or comma-separated like 1,3,5): +SELECT OPTION (I, U, C, Q) [I]: ``` -For per-project installation or additional options, run `node install.js --help`. - --- ## Important: Start Fresh Conversations @@ -405,10 +416,10 @@ For hands-off implementation, Plan2Code includes an optional autonomous loop CLI # From the plan2code root directory: # Option 1: Install everything (recommended) -node install.js # Select option A +node install.js # Select I at the menu # Option 2: Install loop only -node install.js # Select option O +node install.js # Select C, then O at the menu ``` ### Using the Loop diff --git a/install.js b/install.js index 7c12c81..d44db59 100644 --- a/install.js +++ b/install.js @@ -13,15 +13,7 @@ * DESCRIPTION: * Install Plan2Code prompts to user's home directory for global use. * Generates distribution files on-the-fly from source prompts in src/. - * - * OPTIONS: - * (no options) INTERACTIVE - Select platforms from menu - * --dry-run PREVIEW - Show what would be installed without changes - * --platform X TARGETED - Install to specific platform only - * --local LOCAL - Show instructions for project-level installation - * --uninstall REMOVE - Remove installed files - * --help HELP - Display usage information - * + * Always runs interactively — any CLI arguments are silently ignored. */ const fs = require('fs'); @@ -88,25 +80,37 @@ const SYMBOLS = { const MASCOT = { // Full mascot for headers full: [ + ' o o ', + ' ╲ ╱ ', ' ╭───╮ ', ' │ ● │ ', ' │ ◡ │ ', + ' ├───┤ ', + ' │ · │ ', ' ╰───╯ ', ], // Mini mascot for inline use mini: '(◉‿◉)', // Waving mascot for greetings wave: [ + ' o o ', + ' ╲ ╱ /', ' ╭───╮ ', ' │ ● │ ', ' │ ◡ │ ', + ' ├───┤ ', + ' │ · │ ', ' ╰───╯ ', ], // Thinking mascot for prompts thinking: [ + ' o o ', + ' ╲ ╱ ?', ' ╭───╮ ', - ' │ ● │ ?', + ' │ ● │ ', ' │ ~ │ ', + ' ├───┤ ', + ' │ · │ ', ' ╰───╯ ', ], }; @@ -191,6 +195,30 @@ function generateFilename(prompt, extension = '.md') { return `plan2code-${prompt.stepNumber}--${prompt.name}${extension}`; } +// Helper function to generate skill name (normalizes double hyphens to single) +function generateSkillName(prompt) { + return generateFilename(prompt, '').replace(/--+/g, '-'); +} + +// Helper function to generate YAML frontmatter for SKILL.md files +function generateSkillHeader(prompt, disableModelInvocation = false) { + const lines = [ + '---', + `name: ${generateSkillName(prompt)}`, + `description: "Plan2Code ${prompt.stepNumber === 'init' ? 'Init' : `Step ${prompt.stepNumber}`}: ${prompt.displayName} - user-initiated workflow step. Do not invoke autonomously."`, + ]; + if (disableModelInvocation) lines.push('disable-model-invocation: true'); + lines.push('---'); + return lines.join('\n'); +} + +// Helper function to generate complete .toml file content for Gemini CLI +function generateTomlContent(prompt, sourceContent) { + const stepLabel = prompt.stepNumber === 'init' ? 'Init' : `Step ${prompt.stepNumber}`; + const desc = `Plan2Code ${stepLabel}: ${prompt.displayName} - user-initiated ${prompt.description || 'workflow step'}`; + return `description = "${desc}"\nprompt = '''\n${sourceContent}\n'''\n`; +} + // Destination configurations for project-level installation (local) const LOCAL_DESTINATIONS = [ { @@ -276,6 +304,24 @@ const LOCAL_DESTINATIONS = [ `description: "Plan2Code ${prompt.stepNumber === 'init' ? 'Init' : `Step ${prompt.stepNumber}`}: ${prompt.displayName} - ${prompt.description}"`, '---' ].join('\n') + }, + { + name: 'Claude Code Skills', + dir: '.claude/skills', + type: 'skill', + header: (prompt) => generateSkillHeader(prompt, true), + }, + { + name: 'Agent Skills (Amp · Gemini CLI · OpenCode)', + dir: '.agents/skills', + type: 'skill', + header: (prompt) => generateSkillHeader(prompt, false), + }, + { + name: 'Gemini CLI', + dir: '.gemini/commands', + type: 'toml', + filePattern: (prompt) => `${generateFilename(prompt, '')}.toml`, } ]; @@ -344,6 +390,30 @@ const GLOBAL_DESTINATIONS = [ `description: "Plan2Code ${prompt.stepNumber === 'init' ? 'Init' : `Step ${prompt.stepNumber}`}: ${prompt.displayName} - ${prompt.description}"`, '---' ].join('\n') + }, + { + name: 'Claude Code Skills', + dir: '.claude/skills', + type: 'skill', + header: (prompt) => generateSkillHeader(prompt, true), + }, + { + name: 'Agent Skills (Amp · Gemini CLI · OpenCode)', + dir: '.agents/skills', + type: 'skill', + header: (prompt) => generateSkillHeader(prompt, false), + }, + { + name: 'Crush', + dir: 'crush-skills', + type: 'skill', + header: (prompt) => generateSkillHeader(prompt, false), + }, + { + name: 'Gemini CLI', + dir: '.gemini/commands', + type: 'toml', + filePattern: (prompt) => `${generateFilename(prompt, '')}.toml`, } ]; @@ -362,6 +432,26 @@ function getVSCodeCopilotDir() { } } +// Helper function to get Crush skills directory based on platform +function getCrushSkillsDir() { + const homedir = os.homedir(); + if (process.platform === 'win32') { + const localAppData = process.env.LOCALAPPDATA || path.join(homedir, 'AppData', 'Local'); + return path.join(localAppData, 'crush', 'skills'); + } + return path.join(homedir, '.config', 'crush', 'skills'); +} + +// Helper function to get Agent Skills global directory based on platform +function getAgentSkillsDir() { + const homedir = os.homedir(); + if (process.platform === 'win32') { + const localAppData = process.env.LOCALAPPDATA || path.join(homedir, 'AppData', 'Local'); + return path.join(localAppData, 'agents', 'skills'); + } + return path.join(homedir, '.agents', 'skills'); +} + // Helper function to resolve target directory (handles both static and dynamic paths) function resolveTargetDir(target) { if (typeof target.dir === 'function') { @@ -389,13 +479,23 @@ function padEndVisible(str, length, char = ' ') { return str + char.repeat(paddingNeeded); } +// Legacy targets — paths that were previously installed but are no longer active install targets +const LEGACY_TARGETS = [ + { + dir: '.claude/commands', + filePattern: /^plan2code-.*\.md$/, + type: 'flat', + }, +]; + // Installation targets const INSTALL_TARGETS = [ { name: 'Claude Code', id: 'claude', - dir: '.claude/commands', - filePattern: /^plan2code-.*\.md$/, + dir: '.claude/skills', + type: 'skill', + filePattern: /^plan2code-/, icon: '◉ ' }, { @@ -445,18 +545,41 @@ const INSTALL_TARGETS = [ : '~/.config/Code/User/prompts/', filePattern: /^plan2code-.*\.prompt\.md$/, icon: '◉ ' + }, + { + name: 'Agent Skills (Amp · Gemini CLI · OpenCode)', + id: 'agent-skills', + dir: getAgentSkillsDir, + sourceDir: '.agents/skills', + type: 'skill', + filePattern: /^plan2code-/, + displayPath: process.platform === 'win32' + ? '%LOCALAPPDATA%\\agents\\skills' + : '~/.agents/skills', + icon: '◉ ' + }, + { + name: 'Crush', + id: 'crush', + dir: getCrushSkillsDir, + sourceDir: 'crush-skills', + type: 'skill', + filePattern: /^plan2code-/, + displayPath: process.platform === 'win32' + ? '%LOCALAPPDATA%\\crush\\skills' + : '~/.config/crush/skills', + icon: '◉ ' + }, + { + name: 'Gemini CLI', + id: 'gemini', + dir: '.gemini/commands', + type: 'toml', + filePattern: /^plan2code-.*\.toml$/, + icon: '◉ ' } ]; -// Parse command line arguments -const args = process.argv.slice(2); -const dryRun = args.includes('--dry-run'); -const uninstall = args.includes('--uninstall'); -const localInstall = args.includes('--local'); -const platformIndex = args.indexOf('--platform'); -const selectedPlatform = platformIndex !== -1 ? args[platformIndex + 1] : null; -const hasArgs = args.length > 0; - // ============================================================================ // DISPLAY FUNCTIONS // ============================================================================ @@ -481,25 +604,19 @@ function displayMascot(variant = 'full', message = '') { */ function displayHeader() { console.log(''); - console.log(`${COLORS.GREEN}${COLORS.BRIGHT}`); - console.log('╔═════════════════════════════════════════════════════════════════════════════════╗'); - console.log('║ ╭───╮ ║'); - console.log('║ │ ● │ Hi! ║'); - console.log('║ │ ◡ │ Nice to meet you ║'); - console.log('║ ╰───╯ ║'); - console.log('║ ║'); - console.log('║ ██████╗ ██╗ █████╗ ███╗ ██╗ ██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ║'); - console.log('║ ██╔══██╗██║ ██╔══██╗████╗ ██║ ╚════██╗ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ║'); - console.log('║ ██████╔╝██║ ███████║██╔██╗ ██║ █████╔╝ ██║ ██║ ██║██║ ██║█████╗ ║'); - console.log('║ ██╔═══╝ ██║ ██╔══██║██║╚██╗██║ ██╔═══╝ ██║ ██║ ██║██║ ██║██╔══╝ ║'); - console.log('║ ██║ ███████╗██║ ██║██║ ╚████║ ███████╗ ╚██████╗╚██████╔╝██████╔╝███████╗ ║'); - console.log('║ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ║'); - console.log('║ ║'); - console.log('║ G L O B A L I N S T A L L A T I O N S Y S T E M ║'); - console.log('║ https://plan2code.jparkerweb.com ║'); - console.log('║ ║'); - console.log('╚═════════════════════════════════════════════════════════════════════════════════╝'); + console.log(`${COLORS.CYAN}${COLORS.BRIGHT}`); + console.log('╔═════════════════════════════════════════════════════════╗'); + console.log('║ ╭───╮ ║'); + console.log('║ │ ● │ Hi! I\'m Planny! ║'); + console.log('║ │ ◡ │ Nice to meet you ║'); + console.log('║ ╰───╯ Welcome to Plan2Code! ║'); + console.log('║ ║'); + console.log('║ G L O B A L I N S T A L L A T I O N S Y S T E M ║'); + console.log('║ https://jparkerweb.github.io/plan2code ║'); + console.log('║ ║'); + console.log('╚═════════════════════════════════════════════════════════╝'); console.log(COLORS.RESET); + console.log(''); } /** @@ -550,73 +667,6 @@ function displayStatusBox(title, lines) { console.log(COLORS.RESET); } -/** - * Display help information - */ -function displayHelp() { - displayHeader(); - - console.log(`${COLORS.GREEN}${SYMBOLS.ACTIVE} USAGE GUIDE${COLORS.RESET}\n`); - - displayStatusBox('OPTIONS', [ - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js${COLORS.RESET}`, - ` ${COLORS.DIM}Interactive mode - Select platforms from menu${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --dry-run${COLORS.RESET}`, - ` ${COLORS.DIM}Preview mode - Show what would be installed without changes${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --platform ${COLORS.RESET}`, - ` ${COLORS.DIM}Install to specific platform only${COLORS.RESET}`, - ` ${COLORS.DIM}Valid IDs: claude, copilot, cursor, continue, windsurf, codeium, vscode-copilot${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --local${COLORS.RESET}`, - ` ${COLORS.DIM}Show local (project-level) installation instructions${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --uninstall${COLORS.RESET}`, - ` ${COLORS.DIM}Remove all installed Plan2Code files${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --loop${COLORS.RESET}`, - ` ${COLORS.DIM}Build and install plan2code-loop CLI tool${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --uninstall-loop${COLORS.RESET}`, - ` ${COLORS.DIM}Remove plan2code-loop global symlink${COLORS.RESET}`, - '', - `${COLORS.CYAN}◆${COLORS.RESET} ${COLORS.BRIGHT}node install.js --help${COLORS.RESET}`, - ` ${COLORS.DIM}Display this help information${COLORS.RESET}`, - ]); - - console.log(''); - displayStatusBox('INSTALLATION PATHS', [ - `${COLORS.YELLOW}[1]${COLORS.RESET} Claude Code ${COLORS.DIM}→ ~/.claude/commands/${COLORS.RESET}`, - `${COLORS.YELLOW}[2]${COLORS.RESET} Copilot CLI ${COLORS.DIM}→ ~/.copilot/agents/${COLORS.RESET}`, - `${COLORS.YELLOW}[3]${COLORS.RESET} Cursor ${COLORS.DIM}→ ~/.cursor/commands/${COLORS.RESET}`, - `${COLORS.YELLOW}[4]${COLORS.RESET} Continue ${COLORS.DIM}→ ~/.continue/prompts/${COLORS.RESET}`, - `${COLORS.YELLOW}[5]${COLORS.RESET} Windsurf ${COLORS.DIM}→ ~/.codeium/windsurf/global_workflows/${COLORS.RESET}`, - `${COLORS.YELLOW}[6]${COLORS.RESET} Codeium (IJ) ${COLORS.DIM}→ ~/.codeium/global_workflows/${COLORS.RESET}`, - `${COLORS.YELLOW}[7]${COLORS.RESET} VS Code Copilot ${COLORS.DIM}→ /Code/User/prompts/${COLORS.RESET}`, - ]); - - console.log(''); - displayStatusBox('EXAMPLES', [ - `${COLORS.GREEN}$${COLORS.RESET} node install.js`, - ` ${COLORS.DIM}Open interactive menu to select platforms${COLORS.RESET}`, - '', - `${COLORS.GREEN}$${COLORS.RESET} node install.js --platform copilot`, - ` ${COLORS.DIM}Install to Copilot CLI only${COLORS.RESET}`, - '', - `${COLORS.GREEN}$${COLORS.RESET} node install.js --dry-run`, - ` ${COLORS.DIM}Preview installation for all platforms${COLORS.RESET}`, - '', - `${COLORS.GREEN}$${COLORS.RESET} node install.js --uninstall`, - ` ${COLORS.DIM}Remove files from all platforms${COLORS.RESET}`, - ]); - - console.log(''); - console.log(`${COLORS.DIM}This utility installs Plan2Code prompts from dist/global-commands/ to your`); - console.log(`home directory for global access across all projects.${COLORS.RESET}`); - console.log(''); -} - /** * Display progress indicator */ @@ -630,26 +680,6 @@ function displayProgress(current, total, label) { process.stdout.write(`\r${COLORS.CYAN}[${bar}${COLORS.CYAN}]${COLORS.RESET} ${percent}% ${label}`); } -// ============================================================================ -// COMMAND LINE ARGUMENT PROCESSING -// ============================================================================ - -if (args.includes('--help') || args.includes('-h')) { - displayHelp(); - process.exit(0); -} - -// Validate platform argument -if (selectedPlatform) { - const validPlatforms = INSTALL_TARGETS.map(t => t.id); - if (!validPlatforms.includes(selectedPlatform)) { - console.log(`${COLORS.RED}${SYMBOLS.ERROR} ERROR${COLORS.RESET}`); - console.log(`${COLORS.RED}Unknown platform: '${selectedPlatform}'${COLORS.RESET}`); - console.log(`${COLORS.YELLOW}Valid platforms: ${validPlatforms.join(', ')}${COLORS.RESET}\n`); - process.exit(1); - } -} - // ============================================================================ // SYNC PROMPTS FUNCTIONS // ============================================================================ @@ -697,7 +727,7 @@ function writeToDestination(rootDir, baseDir, dest, prompt, sourceContent, stats } // Ensure destination directory exists - if (!dryRun && !fs.existsSync(destDir)) { + if (!fs.existsSync(destDir)) { fs.mkdirSync(destDir, { recursive: true }); } @@ -713,17 +743,104 @@ function writeToDestination(rootDir, baseDir, dest, prompt, sourceContent, stats } if (needsUpdate) { - if (!dryRun) { - try { - fs.writeFileSync(destPath, outputContent, 'utf8'); - if (!quiet) { - console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${destFile}`); - } - } catch (err) { - console.error(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${destFile}: ${err.message}`); - stats.errors++; - return; + try { + fs.writeFileSync(destPath, outputContent, 'utf8'); + if (!quiet) { + console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${destFile}`); } + } catch (err) { + console.error(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${destFile}: ${err.message}`); + stats.errors++; + return; + } + stats.updated++; + } else { + stats.skipped++; + } +} + +/** + * Write a skill file to a destination as /SKILL.md + */ +function writeSkillToDestination(rootDir, baseDir, dest, prompt, sourceContent, stats, quiet = false) { + const skillName = generateSkillName(prompt); + const destDir = path.join(rootDir, baseDir, dest.dir, skillName); + const filePath = path.join(destDir, 'SKILL.md'); + + // Build the output content + let outputContent; + if (dest.header) { + outputContent = dest.header(prompt) + '\n\n' + sourceContent; + } else { + outputContent = sourceContent; + } + + // Ensure destination directory exists + fs.mkdirSync(destDir, { recursive: true }); + + // Check if file needs updating + let needsUpdate = true; + if (fs.existsSync(filePath)) { + try { + const existingContent = fs.readFileSync(filePath, 'utf8'); + needsUpdate = existingContent !== outputContent; + } catch (err) { + // File exists but can't be read, will try to write + } + } + + if (needsUpdate) { + try { + fs.writeFileSync(filePath, outputContent, 'utf8'); + if (!quiet) { + console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${skillName}/SKILL.md`); + } + } catch (err) { + console.error(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${skillName}/SKILL.md: ${err.message}`); + stats.errors++; + return; + } + stats.updated++; + } else { + stats.skipped++; + } +} + +/** + * Write a TOML file to a destination for Gemini CLI + */ +function writeTomlToDestination(rootDir, baseDir, dest, prompt, sourceContent, stats, quiet = false) { + const filename = dest.filePattern(prompt); + const destDir = path.join(rootDir, baseDir, dest.dir); + const filePath = path.join(destDir, filename); + + // Build TOML content + const tomlContent = generateTomlContent(prompt, sourceContent); + + // Ensure destination directory exists + fs.mkdirSync(destDir, { recursive: true }); + + // Check if file needs updating + let needsUpdate = true; + if (fs.existsSync(filePath)) { + try { + const existingContent = fs.readFileSync(filePath, 'utf8'); + needsUpdate = existingContent !== tomlContent; + } catch (err) { + // File exists but can't be read, will try to write + } + } + + if (needsUpdate) { + try { + fs.writeFileSync(filePath, tomlContent, 'utf8'); + if (!quiet) { + console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${filename}`); + } + } catch (err) { + console.error(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${filename}: ${err.message}`); + stats.errors++; + return; } stats.updated++; } else { @@ -744,19 +861,17 @@ function syncPrompts(quiet = false) { } // Clean up old dist directories - if (!dryRun) { - const localDistPath = path.join(projectRoot, 'dist/local-commands'); - const globalDistPath = path.join(projectRoot, 'dist/global-commands'); + const localDistPath = path.join(projectRoot, 'dist/local-commands'); + const globalDistPath = path.join(projectRoot, 'dist/global-commands'); - if (fs.existsSync(localDistPath)) { - if (!quiet) console.log(` ${COLORS.YELLOW}░░░${COLORS.RESET} Cleaning: dist/local-commands/`); - deleteDirectory(localDistPath); - } + if (fs.existsSync(localDistPath)) { + if (!quiet) console.log(` ${COLORS.YELLOW}░░░${COLORS.RESET} Cleaning: dist/local-commands/`); + deleteDirectory(localDistPath); + } - if (fs.existsSync(globalDistPath)) { - if (!quiet) console.log(` ${COLORS.YELLOW}░░░${COLORS.RESET} Cleaning: dist/global-commands/`); - deleteDirectory(globalDistPath); - } + if (fs.existsSync(globalDistPath)) { + if (!quiet) console.log(` ${COLORS.YELLOW}░░░${COLORS.RESET} Cleaning: dist/global-commands/`); + deleteDirectory(globalDistPath); } // Process each source prompt @@ -775,12 +890,24 @@ function syncPrompts(quiet = false) { // Write to dist/local-commands/ for (const dest of LOCAL_DESTINATIONS) { - writeToDestination(projectRoot, 'dist/local-commands', dest, prompt, sourceContent, stats, quiet); + if (dest.type === 'skill') { + writeSkillToDestination(projectRoot, 'dist/local-commands', dest, prompt, sourceContent, stats, quiet); + } else if (dest.type === 'toml') { + writeTomlToDestination(projectRoot, 'dist/local-commands', dest, prompt, sourceContent, stats, quiet); + } else { + writeToDestination(projectRoot, 'dist/local-commands', dest, prompt, sourceContent, stats, quiet); + } } // Write to dist/global-commands/ for (const dest of GLOBAL_DESTINATIONS) { - writeToDestination(projectRoot, 'dist/global-commands', dest, prompt, sourceContent, stats, quiet); + if (dest.type === 'skill') { + writeSkillToDestination(projectRoot, 'dist/global-commands', dest, prompt, sourceContent, stats, quiet); + } else if (dest.type === 'toml') { + writeTomlToDestination(projectRoot, 'dist/global-commands', dest, prompt, sourceContent, stats, quiet); + } else { + writeToDestination(projectRoot, 'dist/global-commands', dest, prompt, sourceContent, stats, quiet); + } } } @@ -807,9 +934,6 @@ function getTargets(platformIds = null) { if (platformIds && platformIds.length > 0) { return INSTALL_TARGETS.filter(t => platformIds.includes(t.id)); } - if (selectedPlatform) { - return INSTALL_TARGETS.filter(t => t.id === selectedPlatform); - } return INSTALL_TARGETS; } @@ -817,6 +941,47 @@ function getTargets(platformIds = null) { // INSTALLATION // ============================================================================ +/** + * Recursively copy skill subdirectories matching /^plan2code-/ from source to dest + */ +function copySkillDirectory(sourcePath, destPath, stats) { + fs.mkdirSync(destPath, { recursive: true }); + const entries = fs.readdirSync(sourcePath); + for (const entry of entries) { + if (!/^plan2code-/.test(entry)) continue; + const srcSubdir = path.join(sourcePath, entry); + if (!fs.statSync(srcSubdir).isDirectory()) continue; + const destSubdir = path.join(destPath, entry); + fs.mkdirSync(destSubdir, { recursive: true }); + const files = fs.readdirSync(srcSubdir); + for (const file of files) { + fs.copyFileSync(path.join(srcSubdir, file), path.join(destSubdir, file)); + stats.copied++; + } + } +} + +/** + * Remove files/dirs matching LEGACY_TARGETS patterns (silent cleanup) + */ +function cleanLegacyTargets() { + const homedir = os.homedir(); + for (const target of LEGACY_TARGETS) { + const targetDir = path.join(homedir, target.dir); + if (!fs.existsSync(targetDir)) continue; + const entries = fs.readdirSync(targetDir); + for (const entry of entries) { + if (!target.filePattern.test(entry)) continue; + const entryPath = path.join(targetDir, entry); + if (target.type === 'skill') { + fs.rmSync(entryPath, { recursive: true, force: true }); + } else { + fs.unlinkSync(entryPath); + } + } + } +} + /** * Execute installation */ @@ -829,10 +994,7 @@ function install(targets = null) { let totalSkipped = 0; let totalErrors = 0; - displaySectionHeader( - 'INSTALLATION', - dryRun ? '[ PREVIEW MODE - NO CHANGES WILL BE MADE ]' : '[ INSTALLING FILES ]' - ); + displaySectionHeader(' INSTALLING FILES '); // Generate distribution files first if (!syncPrompts(true)) { @@ -841,12 +1003,15 @@ function install(targets = null) { } console.log(`${COLORS.GREEN}${SYMBOLS.SUCCESS} Distribution files generated${COLORS.RESET}\n`); + // Clean up legacy install targets before installing new ones + cleanLegacyTargets(); + console.log(''); displayStatusBox('PARAMETERS', [ `${COLORS.CYAN}Source:${COLORS.RESET} ${SOURCE_BASE}/`, `${COLORS.CYAN}Home Directory:${COLORS.RESET} ${homeDir}`, `${COLORS.CYAN}Platforms:${COLORS.RESET} ${targetList.length}`, - `${COLORS.CYAN}Mode:${COLORS.RESET} ${dryRun ? 'PREVIEW' : 'INSTALL'}`, + `${COLORS.CYAN}Mode:${COLORS.RESET} INSTALL`, ]); console.log(''); @@ -873,7 +1038,7 @@ function install(targets = null) { continue; } - // Get files to install + // Get files/entries to install let files; try { files = fs.readdirSync(sourcePath).filter(f => target.filePattern.test(f)); @@ -890,7 +1055,7 @@ function install(targets = null) { } // Create destination directory if needed - if (!dryRun && !fs.existsSync(destPath)) { + if (!fs.existsSync(destPath)) { try { fs.mkdirSync(destPath, { recursive: true }); console.log(` ${COLORS.GREEN}${SYMBOLS.SUCCESS} CREATED${COLORS.RESET} Directory: ${displayPathStr}`); @@ -901,7 +1066,7 @@ function install(targets = null) { } } - // Clean up old files before copying new ones + // Clean up old files/dirs before copying new ones try { const existingFiles = fs.readdirSync(destPath).filter(f => target.filePattern.test(f)); if (existingFiles.length > 0) { @@ -909,7 +1074,9 @@ function install(targets = null) { for (const oldFile of existingFiles) { const oldFilePath = path.join(destPath, oldFile); try { - if (!dryRun) { + if (target.type === 'skill') { + fs.rmSync(oldFilePath, { recursive: true, force: true }); + } else { fs.unlinkSync(oldFilePath); } console.log(` ${COLORS.YELLOW}░░░${COLORS.RESET} Removed: ${oldFile}`); @@ -922,22 +1089,33 @@ function install(targets = null) { // Directory might be newly created, ignore read errors } - // Copy files - console.log(` ${COLORS.CYAN}${SYMBOLS.ACTIVE} COPYING${COLORS.RESET} ${files.length} file(s)...`); - for (const file of files) { - const srcFile = path.join(sourcePath, file); - const destFile = path.join(destPath, file); - + // Copy files/skill directories + if (target.type === 'skill') { + const copyStats = { copied: 0 }; + console.log(` ${COLORS.CYAN}${SYMBOLS.ACTIVE} COPYING${COLORS.RESET} skill directories...`); try { - if (!dryRun) { - fs.copyFileSync(srcFile, destFile); - } - console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${file}`); - totalInstalled++; + copySkillDirectory(sourcePath, destPath, copyStats); + console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${copyStats.copied} file(s) across ${files.length} skill(s)`); + totalInstalled += copyStats.copied; } catch (err) { - console.log(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${file}: ${err.message}`); + console.log(` ${COLORS.RED}✖✖✖${COLORS.RESET} Copy failed: ${err.message}`); totalErrors++; } + } else { + console.log(` ${COLORS.CYAN}${SYMBOLS.ACTIVE} COPYING${COLORS.RESET} ${files.length} file(s)...`); + for (const file of files) { + const srcFile = path.join(sourcePath, file); + const destFile = path.join(destPath, file); + + try { + fs.copyFileSync(srcFile, destFile); + console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} ${file}`); + totalInstalled++; + } catch (err) { + console.log(` ${COLORS.RED}✖✖✖${COLORS.RESET} ${file}: ${err.message}`); + totalErrors++; + } + } } console.log(''); } @@ -949,11 +1127,7 @@ function install(targets = null) { console.log(`${COLORS.CYAN}║${COLORS.RESET}${' '.repeat(75)}${COLORS.CYAN}║${COLORS.RESET}`); console.log(`${COLORS.CYAN}╠═══════════════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - if (dryRun) { - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.YELLOW}Mode:${COLORS.RESET} PREVIEW - No files were copied`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - } else { - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.GREEN}Mode:${COLORS.RESET} INSTALL`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - } + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.GREEN}Mode:${COLORS.RESET} INSTALL`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); const statusColor = totalErrors > 0 ? COLORS.RED : COLORS.GREEN; const statusText = totalErrors > 0 ? 'COMPLETED WITH ERRORS' : 'SUCCESS'; @@ -973,18 +1147,11 @@ function install(targets = null) { // Show celebratory or sad mascot based on result console.log(''); - if (totalErrors === 0 && !dryRun) { - console.log(`${COLORS.GREEN} ╭───╮${COLORS.RESET}`); - console.log(`${COLORS.GREEN} │ ${COLORS.CYAN}★${COLORS.GREEN} │${COLORS.RESET} ${COLORS.BRIGHT}All done! Happy coding!${COLORS.RESET}`); - console.log(`${COLORS.GREEN} │ ${COLORS.BRIGHT}◡${COLORS.GREEN} │${COLORS.RESET} ${COLORS.BRIGHT}If this is your first time using Plan2Code, read the docs here:${COLORS.RESET}`); - console.log(`${COLORS.GREEN} ╰───╯${COLORS.RESET} ${COLORS.BRIGHT}https://github.com/jparkerweb/plan2code${COLORS.RESET}`); - } else if (dryRun) { - console.log(`${COLORS.YELLOW} ╭───╮${COLORS.RESET}`); - console.log(`${COLORS.YELLOW} │ ${COLORS.CYAN}●${COLORS.YELLOW} │${COLORS.RESET}`); - console.log(`${COLORS.YELLOW} │ ○ │${COLORS.RESET} ${COLORS.DIM}That was just a preview!${COLORS.RESET}`); - console.log(`${COLORS.YELLOW} ╰───╯${COLORS.RESET}`); - console.log(''); - console.log(`${COLORS.YELLOW}${SYMBOLS.INFO} Run without --dry-run to install files${COLORS.RESET}`); + if (totalErrors === 0) { + console.log(`${COLORS.GREEN} ╭───╮${COLORS.RESET} ${COLORS.BRIGHT}All done! Happy coding!${COLORS.RESET}`); + console.log(`${COLORS.GREEN} │ ${COLORS.CYAN}★${COLORS.GREEN} │${COLORS.RESET} ${COLORS.BRIGHT}If this is your first time using Plan2Code, read the docs here:${COLORS.RESET}`); + console.log(`${COLORS.GREEN} │ ${COLORS.BRIGHT}◡${COLORS.GREEN} │${COLORS.RESET} ${COLORS.BRIGHT}https://github.com/jparkerweb/plan2code${COLORS.RESET}`); + console.log(`${COLORS.GREEN} ╰───╯${COLORS.RESET}`); } console.log(''); @@ -1006,19 +1173,19 @@ function uninstallFiles(targets = null) { let totalRemoved = 0; let totalErrors = 0; - displaySectionHeader( - 'UNINSTALLATION', - dryRun ? '[ PREVIEW MODE - NO CHANGES WILL BE MADE ]' : '[ REMOVING FILES ]' - ); + displaySectionHeader('UNINSTALLATION', '[ REMOVING FILES ]'); console.log(''); displayStatusBox('PARAMETERS', [ `${COLORS.CYAN}Home Directory:${COLORS.RESET} ${homeDir}`, `${COLORS.CYAN}Platforms:${COLORS.RESET} ${targetList.length}`, - `${COLORS.CYAN}Mode:${COLORS.RESET} ${dryRun ? 'PREVIEW' : 'UNINSTALL'}`, + `${COLORS.CYAN}Mode:${COLORS.RESET} UNINSTALL`, ]); console.log(''); + // Clean up legacy install targets + cleanLegacyTargets(); + console.log(`${COLORS.YELLOW}${SYMBOLS.ACTIVE} STARTING UNINSTALLATION${COLORS.RESET}\n`); for (const target of targetList) { @@ -1036,7 +1203,7 @@ function uninstallFiles(targets = null) { continue; } - // Get files to remove + // Get files/entries to remove let files; try { files = fs.readdirSync(destPath).filter(f => target.filePattern.test(f)); @@ -1051,13 +1218,15 @@ function uninstallFiles(targets = null) { continue; } - // Remove files + // Remove files/skill directories console.log(` ${COLORS.CYAN}${SYMBOLS.ACTIVE} REMOVING${COLORS.RESET} ${files.length} file(s)...`); for (const file of files) { const destFile = path.join(destPath, file); try { - if (!dryRun) { + if (target.type === 'skill') { + fs.rmSync(destFile, { recursive: true, force: true }); + } else { fs.unlinkSync(destFile); } console.log(` ${COLORS.GREEN}▰▰▰${COLORS.RESET} Removed: ${file}`); @@ -1077,11 +1246,7 @@ function uninstallFiles(targets = null) { console.log(`${COLORS.CYAN}║${COLORS.RESET}${' '.repeat(75)}${COLORS.CYAN}║${COLORS.RESET}`); console.log(`${COLORS.CYAN}╠═══════════════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - if (dryRun) { - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.YELLOW}Mode:${COLORS.RESET} PREVIEW - No files were removed`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - } else { - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.GREEN}Mode:${COLORS.RESET} UNINSTALL`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - } + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.GREEN}Mode:${COLORS.RESET} UNINSTALL`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); const statusColor = totalErrors > 0 ? COLORS.RED : COLORS.GREEN; const statusText = totalErrors > 0 ? 'COMPLETED WITH ERRORS' : 'SUCCESS'; @@ -1096,11 +1261,6 @@ function uninstallFiles(targets = null) { console.log(`${COLORS.CYAN}║${COLORS.RESET}${' '.repeat(75)}${COLORS.CYAN}║${COLORS.RESET}`); console.log(`${COLORS.CYAN}╚═══════════════════════════════════════════════════════════════════════════╝${COLORS.RESET}`); - if (dryRun) { - console.log(''); - console.log(`${COLORS.YELLOW}${SYMBOLS.INFO} Run without --dry-run to remove files${COLORS.RESET}`); - } - console.log(''); return totalErrors === 0 ? 0 : 1; @@ -1201,182 +1361,104 @@ function runInteractive() { const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve)); async function main() { + // Task 2.1: Main menu display displayHeader(); console.log(`${COLORS.BLUE}${COLORS.BRIGHT}Version Info:${COLORS.RESET} ${projectVersion.name} ${projectVersion.version}`); - console.log(`${COLORS.GREEN}${SYMBOLS.ACTIVE} SELECT PLATFORMS${COLORS.RESET}\n`); - - console.log(`${COLORS.CYAN}╔════════════════════════════════════════════════════════════════╗${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}AVAILABLE PLATFORMS${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(`${COLORS.CYAN}╠════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - - INSTALL_TARGETS.forEach((target, i) => { - const num = `${COLORS.BRIGHT}${i + 1}.${COLORS.RESET}`; - const icon = `${COLORS.GREEN}${target.icon}${COLORS.RESET}`; - const name = `${COLORS.BRIGHT}${target.name.padEnd(14)}${COLORS.RESET}`; - const targetPath = `${COLORS.DIM}${getDisplayPath(target)}${COLORS.RESET}`; - - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${num} ${icon} ${name} ${targetPath}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - }); - - console.log(`${COLORS.CYAN}╠════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}A.${COLORS.RESET} ${COLORS.MAGENTA}◉ ALL${COLORS.RESET} ${COLORS.BRIGHT}Install to ALL platforms + loop CLI${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}L.${COLORS.RESET} ${COLORS.BLUE}◉ LOCAL${COLORS.RESET} ${COLORS.BRIGHT}Show local (project) install instructions${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}O.${COLORS.RESET} ${COLORS.GREEN}◉ LOOP${COLORS.RESET} ${COLORS.BRIGHT}Install plan2code-loop CLI only${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}U.${COLORS.RESET} ${COLORS.RED}◉ UNINSTALL${COLORS.RESET} ${COLORS.BRIGHT}Remove Plan2Code files + loop CLI${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}Q.${COLORS.RESET} ${COLORS.DIM}◉ QUIT${COLORS.RESET} ${COLORS.BRIGHT}Exit${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(`${COLORS.CYAN}╚════════════════════════════════════════════════════════════════╝${COLORS.RESET}`); console.log(''); - // Show thinking mascot for input prompt - console.log(`${COLORS.MAGENTA} ╭───╮${COLORS.RESET}`); - console.log(`${COLORS.MAGENTA} │ ${COLORS.CYAN}●${COLORS.MAGENTA} │${COLORS.RESET} ${COLORS.DIM}What would you like to do?${COLORS.RESET}`); - console.log(`${COLORS.MAGENTA} │ ${COLORS.YELLOW}~${COLORS.MAGENTA} │${COLORS.RESET} ${COLORS.GREEN}I suggest A: 'Install to ALL platforms + loop CLI'${COLORS.RESET}`); - console.log(`${COLORS.MAGENTA} ╰───╯${COLORS.RESET}`); + console.log(`${COLORS.CYAN}╔═════════════════════════════════════════════════════════╗${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}INSTALL PLAN2CODE${COLORS.RESET}`, 58) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(`${COLORS.CYAN}╠═════════════════════════════════════════════════════════╣${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}I.${COLORS.RESET} ${COLORS.GREEN}INSTALL${COLORS.RESET} Install Plan2Code for all platforms`, 58) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}U.${COLORS.RESET} ${COLORS.RED}UNINSTALL${COLORS.RESET} Remove Plan2Code files`, 58) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}C.${COLORS.RESET} ${COLORS.BLUE}CUSTOM${COLORS.RESET} Advanced options`, 58) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}Q.${COLORS.RESET} ${COLORS.DIM}QUIT${COLORS.RESET} Exit`, 58) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(`${COLORS.CYAN}╚═════════════════════════════════════════════════════════╝${COLORS.RESET}`); console.log(''); - const answer = await question(`${COLORS.CYAN}${SYMBOLS.SELECT} SELECT OPTION${COLORS.RESET} (1-7, A, L, O, U, Q, or 1,3,5): `); - const input = answer.trim().toUpperCase(); - if (input === 'Q' || input === '') { - console.log(`\n${COLORS.YELLOW}${SYMBOLS.WARNING} CANCELLED${COLORS.RESET}\n`); + // Task 2.2: Input prompt + console.log(''); + const answer = await question(`${COLORS.CYAN}${SYMBOLS.SELECT} SELECT OPTION${COLORS.RESET} (I, U, C, Q) [I]: `); + const input = answer.trim().toUpperCase() || 'I'; + + // Task 2.3: I path — Install All + if (input === 'I') { rl.close(); - process.exit(0); - } - - if (input === 'L') { - // Local installation instructions - rl.close(); - process.exit(displayLocalInstructions()); - } - - if (input === 'O') { - // Install plan2code-loop - rl.close(); - process.exit(installPlan2CodeLoop()); + const loopResult = installPlan2CodeLoop(); + console.log(''); + const installResult = install(); + process.exit(loopResult !== 0 ? loopResult : installResult); } + // Task 2.4: U path — Uninstall All if (input === 'U') { - // Uninstall flow console.log(''); - console.log(`${COLORS.CYAN}╔═══════════════════════════════════════════════════════════════════════════╗${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}${COLORS.RED}UNINSTALL FROM PLATFORMS${COLORS.RESET}`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(`${COLORS.CYAN}╠═══════════════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - - INSTALL_TARGETS.forEach((target, i) => { - const num = `${COLORS.BRIGHT}${i + 1}.${COLORS.RESET}`; - const icon = `${COLORS.RED}${target.icon}${COLORS.RESET}`; - const name = `${COLORS.BRIGHT}${target.name.padEnd(14)}${COLORS.RESET}`; - const targetPath = `${COLORS.DIM}${getDisplayPath(target)}${COLORS.RESET}`; - - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${num} ${icon} ${name} ${targetPath}`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - }); - - console.log(`${COLORS.CYAN}╠═══════════════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}A.${COLORS.RESET} ${COLORS.RED}◉ ALL${COLORS.RESET} ${COLORS.BRIGHT}Uninstall from ALL platforms + loop CLI${COLORS.RESET}`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}Q.${COLORS.RESET} ${COLORS.DIM}◉ CANCEL${COLORS.RESET} ${COLORS.BRIGHT}Cancel${COLORS.RESET}`, 76) + `${COLORS.CYAN}║${COLORS.RESET}`); - console.log(`${COLORS.CYAN}╚═══════════════════════════════════════════════════════════════════════════╝${COLORS.RESET}`); - console.log(''); - - // Show worried mascot for uninstall prompt + console.log(`${COLORS.RED} o o${COLORS.RESET}`); + console.log(`${COLORS.RED} ╲ ╱ ${COLORS.YELLOW}!${COLORS.RESET}`); console.log(`${COLORS.RED} ╭───╮${COLORS.RESET}`); console.log(`${COLORS.RED} │ ${COLORS.YELLOW}○${COLORS.RED} │${COLORS.RESET}`); - console.log(`${COLORS.RED} │ ${COLORS.YELLOW}~${COLORS.RED} │${COLORS.RESET} ${COLORS.DIM}Are you sure about this?${COLORS.RESET}`); + console.log(`${COLORS.RED} │ ${COLORS.YELLOW}~${COLORS.RED} │${COLORS.RESET} ${COLORS.DIM}Are you sure? This will remove Plan2Code from all platforms.${COLORS.RESET}`); + console.log(`${COLORS.RED} ├───┤${COLORS.RESET}`); + console.log(`${COLORS.RED} │ · │${COLORS.RESET}`); console.log(`${COLORS.RED} ╰───╯${COLORS.RESET}`); console.log(''); - const uninstallAnswer = await question(`${COLORS.RED}${SYMBOLS.SELECT} SELECT PLATFORMS TO UNINSTALL${COLORS.RESET} (1-7, A, Q, or 1,3,5): `); - const uninstallInput = uninstallAnswer.trim().toUpperCase(); + const confirmAnswer = await question(`${COLORS.RED}${SYMBOLS.SELECT} CONFIRM UNINSTALL${COLORS.RESET} (Y/N) [N]: `); + const confirmInput = confirmAnswer.trim().toUpperCase() || 'N'; - if (uninstallInput === 'Q' || uninstallInput === '') { - console.log(`\n${COLORS.YELLOW}${SYMBOLS.WARNING} CANCELLED${COLORS.RESET}\n`); + if (confirmInput === 'Y') { rl.close(); - process.exit(0); - } - - let targets; - if (uninstallInput === 'A') { - targets = INSTALL_TARGETS; + const uninstallResult = uninstallFiles(); + const loopResult = uninstallPlan2CodeLoop(); + process.exit(uninstallResult !== 0 ? uninstallResult : loopResult); } else { - const indices = uninstallInput.split(',').map(s => parseInt(s.trim()) - 1); - const validIndices = indices.filter(i => i >= 0 && i < INSTALL_TARGETS.length); - - if (validIndices.length === 0) { - console.log(`${COLORS.RED}${SYMBOLS.ERROR} Invalid selection${COLORS.RESET}\n`); - rl.close(); - process.exit(1); - } - - targets = validIndices.map(i => INSTALL_TARGETS[i]); - } - - const platformNames = targets.map(t => `${COLORS.YELLOW}${t.name}${COLORS.RESET}`).join(', '); - const confirm = await question(`\n${COLORS.RED}${SYMBOLS.WARNING} CONFIRM UNINSTALL${COLORS.RESET} from ${targets.length} platform(s): ${platformNames}? (y/N): `); - - if (confirm.trim().toLowerCase() !== 'y') { console.log(`\n${COLORS.YELLOW}${SYMBOLS.WARNING} CANCELLED${COLORS.RESET}\n`); rl.close(); process.exit(0); } - - rl.close(); - const uninstallResult = uninstallFiles(targets); - - // If uninstalling from ALL platforms, also unlink plan2code-loop - if (uninstallInput === 'A') { - console.log(''); - uninstallPlan2CodeLoop(); - } - - process.exit(uninstallResult); } - // Install flow - let targets; - if (input === 'A') { - targets = INSTALL_TARGETS; - } else { - const indices = input.split(',').map(s => parseInt(s.trim()) - 1); - const validIndices = indices.filter(i => i >= 0 && i < INSTALL_TARGETS.length); + // Task 2.5: C path — CUSTOM sub-menu + if (input === 'C') { + console.log(''); + console.log(`${COLORS.CYAN}╔════════════════════════════════════════════════════════════════╗${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}CUSTOM OPTIONS${COLORS.RESET}`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(`${COLORS.CYAN}╠════════════════════════════════════════════════════════════════╣${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}L.${COLORS.RESET} ${COLORS.BLUE}LOCAL${COLORS.RESET} Show local (project) install instructions`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}O.${COLORS.RESET} ${COLORS.GREEN}LOOP CLI${COLORS.RESET} Install plan2code-loop CLI only`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(padEndVisible(`${COLORS.CYAN}║${COLORS.RESET} ${COLORS.BRIGHT}Q.${COLORS.RESET} ${COLORS.DIM}BACK${COLORS.RESET} Return to main menu`, 65) + `${COLORS.CYAN}║${COLORS.RESET}`); + console.log(`${COLORS.CYAN}╚════════════════════════════════════════════════════════════════╝${COLORS.RESET}`); + console.log(''); + const customAnswer = await question(`${COLORS.CYAN}${SYMBOLS.SELECT} SELECT OPTION${COLORS.RESET} (L, O, Q) [Q]: `); + const customInput = customAnswer.trim().toUpperCase() || 'Q'; - if (validIndices.length === 0) { - console.log(`${COLORS.RED}${SYMBOLS.ERROR} Invalid selection${COLORS.RESET}\n`); + // Task 2.6: C > L — show local install instructions + if (customInput === 'L') { rl.close(); - process.exit(1); + process.exit(displayLocalInstructions()); } - targets = validIndices.map(i => INSTALL_TARGETS[i]); + // Task 2.7: C > O — install loop CLI only + if (customInput === 'O') { + rl.close(); + const result = installPlan2CodeLoop(); + process.exit(result); + } + + // Task 2.8: C > Q — back to main menu + return main(); } - const platformNames = targets.map(t => `${COLORS.YELLOW}${t.name}${COLORS.RESET}`).join(', '); - // Show happy mascot for install confirmation - console.log(''); - console.log(`${COLORS.GREEN} ╭───╮${COLORS.RESET}`); - console.log(`${COLORS.GREEN} │ ${COLORS.CYAN}●${COLORS.GREEN} │${COLORS.RESET}`); - console.log(`${COLORS.GREEN} │ ${COLORS.BRIGHT}◡${COLORS.GREEN} │${COLORS.RESET} ${COLORS.DIM}Ready to install!${COLORS.RESET}`); - console.log(`${COLORS.GREEN} ╰───╯${COLORS.RESET}`); - const confirm = await question(`\n${COLORS.GREEN}${SYMBOLS.SELECT} CONFIRM INSTALL${COLORS.RESET}? (Y/n): `); - - if (confirm.trim().toLowerCase() === 'n') { + // Task 2.8: Q — exit + if (input === 'Q') { console.log(`\n${COLORS.YELLOW}${SYMBOLS.WARNING} CANCELLED${COLORS.RESET}\n`); rl.close(); process.exit(0); } - rl.close(); - - // If installing ALL platforms, install plan2code-loop first - let loopResult = 0; - if (input === 'A') { - loopResult = installPlan2CodeLoop(); - console.log(''); - } - - const installResult = install(targets); - - if (input === 'A') { - process.exit(installResult === 0 && loopResult === 0 ? 0 : 1); - } - - process.exit(installResult); + // Task 2.8: Invalid input — loop back + console.log(`\n${COLORS.RED}${SYMBOLS.ERROR} Invalid option. Please choose I, U, C, or Q.${COLORS.RESET}\n`); + return main(); } main().catch(err => { @@ -1664,22 +1746,5 @@ function uninstallPlan2CodeLoop() { // MAIN // ============================================================================ -// Additional CLI arguments for plan2code-loop -const installLoop = args.includes('--loop'); -const uninstallLoop = args.includes('--uninstall-loop'); - -// Run the appropriate function -if (installLoop) { - process.exit(installPlan2CodeLoop()); -} else if (uninstallLoop) { - process.exit(uninstallPlan2CodeLoop()); -} else if (!hasArgs) { - // No arguments - run interactive menu - runInteractive(); -} else if (localInstall) { - process.exit(displayLocalInstructions()); -} else if (uninstall) { - process.exit(uninstallFiles()); -} else { - process.exit(install()); -} +// Always run interactive menu +runInteractive(); diff --git a/package.json b/package.json index 7dcd930..db8dd96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plan2code", - "version": "1.6.1", + "version": "1.7.0", "private": true, "bin": { "plan2code": "./install.js" diff --git a/plan2code-loop/package.json b/plan2code-loop/package.json index 6f54fb2..87e6fcb 100644 --- a/plan2code-loop/package.json +++ b/plan2code-loop/package.json @@ -1,6 +1,6 @@ { "name": "plan2code-loop", - "version": "1.6.1", + "version": "1.6.2", "description": "Plan2Code Loop - Autonomous spec-driven implementation CLI", "type": "module", "main": "dist/index.js", diff --git a/plan2code-loop/src/cli.ts b/plan2code-loop/src/cli.ts index 328162b..0fd6ccb 100644 --- a/plan2code-loop/src/cli.ts +++ b/plan2code-loop/src/cli.ts @@ -176,7 +176,7 @@ async function handleExistingSession( export async function setupSession( stateManager: StateManager ): Promise { - // Show welcome + // Show Planny welcome logger.welcome(); // Select spec diff --git a/plan2code-loop/src/utils/git.ts b/plan2code-loop/src/utils/git.ts index 6e36daf..814a5eb 100644 --- a/plan2code-loop/src/utils/git.ts +++ b/plan2code-loop/src/utils/git.ts @@ -109,7 +109,9 @@ export async function createTaskCommit(options: GitCommitOptions): Promise ``` > ⋅ +> o o +> ╲ ╱ ? > ╭───╮ > │ ● │ > │ ~ │ What should we update? +> ├───┤ +> │ · │ > ╰───╯ > ``` > @@ -140,10 +148,12 @@ Check for other AI agent config files and offer to replace with AGENTS.md refere ### If Files Found ``` -> ╭───╮ -> │ ● │ -> │ ~ │ Found other AI agent configs! -> ╰───╯ + o o + \ / + +---+ + | o | + | ~ | Found other AI agent configs! + +---+ ``` > Found AI config files that could reference AGENTS.md: diff --git a/src/plan2code---init.md b/src/plan2code---init.md index b363ad2..69fca9f 100644 --- a/src/plan2code---init.md +++ b/src/plan2code---init.md @@ -4,9 +4,13 @@ Start all CREATE AGENTS MODE responses with '💡' ``` ⋅ + o o + ╲ ╱ ╭───╮ │ ● │ │ ◡ │ Let me explore your codebase! + ├───┤ + │ · │ ╰───╯ ``` @@ -57,9 +61,13 @@ If files found, show: ``` ⋅ + o o + ╲ ╱ ╭───╮ │ ● │ │ ~ │ Found some other AI agent configs! + ├───┤ + │ · │ ╰───╯ I found these AI agent configuration files: diff --git a/src/plan2code---quick-task.md b/src/plan2code---quick-task.md index 47479db..1700da1 100644 --- a/src/plan2code---quick-task.md +++ b/src/plan2code---quick-task.md @@ -16,9 +16,13 @@ Check for `./AGENTS.md` first: > ``` > ⋅ + > o o + > ╲ ╱ ? > ╭───╮ > │ ● │ > │ ~ │ Hmm, I don't see an AGENTS.md... + > ├───┤ + > │ · │ > ╰───╯ > ``` > @@ -39,9 +43,13 @@ Check for `./AGENTS.md` first: ``` ⋅ + o o + ╲ ╱ ? ╭───╮ │ ● │ │ ~ │ I'm ready to help! + ├───┤ + │ · │ ╰───╯ ``` diff --git a/src/plan2code-1--plan.md b/src/plan2code-1--plan.md index ee705f3..e35c8c9 100644 --- a/src/plan2code-1--plan.md +++ b/src/plan2code-1--plan.md @@ -15,9 +15,13 @@ Check if `./AGENTS.md` exists: > ``` > ⋅ + > o o + > ╲ ╱ ? > ╭───╮ > │ ● │ > │ ~ │ Hmm, I don't see an AGENTS.md... + > ├───┤ + > │ · │ > ╰───╯ > ``` > @@ -265,9 +269,13 @@ When complete (PLAN-DRAFT created), tell user: > > ``` > ⋅ +> o o +> ╲ ╱ > ╭───╮ > │ ★ │ > │ ◡ │ Planning done! Ready for documentation! +> ├───┤ +> │ · │ > ╰───╯ > ============================================= > NEXT STEP: Start a NEW conversation then run: diff --git a/src/plan2code-1b--revise-plan.md b/src/plan2code-1b--revise-plan.md index 9e958d3..b2e86ba 100644 --- a/src/plan2code-1b--revise-plan.md +++ b/src/plan2code-1b--revise-plan.md @@ -170,9 +170,13 @@ Report and resolve issues before proceeding. ``` ⋅ + o o + ╲ ╱ ╭───╮ │ ★ │ │ ◡ │ All revised! Ready to continue! + ├───┤ + │ · │ ╰───╯ ╔═══════════════════════════════════════════════════════════════════╗ diff --git a/src/plan2code-2--document.md b/src/plan2code-2--document.md index 9edd35c..3117587 100644 --- a/src/plan2code-2--document.md +++ b/src/plan2code-2--document.md @@ -204,9 +204,13 @@ Parallel Execution: [Groups or "None - sequential only"] > > ``` > ⋅ +> o o +> ╲ ╱ > ╭───╮ > │ ★ │ > │ ◡ │ Specs are ready! Time to build! +> ├───┤ +> │ · │ > ╰───╯ > ============================================ > NEXT STEP: Start a NEW conversation and run: diff --git a/src/plan2code-3--implement.md b/src/plan2code-3--implement.md index e0ad587..c94ca47 100644 --- a/src/plan2code-3--implement.md +++ b/src/plan2code-3--implement.md @@ -182,9 +182,13 @@ Sections: Summary (2-3 sentences), Tasks Completed (Y/Z + blocked list), Test Re ``` ⋅ + o o + ╲ ╱ ? ╭───╮ │ ● │ │ ~ │ Ready for your review! + ├───┤ + │ · │ ╰───╯ ``` @@ -194,27 +198,35 @@ Sections: Summary (2-3 sentences), Tasks Completed (Y/Z + blocked list), Test Re On user "approved": 1. Mark `[/]` → `[x]` in overview.md, update phase-X.md status to "Complete" -2. Show Mascot art with completion message +2. Show Planny art with completion message 3. Provide: `git add -A && git commit -m "Complete Phase X: [Phase Name]" -m "AI Assisted"` 4. **If more phases:** "NEXT STEP: Start NEW conversation and run: `/plan2code-3--implement`" 5. **If final phase:** "NEXT STEP: Start NEW conversation and run: `/plan2code-4--finalize`" 6. Mention `/plan2code-1b--revise` option -continuing: +Planny (continuing): ``` ⋅ + o o + ╲ ╱ ╭───╮ │ ★ │ │ ◡ │ Phase done! Great progress! + ├───┤ + │ · │ ╰───╯ ``` -final phase: +Planny (final phase): ``` ⋅ + o o + ╲ ╲ ╱ ╱ ╭───╮ │ ★ │ │ ◡ │ All phases complete! Amazing work! + ├───┤ + │ · │ ╰───╯ ``` @@ -235,7 +247,7 @@ If user says "abort", "cancel", or similar: |-------|----------| | Lost context mid-phase | Attach specs, say "resume from Task X.Y" | | Spec unclear/conflicting | Mark task blocked, ask user | -| Need to change plan | Pause, use `/plan2code-1b--revise` | +| Need to change plan | Pause, use `/plan2code-1b--revise-plan` | ## Learning Capture diff --git a/src/plan2code-4--finalize.md b/src/plan2code-4--finalize.md index acbfd18..bd2b17f 100644 --- a/src/plan2code-4--finalize.md +++ b/src/plan2code-4--finalize.md @@ -177,13 +177,17 @@ Add this summary to `overview.md` under `## Completion Summary`. Report: table of documents needing updates with proposed changes. List each document with specific additions. -If updates needed, show Mascot and ask for approval: +If updates needed, show Planny and ask for approval: ``` ⋅ + o o + ╲ ╱ ? ╭───╮ │ ● │ │ ~ │ Found some docs that need updating! + ├───┤ + │ · │ ╰───╯ ``` @@ -257,9 +261,13 @@ specs--completed/ ``` ⋅ + o o + ╲ ╲ ╱ ╱ ╭───╮ │ ★ │ │ ◡ │ You did it! Feature complete! + ├───┤ + │ · │ ╰───╯ ``` diff --git a/version.json b/version.json index 89deb3c..34106a3 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "name": "Plan2Code", - "version": "1.6.1", + "version": "1.7.0", "description": "A structured 4-step workflow methodology for AI-assisted software development", "keywords": [ "ai", @@ -17,5 +17,7 @@ "url": "https://github.com/jparkerweb/plan2code" }, "homepage": "https://plan2code.jparkerweb.com", + "releaseDate": "2026-02-20", "mode": "utility" } +