diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ded7edbe..7905c9587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] -### Fixed -- `gsd-verifier` agent now declares the Write tool, avoiding Bash heredoc fallbacks that could corrupt editor permission files when generating VERIFICATION.md reports. - ## [1.18.0] - 2026-02-08 ### Added @@ -295,7 +292,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Fixed - Installation on WSL2/non-TTY terminals now works correctly - detects non-interactive stdin and falls back to global install automatically - Installation now verifies files were actually copied before showing success checkmarks -- Orphaned `gsd-notify.sh` hook from previous versions is ahora automatically removed during install (both file and settings.json registration) +- Orphaned `gsd-notify.sh` hook from previous versions is now automatically removed during install (both file and settings.json registration) ## [1.6.3] - 2025-01-17 @@ -330,21 +327,21 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **BREAKING:** `/gsd:research-project` — integrated into project/milestone flows ### Added -- `/gsd:verify-work` ahora includes next-step routing after verification completes +- `/gsd:verify-work` now includes next-step routing after verification completes ## [1.5.30] - 2026-01-17 ### Fixed - Output templates in `plan-phase`, `execute-phase`, and `audit-milestone` now render markdown correctly instead of showing literal backticks -- Next-step suggestions ahora consistently recommend `/gsd:discuss-phase` before `/gsd:plan-phase` across all routing paths +- Next-step suggestions now consistently recommend `/gsd:discuss-phase` before `/gsd:plan-phase` across all routing paths ## [1.5.29] - 2025-01-16 ### Changed -- Discuss-phase ahora uses domain-aware questioning with deeper probing for gray areas +- Discuss-phase now uses domain-aware questioning with deeper probing for gray areas ### Fixed -- Windows hooks ahora work via Node.js conversion (statusline, update-check) +- Windows hooks now work via Node.js conversion (statusline, update-check) - Phase input normalization at command entry points - Removed blocking notification popups (gsd-notify) on all platforms @@ -356,36 +353,36 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **BREAKING:** Removed `/gsd:execute-plan` command (use `/gsd:execute-phase` instead) ### Fixed -- Phase directory matching ahora handles both zero-padded (05-*) and unpadded (5-*) folder names +- Phase directory matching now handles both zero-padded (05-*) and unpadded (5-*) folder names - Map-codebase agent output collection ## [1.5.27] - 2026-01-16 ### Fixed -- Orchestrator corrections between executor completions are ahora committed (previously left uncommitted when orchestrator made small fixes between waves) +- Orchestrator corrections between executor completions are now committed (previously left uncommitted when orchestrator made small fixes between waves) ## [1.5.26] - 2026-01-16 ### Fixed -- Revised plans ahora get committed after checker feedback (previously sólo initial plans were committed, leaving revisions uncommitted) +- Revised plans now get committed after checker feedback (previously only initial plans were committed, leaving revisions uncommitted) ## [1.5.25] - 2026-01-16 ### Fixed - Stop notification hook no longer shows stale project state (now uses session-scoped todos only) -- Researcher agent ahora reliably loads CONTEXT.md from discuss-phase +- Researcher agent now reliably loads CONTEXT.md from discuss-phase ## [1.5.24] - 2026-01-16 ### Fixed -- Stop notification hook ahora correctly parses STATE.md fields (was always showing "Ready for input") -- Planner agent ahora reliably loads CONTEXT.md and RESEARCH.md files +- Stop notification hook now correctly parses STATE.md fields (was always showing "Ready for input") +- Planner agent now reliably loads CONTEXT.md and RESEARCH.md files ## [1.5.23] - 2025-01-16 ### Added - Cross-platform completion notification hook (Mac/Linux/Windows alerts when Claude stops) -- Phase researcher ahora loads CONTEXT.md from discuss-phase to focus research on user decisions +- Phase researcher now loads CONTEXT.md from discuss-phase to focus research on user decisions ### Fixed - Consistent zero-padding for phase directories (01-name, not 1-name) @@ -399,7 +396,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Statusline update indicator — shows `⬆ /gsd:update` when a new version is available ### Fixed -- Planner ahora updates ROADMAP.md placeholders after planning completes +- Planner now updates ROADMAP.md placeholders after planning completes ## [1.5.21] - 2026-01-16 @@ -408,12 +405,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Research synthesizer agent that consolidates parallel research into SUMMARY.md ### Changed -- **Unified `/gsd:new-project` flow** — Single command ahora handles questions → research → requirements → roadmap (~10 min) +- **Unified `/gsd:new-project` flow** — Single command now handles questions → research → requirements → roadmap (~10 min) - Simplified README to reflect streamlined workflow: new-project → plan-phase → execute-phase - Added optional `/gsd:discuss-phase` documentation for UI/UX/behavior decisions before planning ### Fixed -- verify-work ahora shows clear checkpoint box with action prompt ("Type 'pass' or describe what's wrong") +- verify-work now shows clear checkpoint box with action prompt ("Type 'pass' or describe what's wrong") - Planner uses correct `{phase}-{plan}-PLAN.md` naming convention - Planner no longer surfaces internal `user_setup` in output - Research synthesizer commits all research files together (not individually) @@ -423,7 +420,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.5.20] - 2026-01-16 ### Fixed -- Research no longer skipped based on premature "Research: Unlikely" predictions made during roadmap creation. The `--skip-research` flag provides explicit control cuando needed. +- Research no longer skipped based on premature "Research: Unlikely" predictions made during roadmap creation. The `--skip-research` flag provides explicit control when needed. ### Removed - `Research: Likely/Unlikely` fields from roadmap phase template @@ -436,13 +433,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - `/gsd:discuss-phase` redesigned with intelligent gray area analysis — analyzes phase to identify discussable areas (UI, UX, Behavior, etc.), presents multi-select for user control, deep-dives each area with focused questioning - Explicit scope guardrail prevents scope creep during discussion — captures deferred ideas without acting on them - CONTEXT.md template restructured for decisions (domain boundary, decisions by category, Claude's discretion, deferred ideas) -- Downstream awareness: discuss-phase ahora explicitly documents that CONTEXT.md feeds researcher and planner agents -- `/gsd:plan-phase` ahora integrates research — spawns `gsd-phase-researcher` before planning unless research exists or `--skip-research` flag used +- Downstream awareness: discuss-phase now explicitly documents that CONTEXT.md feeds researcher and planner agents +- `/gsd:plan-phase` now integrates research — spawns `gsd-phase-researcher` before planning unless research exists or `--skip-research` flag used ## [1.5.18] - 2026-01-16 ### Added -- **Plan verification loop** — Plans are ahora verified before execution with a planner → checker → revise cycle +- **Plan verification loop** — Plans are now verified before execution with a planner → checker → revise cycle - New `gsd-plan-checker` agent (744 lines) validates plans will achieve phase goals - Six verification dimensions: requirement coverage, task completeness, dependency correctness, key links, scope sanity, must_haves derivation - Max 3 revision iterations before user escalation @@ -481,10 +478,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Research subagent prompt template for context-only spawning ### Changed -- `/gsd:research-phase` refactored to thin orchestrator — ahora injects rich context (key insight framing, downstream consumer info, quality gates) to gsd-researcher agent +- `/gsd:research-phase` refactored to thin orchestrator — now injects rich context (key insight framing, downstream consumer info, quality gates) to gsd-researcher agent - `/gsd:research-project` refactored to spawn 4 parallel gsd-researcher agents with milestone-aware context (greenfield vs v1.1+) and roadmap implications guidance - `/gsd:debug` refactored to thin orchestrator (149 lines) — spawns gsd-debugger agent with full debugging expertise -- `/gsd:new-milestone` ahora explicitly references MILESTONE-CONTEXT.md +- `/gsd:new-milestone` now explicitly references MILESTONE-CONTEXT.md ### Deprecated - `workflows/research-phase.md` — consolidated into gsd-researcher agent @@ -497,36 +494,36 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.5.15] - 2025-01-15 ### Fixed -- **Agents ahora install correctly** — The `agents/` folder (gsd-executor, gsd-verifier, gsd-integration-checker, gsd-milestone-auditor) was missing from npm package, now included +- **Agents now install correctly** — The `agents/` folder (gsd-executor, gsd-verifier, gsd-integration-checker, gsd-milestone-auditor) was missing from npm package, now included ### Changed - Consolidated `/gsd:plan-fix` into `/gsd:plan-phase --gaps` for simpler workflow -- UAT file writes ahora batched instead of per-response for better performance +- UAT file writes now batched instead of per-response for better performance ## [1.5.14] - 2025-01-15 ### Fixed -- Plan-phase ahora always routes to `/gsd:execute-phase` after planning, even for single-plan phases +- Plan-phase now always routes to `/gsd:execute-phase` after planning, even for single-plan phases ## [1.5.13] - 2026-01-15 ### Fixed -- `/gsd:new-milestone` ahora presents research and requirements paths as equal options, matching `/gsd:new-project` format +- `/gsd:new-milestone` now presents research and requirements paths as equal options, matching `/gsd:new-project` format ## [1.5.12] - 2025-01-15 ### Changed - **Milestone cycle reworked for proper requirements flow:** - - `complete-milestone` ahora archives AND deletes ROADMAP.md and REQUIREMENTS.md (fresh for next milestone) - - `new-milestone` is ahora a "brownfield new-project" — updates PROJECT.md with new goals, routes to define-requirements - - `discuss-milestone` is ahora required before `new-milestone` (creates context file) + - `complete-milestone` now archives AND deletes ROADMAP.md and REQUIREMENTS.md (fresh for next milestone) + - `new-milestone` is now a "brownfield new-project" — updates PROJECT.md with new goals, routes to define-requirements + - `discuss-milestone` is now required before `new-milestone` (creates context file) - `research-project` is milestone-aware — focuses on new features, ignores already-validated requirements - `create-roadmap` continues phase numbering from previous milestone - Flow: complete → discuss → new-milestone → research → requirements → roadmap ### Fixed -- `MILESTONE-AUDIT.md` ahora versioned as `v{version}-MILESTONE-AUDIT.md` and archived on completion -- `progress` ahora correctly routes to `/gsd:discuss-milestone` when between milestones (Route F) +- `MILESTONE-AUDIT.md` now versioned as `v{version}-MILESTONE-AUDIT.md` and archived on completion +- `progress` now correctly routes to `/gsd:discuss-milestone` when between milestones (Route F) ## [1.5.11] - 2025-01-15 @@ -536,10 +533,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.5.10] - 2025-01-15 ### Changed -- Milestone audit ahora reads existing phase VERIFICATION.md files instead of re-verifying each phase, aggregates tech debt and deferred gaps, adds `tech_debt` status for non-blocking accumulated debt +- Milestone audit now reads existing phase VERIFICATION.md files instead of re-verifying each phase, aggregates tech debt and deferred gaps, adds `tech_debt` status for non-blocking accumulated debt ### Fixed -- VERIFICATION.md ahora included in phase completion commit alongside ROADMAP.md, STATE.md, and REQUIREMENTS.md +- VERIFICATION.md now included in phase completion commit alongside ROADMAP.md, STATE.md, and REQUIREMENTS.md ## [1.5.9] - 2025-01-15 @@ -549,7 +546,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Changed - Checkpoint display format improved with box headers and unmissable "→ YOUR ACTION:" prompts - Subagent colors updated (executor: yellow, integration-checker: blue) -- Execute-phase ahora recommends `/gsd:audit-milestone` when milestone completes +- Execute-phase now recommends `/gsd:audit-milestone` when milestone completes ### Fixed - Research-phase no longer gatekeeps by domain type @@ -574,11 +571,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Goal-backward planning reference: Documentation for deriving must-haves from goals ### Changed -- execute-plan and execute-phase ahora spawn `gsd-executor` subagent instead of using inline workflow +- execute-plan and execute-phase now spawn `gsd-executor` subagent instead of using inline workflow - Roadmap and planning workflows enhanced with goal-backward analysis ### Removed -- Obsolete templates (`checkpoint-resume.md`, `subagent-task-prompt.md`) — logic ahora lives in subagents +- Obsolete templates (`checkpoint-resume.md`, `subagent-task-prompt.md`) — logic now lives in subagents ### Fixed - Updated remaining `general-purpose` subagent references to use `gsd-executor` @@ -590,25 +587,25 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - README: Research recommended for quality; skip only for speed ### Fixed -- execute-phase: Phase metadata (timing, wave info) ahora bundled into single commit instead of separate commits +- execute-phase: Phase metadata (timing, wave info) now bundled into single commit instead of separate commits ## [1.5.5] - 2025-01-15 ### Changed -- README ahora documents the `research-project` → `define-requirements` flow (optional but recommended before `create-roadmap`) +- README now documents the `research-project` → `define-requirements` flow (optional but recommended before `create-roadmap`) - Commands section reorganized into 7 grouped tables (Setup, Execution, Verification, Milestones, Phase Management, Session, Utilities) for easier scanning -- Context Engineering table ahora includes `research/` and `REQUIREMENTS.md` +- Context Engineering table now includes `research/` and `REQUIREMENTS.md` ## [1.5.4] - 2025-01-15 ### Changed -- Research phase ahora loads REQUIREMENTS.md to focus research on concrete requirements (e.g., "email verification") rather than just high-level roadmap descriptions +- Research phase now loads REQUIREMENTS.md to focus research on concrete requirements (e.g., "email verification") rather than just high-level roadmap descriptions ## [1.5.3] - 2025-01-15 ### Changed -- **execute-phase narration**: Orchestrator ahora describes what each wave builds before spawning agents, and summarizes what was built after completion. No more staring at opaque status updates. -- **new-project flow**: Ahora offers two paths — research first (recommended) or define requirements directly (fast path for familiar domains) +- **execute-phase narration**: Orchestrator now describes what each wave builds before spawning agents, and summarizes what was built after completion. No more staring at opaque status updates. +- **new-project flow**: Now offers two paths — research first (recommended) or define requirements directly (fast path for familiar domains) - **define-requirements**: Works without prior research. Gathers requirements through conversation when FEATURES.md doesn't exist. ### Removed @@ -619,17 +616,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.5.2] - 2026-01-15 ### Added -- Requirements traceability: roadmap phases ahora include `Requirements:` field listing which REQ-IDs they cover +- Requirements traceability: roadmap phases now include `Requirements:` field listing which REQ-IDs they cover - plan-phase loads REQUIREMENTS.md and shows phase-specific requirements before planning -- Requirements automatically marked Complete cuando phase finishes +- Requirements automatically marked Complete when phase finishes ### Changed -- Workflow preferences (mode, depth, parallelization) ahora asked in single prompt instead of 3 separate questions +- Workflow preferences (mode, depth, parallelization) now asked in single prompt instead of 3 separate questions - define-requirements shows full requirements list inline before commit (not just counts) - Research-project and workflow aligned to both point to define-requirements as next step ### Fixed -- Requirements status ahora updated by orchestrator (commands) instead of subagent workflow, which couldn't determine phase completion +- Requirements status now updated by orchestrator (commands) instead of subagent workflow, which couldn't determine phase completion ## [1.5.1] - 2026-01-14 @@ -642,11 +639,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - New `/gsd:research-project` command for pre-roadmap ecosystem research — spawns parallel agents to investigate stack, features, architecture, and pitfalls before you commit to a roadmap - New `/gsd:define-requirements` command for scoping v1 requirements from research findings — transforms "what exists in this domain" into "what we're building" -- Requirements traceability: phases ahora map to specific requirement IDs with 100% coverage validation +- Requirements traceability: phases now map to specific requirement IDs with 100% coverage validation ### Changed -- **BREAKING:** New project flow is ahora: `new-project → research-project → define-requirements → create-roadmap` -- Roadmap creation ahora requires REQUIREMENTS.md and validates all v1 requirements are mapped to phases +- **BREAKING:** New project flow is now: `new-project → research-project → define-requirements → create-roadmap` +- Roadmap creation now requires REQUIREMENTS.md and validates all v1 requirements are mapped to phases - Simplified questioning in new-project to four essentials (vision, core priority, boundaries, constraints) ## [1.4.29] - 2026-01-14 @@ -659,7 +656,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Fixed - Restored comprehensive checkpoint documentation with full examples for verification, decisions, and auth gates - Fixed execute-plan command to use fresh continuation agents instead of broken resume pattern -- Rich checkpoint presentation formats ahora documented for all three checkpoint types +- Rich checkpoint presentation formats now documented for all three checkpoint types + +### Changed +- Slimmed execute-phase command to properly delegate checkpoint handling to workflow ## [1.4.27] - 2025-01-14 @@ -676,7 +676,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - New `/gsd:whats-new` command shows changes since your installed version - VERSION file written during installation for version tracking -- CHANGELOG.md ahora included in package installation +- CHANGELOG.md now included in package installation ## [1.4.24] - 2026-01-14 @@ -708,7 +708,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Fixed - Standardized debug file naming convention -- Debug workflow ahora invokes execute-plan correctly +- Debug workflow now invokes execute-plan correctly ## [1.4.19] - 2026-01-14 @@ -739,7 +739,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.4.14] - 2026-01-13 ### Changed -- YOLO mode is ahora recommended default in new-project +- YOLO mode is now recommended default in new-project ## [1.4.13] - 2026-01-13 @@ -750,12 +750,12 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.4.12] - 2026-01-13 ### Changed -- execute-phase is ahora recommended as primary execution command +- execute-phase is now recommended as primary execution command ## [1.4.11] - 2026-01-13 ### Fixed -- Checkpoints ahora use fresh continuation agents instead of resume +- Checkpoints now use fresh continuation agents instead of resume ## [1.4.10] - 2026-01-13 @@ -825,7 +825,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Changed - Renamed `execute-phase.md` workflow to `execute-plan.md` for clarity -- Plan frontmatter ahora includes `wave`, `depends_on`, `files_modified`, `autonomous` +- Plan frontmatter now includes `wave`, `depends_on`, `files_modified`, `autonomous` ## [1.4.0] - 2026-01-12 @@ -836,7 +836,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Agent history schema v1.2 with parallel execution support ### Changed -- Plans ahora puede specify wave numbers and dependencies +- Plans can now specify wave numbers and dependencies - execute-phase orchestrates multiple subagents in waves ## [1.3.34] - 2026-01-11 @@ -898,7 +898,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Claude Code marketplace plugin support ### Fixed -- Phase artifacts ahora committed when created +- Phase artifacts now committed when created ## [1.3.25] - 2026-01-06 @@ -1042,7 +1042,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Fixed - Permission errors for non-DSP users (removed shell context) -- First question is ahora freeform, not AskUserQuestion +- First question is now freeform, not AskUserQuestion ## [1.2.13] - 2025-12-17 @@ -1117,7 +1117,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [1.1.2] - 2025-12-15 ### Fixed -- YOLO mode ahora skips confirmation gates in plan-phase +- YOLO mode now skips confirmation gates in plan-phase ## [1.1.1] - 2025-12-15 diff --git a/README.md b/README.md index f934a0e0e..ad4a503d6 100644 --- a/README.md +++ b/README.md @@ -607,19 +607,14 @@ To remove GSD completely: ```bash # Global installs -npx get-shit-done-cc@latest --claude --global --uninstall -npx get-shit-done-cc@latest --opencode --global --uninstall -npx get-shit-done-cc@latest --gemini --global --uninstall +npx get-shit-done-cc --claude --global --uninstall +npx get-shit-done-cc --opencode --global --uninstall # Local installs (current project) -npx get-shit-done-cc@latest --claude --local --uninstall -npx get-shit-done-cc@latest --opencode --local --uninstall -npx get-shit-done-cc@latest --gemini --local --uninstall +npx get-shit-done-cc --claude --local --uninstall +npx get-shit-done-cc --opencode --local --uninstall ``` -> [!IMPORTANT] -> Always use `@latest` when uninstalling. If you installed an older version, npx may have cached it, and that version might not support the `--uninstall` flag. - This removes all GSD commands, agents, hooks, and settings while preserving your other configurations. --- diff --git a/agents/general-purpose.md b/agents/general-purpose.md deleted file mode 100644 index fe0b0e983..000000000 --- a/agents/general-purpose.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: general-purpose -description: A flexible agent that adopts personas based on task instructions. Used as a base for dynamic subagents. -tools: Read, Write, Bash, List, Grep, AskUser -color: grey ---- - -You are a helpful general-purpose assistant. -You are often used as a base agent that reads specific instructions from a file to adopt a new persona. -Always follow the instructions provided in the task prompt. -When asked to read a file for instructions, read it first and then execute the task as that role. diff --git a/agents/gsd-verifier.md b/agents/gsd-verifier.md index d952b326a..4b2e0babf 100644 --- a/agents/gsd-verifier.md +++ b/agents/gsd-verifier.md @@ -1,7 +1,7 @@ --- name: gsd-verifier description: Verifies phase goal achievement through goal-backward analysis. Checks codebase delivers what phase promised, not just that tasks completed. Creates VERIFICATION.md report. -tools: Read, Write, Bash, Grep, Glob +tools: Read, Bash, Grep, Glob color: green --- @@ -254,7 +254,7 @@ Run anti-pattern detection on each file: grep -n -E "TODO|FIXME|XXX|HACK|PLACEHOLDER" "$file" 2>/dev/null grep -n -E "placeholder|coming soon|will be here" "$file" -i 2>/dev/null # Empty implementations -grep -n -E "return null|return \{}|return \[]|=> \{}" "$file" 2>/dev/null +grep -n -E "return null|return \{\}|return \[\]|=> \{\}" "$file" 2>/dev/null # Console.log only implementations grep -n -B 2 -A 2 "console\.log" "$file" 2>/dev/null | grep -E "^\s*(const|function|=>)" ``` diff --git a/bin/install-vibe.js b/bin/install-vibe.js deleted file mode 100644 index 5fae5be57..000000000 --- a/bin/install-vibe.js +++ /dev/null @@ -1,208 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const os = require('os'); -const { execSync } = require('child_process'); - -const VIBE_HOME = process.env.VIBE_HOME || path.join(os.homedir(), '.vibe'); -const GSD_SRC = path.resolve(__dirname, '..'); -const GSD_DEST = path.join(VIBE_HOME, 'get-shit-done'); - -// Directories to create -const DIRS = [ - path.join(VIBE_HOME, 'skills'), - path.join(VIBE_HOME, 'agents'), - path.join(VIBE_HOME, 'prompts'), - GSD_DEST, - path.join(GSD_DEST, 'bin'), - path.join(GSD_DEST, 'workflows'), - path.join(GSD_DEST, 'templates'), - path.join(GSD_DEST, 'references'), -]; - -// Helper to copy directory recursively -function copyDir(src, dest) { - if (!fs.existsSync(src)) return; - if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true }); - - const entries = fs.readdirSync(src, { withFileTypes: true }); - for (const entry of entries) { - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, entry.name); - if (entry.isDirectory()) { - copyDir(srcPath, destPath); - } else { - fs.copyFileSync(srcPath, destPath); - } - } -} - -// 1. Setup Directories -function ensureDirectories() { - console.log('Setting up directories...'); - DIRS.forEach(dir => { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - console.log(`Created ${dir}`); - } - }); -} - -// 2. Copy Core Files -function copyCoreFiles() { - console.log('Copying core GSD files...'); - copyDir(path.join(GSD_SRC, 'get-shit-done/bin'), path.join(GSD_DEST, 'bin')); - copyDir(path.join(GSD_SRC, 'get-shit-done/templates'), path.join(GSD_DEST, 'templates')); - copyDir(path.join(GSD_SRC, 'get-shit-done/references'), path.join(GSD_DEST, 'references')); - - // Make bin executable - try { - if (fs.existsSync(path.join(GSD_DEST, 'bin'))) { - const binFiles = fs.readdirSync(path.join(GSD_DEST, 'bin')); - binFiles.forEach(f => fs.chmodSync(path.join(GSD_DEST, 'bin', f), '755')); - } - } catch (e) { - console.warn('Could not chmod bin files (expected on Windows).'); - } -} - -// 3. Convert Commands to Skills -function convertCommandsToSkills() { - console.log('Converting commands to skills...'); - const commandsDir = path.join(GSD_SRC, 'commands/gsd'); - if (fs.existsSync(commandsDir)) { - const files = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md')); - - files.forEach(file => { - const content = fs.readFileSync(path.join(commandsDir, file), 'utf8'); - const slug = file.replace('.md', ''); - const skillName = `gsd-${slug}`; - const skillDir = path.join(VIBE_HOME, 'skills', skillName); - - if (!fs.existsSync(skillDir)) fs.mkdirSync(skillDir, { recursive: true }); - - // Transform content - let newContent = content - .replace(/~\/\.claude/g, '~/.vibe') - .replace(/name: gsd:([\w-]+)/, `name: gsd-$1\nuser-invocable: true`) - .replace(/Task\(prompt="(?[\s\S]*?)", subagent_type="(?[\w-]+)",.*?\)/g, (match, prompt, agent) => { - return `task(task="${prompt.replace(/"/g, '\\"')}", agent="${agent}")`; - }); - - // Fix allowed tools - const toolMap = { - 'Read': 'read_file', - 'Write': 'write_file', - 'Bash': 'bash', - 'Glob': 'list_dir', - 'Grep': 'grep', - 'AskUserQuestion': 'ask_user_question', - 'Task': 'task' - }; - - newContent = newContent.replace(/allowed-tools:\s*\n((?:\s+-\s+\w+\n?)+)/, (match, toolsBlock) => { - const tools = toolsBlock.split('\n') - .map(t => t.trim().replace('- ', '')) - .filter(t => t) - .map(t => toolMap[t] || t) - .map(t => ` - ${t}`) - .join('\n'); - return `allowed-tools:\n${tools}\n`; - }); - - fs.writeFileSync(path.join(skillDir, 'SKILL.md'), newContent); - console.log(`Created skill: ${skillName}`); - }); - } -} - -// 4. Convert Agents -function convertAgents() { - console.log('Converting agents...'); - const agentsDir = path.join(GSD_SRC, 'agents'); - if (fs.existsSync(agentsDir)) { - const files = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')); - - files.forEach(file => { - const content = fs.readFileSync(path.join(agentsDir, file), 'utf8'); - const agentName = file.replace('.md', ''); - - // 4.1 Write System Prompt - const promptPath = path.join(VIBE_HOME, 'prompts', `${agentName}.md`); - // Remove frontmatter for the prompt file - const promptContent = content.replace(/^---\n[\s\S]+?\n---\n/, ''); - fs.writeFileSync(promptPath, promptContent); - - // 4.2 Write Agent TOML - const tomlPath = path.join(VIBE_HOME, 'agents', `${agentName}.toml`); - const tomlContent = ` -system_prompt_id = "${agentName}" -active_model = "mistral-large-latest" -agent_type = "subagent" - -[tools] -read_file.permission = "always" -write_file.permission = "always" -bash.permission = "always" -grep.permission = "always" -list_dir.permission = "always" -ask_user_question.permission = "always" -`; - fs.writeFileSync(tomlPath, tomlContent.trim()); - console.log(`Created agent: ${agentName}`); - }); - } -} - - -// 5. Patch Workflows -function patchWorkflows() { - console.log('Patching workflows...'); - const workflowsSrc = path.join(GSD_SRC, 'get-shit-done/workflows'); - const workflowsDest = path.join(GSD_DEST, 'workflows'); - - if (fs.existsSync(workflowsSrc)) { - const files = fs.readdirSync(workflowsSrc).filter(f => f.endsWith('.md')); - - files.forEach(file => { - let content = fs.readFileSync(path.join(workflowsSrc, file), 'utf8'); - - // Patch paths - content = content.replace(/~\/\.claude/g, '~/.vibe'); - - // Patch Task calls - content = content.replace(/Task\(\s*prompt\s*=\s*"([\s\S]*?)",\s*subagent_type\s*=\s*"([^"]+)"(?:,\s*model\s*=\s*"[^"]+")?\s*\)/g, (match, prompt, agent) => { - return `task(task="${prompt}", agent="${agent}")`; - }); - - // Fix path to agent prompts (which are in prompts/ dir, not agents/ dir) - content = content.replace(/~\/\.vibe\/agents\/([a-zA-Z0-9_-]+\.md)/g, '~/.vibe/prompts/$1'); - - fs.writeFileSync(path.join(workflowsDest, file), content); - console.log(`Patched workflow: ${file}`); - }); - } -} - -function install() { - console.log('Starting GSD Vibe installation...'); - - ensureDirectories(); - copyCoreFiles(); - convertCommandsToSkills(); - convertAgents(); - patchWorkflows(); - - console.log('Installation complete! Please restart Vibe CLI.'); - return { runtime: 'vibe', success: true }; -} - -module.exports = { install }; - -if (require.main === module) { - try { - install(); - } catch (err) { - console.error('Installation failed:', err); - process.exit(1); - } -} diff --git a/bin/install.js b/bin/install.js index 1ffee7206..42cea961c 100755 --- a/bin/install.js +++ b/bin/install.js @@ -15,7 +15,6 @@ const reset = '\x1b[0m'; // Get version from package.json const pkg = require('../package.json'); -const { install: installVibe } = require('./install-vibe'); // Parse args const args = process.argv.slice(2); @@ -24,7 +23,6 @@ const hasLocal = args.includes('--local') || args.includes('-l'); const hasOpencode = args.includes('--opencode'); const hasClaude = args.includes('--claude'); const hasGemini = args.includes('--gemini'); -const hasVibe = args.includes('--vibe'); const hasBoth = args.includes('--both'); // Legacy flag, keeps working const hasAll = args.includes('--all'); const hasUninstall = args.includes('--uninstall') || args.includes('-u'); @@ -32,21 +30,19 @@ const hasUninstall = args.includes('--uninstall') || args.includes('-u'); // Runtime selection - can be set by flags or interactive prompt let selectedRuntimes = []; if (hasAll) { - selectedRuntimes = ['claude', 'opencode', 'gemini', 'vibe']; + selectedRuntimes = ['claude', 'opencode', 'gemini']; } else if (hasBoth) { selectedRuntimes = ['claude', 'opencode']; } else { if (hasOpencode) selectedRuntimes.push('opencode'); if (hasClaude) selectedRuntimes.push('claude'); if (hasGemini) selectedRuntimes.push('gemini'); - if (hasVibe) selectedRuntimes.push('vibe'); } // Helper to get directory name for a runtime (used for local/project installs) function getDirName(runtime) { if (runtime === 'opencode') return '.opencode'; if (runtime === 'gemini') return '.gemini'; - if (runtime === 'vibe') return '.vibe'; return '.claude'; } @@ -60,17 +56,17 @@ function getOpencodeGlobalDir() { if (process.env.OPENCODE_CONFIG_DIR) { return expandTilde(process.env.OPENCODE_CONFIG_DIR); } - + // 2. OPENCODE_CONFIG env var (use its directory) if (process.env.OPENCODE_CONFIG) { return path.dirname(expandTilde(process.env.OPENCODE_CONFIG)); } - + // 3. XDG_CONFIG_HOME/opencode if (process.env.XDG_CONFIG_HOME) { return path.join(expandTilde(process.env.XDG_CONFIG_HOME), 'opencode'); } - + // 4. Default: ~/.config/opencode (XDG default) return path.join(os.homedir(), '.config', 'opencode'); } @@ -88,7 +84,7 @@ function getGlobalDir(runtime, explicitDir = null) { } return getOpencodeGlobalDir(); } - + if (runtime === 'gemini') { // Gemini: --config-dir > GEMINI_CONFIG_DIR > ~/.gemini if (explicitDir) { @@ -99,14 +95,7 @@ function getGlobalDir(runtime, explicitDir = null) { } return path.join(os.homedir(), '.gemini'); } - - if (runtime === 'vibe') { - if (process.env.VIBE_HOME) { - return expandTilde(process.env.VIBE_HOME); - } - return path.join(os.homedir(), '.vibe'); - } - + // Claude Code: --config-dir > CLAUDE_CONFIG_DIR > ~/.claude if (explicitDir) { return expandTilde(explicitDir); @@ -571,7 +560,7 @@ function convertClaudeToGeminiToml(content) { const frontmatter = content.substring(3, endIndex).trim(); const body = content.substring(endIndex + 3).trim(); - + // Extract description from frontmatter let description = ''; const lines = frontmatter.split('\n'); @@ -588,9 +577,9 @@ function convertClaudeToGeminiToml(content) { if (description) { toml += `description = ${JSON.stringify(description)}\n`; } - + toml += `prompt = ${JSON.stringify(body)}\n`; - + return toml; } @@ -609,7 +598,7 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) { if (!fs.existsSync(srcDir)) { return; } - + // Remove old gsd-*.md files before copying new ones if (fs.existsSync(destDir)) { for (const file of fs.readdirSync(destDir)) { @@ -620,12 +609,12 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) { } else { fs.mkdirSync(destDir, { recursive: true }); } - + const entries = fs.readdirSync(srcDir, { withFileTypes: true }); - + for (const entry of entries) { const srcPath = path.join(srcDir, entry.name); - + if (entry.isDirectory()) { // Recurse into subdirectories, adding to prefix // e.g., commands/gsd/debug/start.md -> command/gsd-debug-start.md @@ -764,8 +753,8 @@ function cleanupOrphanedHooks(settings) { // Fix #330: Update statusLine if it points to old statusline.js path if (settings.statusLine && settings.statusLine.command && - settings.statusLine.command.includes('statusline.js') && - !settings.statusLine.command.includes('gsd-statusline.js')) { + settings.statusLine.command.includes('statusline.js') && + !settings.statusLine.command.includes('gsd-statusline.js')) { // Replace old path with new path settings.statusLine.command = settings.statusLine.command.replace( /statusline\.js/, @@ -886,7 +875,7 @@ function uninstall(isGlobal, runtime = 'claude') { // Remove GSD statusline if it references our hook if (settings.statusLine && settings.statusLine.command && - settings.statusLine.command.includes('gsd-statusline')) { + settings.statusLine.command.includes('gsd-statusline')) { delete settings.statusLine; settingsModified = true; console.log(` ${green}✓${reset} Removed GSD statusline from settings`); @@ -1076,7 +1065,7 @@ function configureOpencodePermissions() { const gsdPath = opencodeConfigDir === defaultConfigDir ? '~/.config/opencode/get-shit-done/*' : `${opencodeConfigDir.replace(/\\/g, '/')}/get-shit-done/*`; - + let modified = false; // Configure read permission @@ -1320,7 +1309,7 @@ function install(isGlobal, runtime = 'claude') { // OpenCode: flat structure in command/ directory const commandDir = path.join(targetDir, 'command'); fs.mkdirSync(commandDir, { recursive: true }); - + // Copy commands/gsd/*.md as command/gsd-*.md (flatten structure) const gsdSrc = path.join(src, 'commands', 'gsd'); copyFlattenedCommands(gsdSrc, commandDir, 'gsd', pathPrefix, runtime); @@ -1334,7 +1323,7 @@ function install(isGlobal, runtime = 'claude') { // Claude Code & Gemini: nested structure in commands/ directory const commandsDir = path.join(targetDir, 'commands'); fs.mkdirSync(commandsDir, { recursive: true }); - + const gsdSrc = path.join(src, 'commands', 'gsd'); const gsdDest = path.join(commandsDir, 'gsd'); copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime); @@ -1417,46 +1406,20 @@ function install(isGlobal, runtime = 'claude') { } // Copy hooks from dist/ (bundled with dependencies) - // Dev installs from a git clone may not have hooks/dist yet. - // In that case, build it on the fly so hooks still install. - const hooksDist = path.join(src, 'hooks', 'dist'); - const hooksDir = path.join(src, 'hooks'); - - if (!fs.existsSync(hooksDist) && fs.existsSync(hooksDir)) { - try { - const buildScript = path.join(src, 'scripts', 'build-hooks.js'); - if (fs.existsSync(buildScript)) { - require(buildScript); - } - } catch (e) { - // fall through, we'll attempt a direct copy below - } - - // Fallback: copy known hooks into dist if they exist - if (!fs.existsSync(hooksDist)) { - fs.mkdirSync(hooksDist, { recursive: true }); - for (const hookName of ['gsd-check-update.js', 'gsd-statusline.js']) { - const inRepoHook = path.join(hooksDir, hookName); - if (fs.existsSync(inRepoHook)) { - fs.copyFileSync(inRepoHook, path.join(hooksDist, hookName)); - } - } - } - } - - if (fs.existsSync(hooksDist)) { + const hooksSrc = path.join(src, 'hooks', 'dist'); + if (fs.existsSync(hooksSrc)) { const hooksDest = path.join(targetDir, 'hooks'); fs.mkdirSync(hooksDest, { recursive: true }); - const hookEntries = fs.readdirSync(hooksDist); + const hookEntries = fs.readdirSync(hooksSrc); for (const entry of hookEntries) { - const srcFile = path.join(hooksDist, entry); + const srcFile = path.join(hooksSrc, entry); if (fs.statSync(srcFile).isFile()) { const destFile = path.join(hooksDest, entry); fs.copyFileSync(srcFile, destFile); } } if (verifyInstalled(hooksDest, 'hooks')) { - console.log(` ${green}✓${reset} Installed hooks`); + console.log(` ${green}✓${reset} Installed hooks (bundled)`); } else { failures.push('hooks'); } @@ -1575,17 +1538,6 @@ function handleStatusline(settings, isInteractive, callback) { return; } - // Auto-migrate renamed GSD statusline (hooks/statusline.js -> hooks/gsd-statusline.js) - // Only migrate if it looks like GSD's statusline (node command with hooks/statusline.js path) - const existingCmd = settings.statusLine.command || ''; - const isOldGsdStatusline = existingCmd.includes('node') && - (existingCmd.includes('hooks/statusline.js') || existingCmd.includes('hooks\\statusline.js')); - if (isOldGsdStatusline) { - console.log(` ${green}✓${reset} Migrating statusline.js → gsd-statusline.js`); - callback(true); - return; - } - if (!isInteractive) { console.log(` ${yellow}⚠${reset} Skipping statusline (already configured)`); console.log(` Use ${cyan}--force-statusline${reset} to replace\n`); @@ -1593,7 +1545,7 @@ function handleStatusline(settings, isInteractive, callback) { return; } - const displayCmd = settings.statusLine.command || settings.statusLine.url || '(custom)'; + const existingCmd = settings.statusLine.command || settings.statusLine.url || '(custom)'; const rl = readline.createInterface({ input: process.stdin, @@ -1603,7 +1555,7 @@ function handleStatusline(settings, isInteractive, callback) { console.log(` ${yellow}⚠${reset} Existing statusline detected\n Your current statusline: - ${dim}command: ${displayCmd}${reset} + ${dim}command: ${existingCmd}${reset} GSD includes a statusline showing: • Model name @@ -1644,7 +1596,6 @@ function promptRuntime(callback) { ${cyan}2${reset}) OpenCode ${dim}(~/.config/opencode)${reset} - open source, free models ${cyan}3${reset}) Gemini ${dim}(~/.gemini)${reset} ${cyan}4${reset}) All - ${cyan}5${reset}) Mistral Vibe `); rl.question(` Choice ${dim}[1]${reset}: `, (answer) => { @@ -1652,13 +1603,11 @@ function promptRuntime(callback) { rl.close(); const choice = answer.trim() || '1'; if (choice === '4') { - callback(['claude', 'opencode', 'gemini', 'vibe']); + callback(['claude', 'opencode', 'gemini']); } else if (choice === '3') { callback(['gemini']); } else if (choice === '2') { callback(['opencode']); - } else if (choice === '5') { - callback(['vibe']); } else { callback(['claude']); } @@ -1717,15 +1666,6 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) { const results = []; for (const runtime of runtimes) { - if (runtime === 'vibe') { - try { - const result = installVibe(); - results.push(result); - } catch (e) { - console.error('Vibe installation failed', e); - } - continue; - } const result = install(isGlobal, runtime); results.push(result); } @@ -1736,30 +1676,28 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) { // Logic: if both are present, ask once if interactive? Or ask for each? // Simpler: Ask once and apply to both if applicable. - + if (claudeResult || geminiResult) { // Use whichever settings exist to check for existing statusline const primaryResult = claudeResult || geminiResult; - + handleStatusline(primaryResult.settings, isInteractive, (shouldInstallStatusline) => { if (claudeResult) { finishInstall(claudeResult.settingsPath, claudeResult.settings, claudeResult.statuslineCommand, shouldInstallStatusline, 'claude'); } if (geminiResult) { - finishInstall(geminiResult.settingsPath, geminiResult.settings, geminiResult.statuslineCommand, shouldInstallStatusline, 'gemini'); + finishInstall(geminiResult.settingsPath, geminiResult.settings, geminiResult.statuslineCommand, shouldInstallStatusline, 'gemini'); } - + const opencodeResult = results.find(r => r.runtime === 'opencode'); if (opencodeResult) { finishInstall(opencodeResult.settingsPath, opencodeResult.settings, opencodeResult.statuslineCommand, false, 'opencode'); } }); } else { - // Only OpenCode or Vibe - const opencodeResult = results.find(r => r.runtime === 'opencode'); - if (opencodeResult) { - finishInstall(opencodeResult.settingsPath, opencodeResult.settings, opencodeResult.statuslineCommand, false, 'opencode'); - } + // Only OpenCode + const opencodeResult = results[0]; + finishInstall(opencodeResult.settingsPath, opencodeResult.settings, opencodeResult.statuslineCommand, false, 'opencode'); } } diff --git a/commands/gsd/debug.md b/commands/gsd/debug.md index 3ca0fa5de..23dfa11e9 100644 --- a/commands/gsd/debug.md +++ b/commands/gsd/debug.md @@ -151,15 +151,6 @@ Task( ) ``` -## 6. Close Debug Session - -After fix is complete (or user chooses "Plan fix" / "Manual fix"), move debug file to resolved if the agent didn't already: - -```bash -mkdir -p .planning/debug/resolved -mv .planning/debug/{slug}.md .planning/debug/resolved/ 2>/dev/null -``` - @@ -168,5 +159,4 @@ mv .planning/debug/{slug}.md .planning/debug/resolved/ 2>/dev/null - [ ] gsd-debugger spawned with context - [ ] Checkpoints handled correctly - [ ] Root cause confirmed before fixing -- [ ] Resolved sessions moved to .planning/debug/resolved/ diff --git a/commands/gsd/verify-work.md b/commands/gsd/verify-work.md index d117ea8d9..b5c9e13bc 100644 --- a/commands/gsd/verify-work.md +++ b/commands/gsd/verify-work.md @@ -14,9 +14,9 @@ allowed-tools: Validate built features through conversational testing with persistent state. -Purpose: Confirm what Claude built actually works from user's perspective. One test at a time, plain text responses, no interrogation. When issues are found, record them in UAT.md and route user to /gsd:plan-phase --gaps in a fresh context. +Purpose: Confirm what Claude built actually works from user's perspective. One test at a time, plain text responses, no interrogation. When issues are found, automatically diagnose, plan fixes, and prepare for execution. -Output: {phase}-UAT.md tracking all test results. If issues found: structured gaps ready for /gsd:plan-phase --gaps +Output: {phase}-UAT.md tracking all test results. If issues found: diagnosed gaps, verified fix plans ready for /gsd:execute-phase @@ -35,5 +35,5 @@ Phase: $ARGUMENTS (optional) Execute the verify-work workflow from @~/.claude/get-shit-done/workflows/verify-work.md end-to-end. -Preserve all workflow gates (session management, test presentation, gap recording, routing). +Preserve all workflow gates (session management, test presentation, diagnosis, fix planning, routing). diff --git a/get-shit-done/bin/gsd-tools.js b/get-shit-done/bin/gsd-tools.js index 840905b70..b04e65b93 100755 --- a/get-shit-done/bin/gsd-tools.js +++ b/get-shit-done/bin/gsd-tools.js @@ -51,8 +51,7 @@ * progress [json|table|bar] Render progress in various formats * * Todos: - * todo start Move todo from pending to in-progress - * todo complete Move todo from in-progress (or pending) to done + * todo complete Move todo from pending to completed * * Scaffolding: * scaffold context --phase Create CONTEXT.md template @@ -161,7 +160,6 @@ function loadConfig(cwd) { model_profile: 'balanced', commit_docs: true, search_gitignored: false, - git_tag: true, branching_strategy: 'none', phase_branch_template: 'gsd/phase-{phase}-{slug}', milestone_branch_template: 'gsd/{milestone}-{slug}', @@ -195,7 +193,6 @@ function loadConfig(cwd) { model_profile: get('model_profile') ?? defaults.model_profile, commit_docs: get('commit_docs', { section: 'planning', field: 'commit_docs' }) ?? defaults.commit_docs, search_gitignored: get('search_gitignored', { section: 'planning', field: 'search_gitignored' }) ?? defaults.search_gitignored, - git_tag: get('git_tag', { section: 'planning', field: 'git_tag' }) ?? defaults.git_tag, branching_strategy: get('branching_strategy', { section: 'git', field: 'branching_strategy' }) ?? defaults.branching_strategy, phase_branch_template: get('phase_branch_template', { section: 'git', field: 'phase_branch_template' }) ?? defaults.phase_branch_template, milestone_branch_template: get('milestone_branch_template', { section: 'git', field: 'milestone_branch_template' }) ?? defaults.milestone_branch_template, @@ -518,12 +515,9 @@ function cmdCurrentTimestamp(format, raw) { function cmdListTodos(cwd, area, raw) { const pendingDir = path.join(cwd, '.planning', 'todos', 'pending'); - const inProgressDir = path.join(cwd, '.planning', 'todos', 'in-progress'); let count = 0; const todos = []; - let inProgressCount = 0; - const inProgressTodos = []; try { const files = fs.readdirSync(pendingDir).filter(f => f.endsWith('.md')); @@ -552,35 +546,7 @@ function cmdListTodos(cwd, area, raw) { } } catch {} - try { - const files = fs.readdirSync(inProgressDir).filter(f => f.endsWith('.md')); - - for (const file of files) { - try { - const content = fs.readFileSync(path.join(inProgressDir, file), 'utf-8'); - const createdMatch = content.match(/^created:\s*(.+)$/m); - const titleMatch = content.match(/^title:\s*(.+)$/m); - const areaMatch = content.match(/^area:\s*(.+)$/m); - const startedMatch = content.match(/^started:\s*(.+)$/m); - - const todoArea = areaMatch ? areaMatch[1].trim() : 'general'; - - if (area && todoArea !== area) continue; - - inProgressCount++; - inProgressTodos.push({ - file, - created: createdMatch ? createdMatch[1].trim() : 'unknown', - started: startedMatch ? startedMatch[1].trim() : 'unknown', - title: titleMatch ? titleMatch[1].trim() : 'Untitled', - area: todoArea, - path: path.join('.planning', 'todos', 'in-progress', file), - }); - } catch {} - } - } catch {} - - const result = { count, todos, in_progress_count: inProgressCount, in_progress_todos: inProgressTodos }; + const result = { count, todos }; output(result, raw, count.toString()); } @@ -632,7 +598,6 @@ function cmdConfigEnsureSection(cwd, raw) { model_profile: 'balanced', commit_docs: true, search_gitignored: false, - git_tag: true, branching_strategy: 'none', phase_branch_template: 'gsd/phase-{phase}-{slug}', milestone_branch_template: 'gsd/{milestone}-{slug}', @@ -3418,35 +3383,6 @@ function cmdProgressRender(cwd, format, raw) { } } -// ─── Todo Start ────────────────────────────────────────────────────────────── - -function cmdTodoStart(cwd, filename, raw) { - if (!filename) { - error('filename required for todo start'); - } - - const pendingDir = path.join(cwd, '.planning', 'todos', 'pending'); - const inProgressDir = path.join(cwd, '.planning', 'todos', 'in-progress'); - const sourcePath = path.join(pendingDir, filename); - - if (!fs.existsSync(sourcePath)) { - error(`Todo not found: ${filename}`); - } - - // Ensure in-progress directory exists - fs.mkdirSync(inProgressDir, { recursive: true }); - - // Read, add started timestamp, move - let content = fs.readFileSync(sourcePath, 'utf-8'); - const today = new Date().toISOString().split('T')[0]; - content = `started: ${today}\n` + content; - - fs.writeFileSync(path.join(inProgressDir, filename), content, 'utf-8'); - fs.unlinkSync(sourcePath); - - output({ started: true, file: filename, date: today }, raw, 'started'); -} - // ─── Todo Complete ──────────────────────────────────────────────────────────── function cmdTodoComplete(cwd, filename, raw) { @@ -3454,28 +3390,23 @@ function cmdTodoComplete(cwd, filename, raw) { error('filename required for todo complete'); } - const inProgressDir = path.join(cwd, '.planning', 'todos', 'in-progress'); const pendingDir = path.join(cwd, '.planning', 'todos', 'pending'); - const doneDir = path.join(cwd, '.planning', 'todos', 'done'); + const completedDir = path.join(cwd, '.planning', 'todos', 'completed'); + const sourcePath = path.join(pendingDir, filename); - // Check in-progress first, then fall back to pending - let sourcePath = path.join(inProgressDir, filename); if (!fs.existsSync(sourcePath)) { - sourcePath = path.join(pendingDir, filename); - if (!fs.existsSync(sourcePath)) { - error(`Todo not found: ${filename}`); - } + error(`Todo not found: ${filename}`); } - // Ensure done directory exists - fs.mkdirSync(doneDir, { recursive: true }); + // Ensure completed directory exists + fs.mkdirSync(completedDir, { recursive: true }); // Read, add completion timestamp, move let content = fs.readFileSync(sourcePath, 'utf-8'); const today = new Date().toISOString().split('T')[0]; content = `completed: ${today}\n` + content; - fs.writeFileSync(path.join(doneDir, filename), content, 'utf-8'); + fs.writeFileSync(path.join(completedDir, filename), content, 'utf-8'); fs.unlinkSync(sourcePath); output({ completed: true, file: filename, date: today }, raw, 'completed'); @@ -3721,7 +3652,6 @@ function cmdInitPlanPhase(cwd, phase, includes, raw) { research_enabled: config.research, plan_checker_enabled: config.plan_checker, commit_docs: config.commit_docs, - branching_strategy: config.branching_strategy, // Phase info phase_found: !!phaseInfo, @@ -3731,17 +3661,6 @@ function cmdInitPlanPhase(cwd, phase, includes, raw) { phase_slug: phaseInfo?.phase_slug || null, padded_phase: phaseInfo?.phase_number?.padStart(2, '0') || null, - // Branch name (pre-computed) — ensures early branching before first commit - branch_name: config.branching_strategy === 'phase' && phaseInfo - ? config.phase_branch_template - .replace('{phase}', phaseInfo.phase_number) - .replace('{slug}', phaseInfo.phase_slug || 'phase') - : config.branching_strategy === 'milestone' - ? config.milestone_branch_template - .replace('{milestone}', getMilestoneInfo(cwd).version) - .replace('{slug}', generateSlugInternal(getMilestoneInfo(cwd).name) || 'milestone') - : null, - // Existing artifacts has_research: phaseInfo?.has_research || false, has_context: phaseInfo?.has_context || false, @@ -4000,13 +3919,11 @@ function cmdInitVerifyWork(cwd, phase, raw) { function cmdInitPhaseOp(cwd, phase, raw) { const config = loadConfig(cwd); const phaseInfo = findPhaseInternal(cwd, phase); - const milestone = getMilestoneInfo(cwd); const result = { // Config commit_docs: config.commit_docs, brave_search: config.brave_search, - branching_strategy: config.branching_strategy, // Phase info phase_found: !!phaseInfo, @@ -4016,17 +3933,6 @@ function cmdInitPhaseOp(cwd, phase, raw) { phase_slug: phaseInfo?.phase_slug || null, padded_phase: phaseInfo?.phase_number?.padStart(2, '0') || null, - // Branch name (pre-computed) — ensures early branching before first commit - branch_name: config.branching_strategy === 'phase' && phaseInfo - ? config.phase_branch_template - .replace('{phase}', phaseInfo.phase_number) - .replace('{slug}', phaseInfo.phase_slug || 'phase') - : config.branching_strategy === 'milestone' - ? config.milestone_branch_template - .replace('{milestone}', milestone.version) - .replace('{slug}', generateSlugInternal(milestone.name) || 'milestone') - : null, - // Existing artifacts has_research: phaseInfo?.has_research || false, has_context: phaseInfo?.has_context || false, @@ -4046,7 +3952,7 @@ function cmdInitTodos(cwd, area, raw) { const config = loadConfig(cwd); const now = new Date(); - // List pending todos + // List todos (reuse existing logic) const pendingDir = path.join(cwd, '.planning', 'todos', 'pending'); let count = 0; const todos = []; @@ -4075,37 +3981,6 @@ function cmdInitTodos(cwd, area, raw) { } } catch {} - // List in-progress todos - const inProgressDir = path.join(cwd, '.planning', 'todos', 'in-progress'); - let inProgressCount = 0; - const inProgressTodos = []; - - try { - const files = fs.readdirSync(inProgressDir).filter(f => f.endsWith('.md')); - for (const file of files) { - try { - const content = fs.readFileSync(path.join(inProgressDir, file), 'utf-8'); - const createdMatch = content.match(/^created:\s*(.+)$/m); - const titleMatch = content.match(/^title:\s*(.+)$/m); - const areaMatch = content.match(/^area:\s*(.+)$/m); - const startedMatch = content.match(/^started:\s*(.+)$/m); - const todoArea = areaMatch ? areaMatch[1].trim() : 'general'; - - if (area && todoArea !== area) continue; - - inProgressCount++; - inProgressTodos.push({ - file, - created: createdMatch ? createdMatch[1].trim() : 'unknown', - started: startedMatch ? startedMatch[1].trim() : 'unknown', - title: titleMatch ? titleMatch[1].trim() : 'Untitled', - area: todoArea, - path: path.join('.planning', 'todos', 'in-progress', file), - }); - } catch {} - } - } catch {} - const result = { // Config commit_docs: config.commit_docs, @@ -4117,20 +3992,16 @@ function cmdInitTodos(cwd, area, raw) { // Todo inventory todo_count: count, todos, - in_progress_count: inProgressCount, - in_progress_todos: inProgressTodos, area_filter: area || null, // Paths pending_dir: '.planning/todos/pending', - in_progress_dir: '.planning/todos/in-progress', - done_dir: '.planning/todos/done', + completed_dir: '.planning/todos/completed', // File existence planning_exists: pathExistsInternal(cwd, '.planning'), todos_dir_exists: pathExistsInternal(cwd, '.planning/todos'), pending_dir_exists: pathExistsInternal(cwd, '.planning/todos/pending'), - in_progress_dir_exists: pathExistsInternal(cwd, '.planning/todos/in-progress'), }; output(result, raw); @@ -4623,12 +4494,10 @@ async function main() { case 'todo': { const subcommand = args[1]; - if (subcommand === 'start') { - cmdTodoStart(cwd, args[2], raw); - } else if (subcommand === 'complete') { + if (subcommand === 'complete') { cmdTodoComplete(cwd, args[2], raw); } else { - error('Unknown todo subcommand. Available: start, complete'); + error('Unknown todo subcommand. Available: complete'); } break; } diff --git a/get-shit-done/bin/gsd-tools.test.js b/get-shit-done/bin/gsd-tools.test.js index 42c344dc8..c79786433 100644 --- a/get-shit-done/bin/gsd-tools.test.js +++ b/get-shit-done/bin/gsd-tools.test.js @@ -1885,60 +1885,6 @@ describe('progress command', () => { }); }); -// ───────────────────────────────────────────────────────────────────────────── -// todo start command -// ───────────────────────────────────────────────────────────────────────────── - -describe('todo start command', () => { - let tmpDir; - - beforeEach(() => { - tmpDir = createTempProject(); - }); - - afterEach(() => { - cleanup(tmpDir); - }); - - test('moves todo from pending to in-progress', () => { - const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending'); - fs.mkdirSync(pendingDir, { recursive: true }); - fs.writeFileSync( - path.join(pendingDir, 'add-dark-mode.md'), - `title: Add dark mode\narea: ui\ncreated: 2025-01-01\n` - ); - - const result = runGsdTools('todo start add-dark-mode.md', tmpDir); - assert.ok(result.success, `Command failed: ${result.error}`); - - const output = JSON.parse(result.output); - assert.strictEqual(output.started, true); - - // Verify moved - assert.ok( - !fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'pending', 'add-dark-mode.md')), - 'should be removed from pending' - ); - assert.ok( - fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'in-progress', 'add-dark-mode.md')), - 'should be in in-progress' - ); - - // Verify started timestamp added - const content = fs.readFileSync( - path.join(tmpDir, '.planning', 'todos', 'in-progress', 'add-dark-mode.md'), - 'utf-8' - ); - assert.ok(content.startsWith('started:'), 'should have started timestamp'); - }); - - test('fails for nonexistent todo', () => { - const result = runGsdTools('todo start nonexistent.md', tmpDir); - assert.ok(!result.success, 'should fail'); - assert.ok(result.error.includes('not found'), 'error mentions not found'); - }); -}); - // ───────────────────────────────────────────────────────────────────────────── // todo complete command // ───────────────────────────────────────────────────────────────────────────── @@ -1954,12 +1900,12 @@ describe('todo complete command', () => { cleanup(tmpDir); }); - test('completes todo from in-progress', () => { - const inProgressDir = path.join(tmpDir, '.planning', 'todos', 'in-progress'); - fs.mkdirSync(inProgressDir, { recursive: true }); + test('moves todo from pending to completed', () => { + const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending'); + fs.mkdirSync(pendingDir, { recursive: true }); fs.writeFileSync( - path.join(inProgressDir, 'add-dark-mode.md'), - `started: 2025-01-01\ntitle: Add dark mode\narea: ui\ncreated: 2025-01-01\n` + path.join(pendingDir, 'add-dark-mode.md'), + `title: Add dark mode\narea: ui\ncreated: 2025-01-01\n` ); const result = runGsdTools('todo complete add-dark-mode.md', tmpDir); @@ -1970,47 +1916,22 @@ describe('todo complete command', () => { // Verify moved assert.ok( - !fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'in-progress', 'add-dark-mode.md')), - 'should be removed from in-progress' + !fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'pending', 'add-dark-mode.md')), + 'should be removed from pending' ); assert.ok( - fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'done', 'add-dark-mode.md')), - 'should be in done' + fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'completed', 'add-dark-mode.md')), + 'should be in completed' ); // Verify completion timestamp added const content = fs.readFileSync( - path.join(tmpDir, '.planning', 'todos', 'done', 'add-dark-mode.md'), + path.join(tmpDir, '.planning', 'todos', 'completed', 'add-dark-mode.md'), 'utf-8' ); assert.ok(content.startsWith('completed:'), 'should have completed timestamp'); }); - test('completes todo from pending directly', () => { - const pendingDir = path.join(tmpDir, '.planning', 'todos', 'pending'); - fs.mkdirSync(pendingDir, { recursive: true }); - fs.writeFileSync( - path.join(pendingDir, 'fix-bug.md'), - `title: Fix bug\narea: api\ncreated: 2025-01-01\n` - ); - - const result = runGsdTools('todo complete fix-bug.md', tmpDir); - assert.ok(result.success, `Command failed: ${result.error}`); - - const output = JSON.parse(result.output); - assert.strictEqual(output.completed, true); - - // Verify moved to done (not completed) - assert.ok( - !fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'pending', 'fix-bug.md')), - 'should be removed from pending' - ); - assert.ok( - fs.existsSync(path.join(tmpDir, '.planning', 'todos', 'done', 'fix-bug.md')), - 'should be in done' - ); - }); - test('fails for nonexistent todo', () => { const result = runGsdTools('todo complete nonexistent.md', tmpDir); assert.ok(!result.success, 'should fail'); diff --git a/get-shit-done/references/planning-config.md b/get-shit-done/references/planning-config.md index 2a6b1371f..a7f09b26d 100644 --- a/get-shit-done/references/planning-config.md +++ b/get-shit-done/references/planning-config.md @@ -6,8 +6,7 @@ Configuration options for `.planning/` directory behavior. ```json "planning": { "commit_docs": true, - "search_gitignored": false, - "git_tag": true + "search_gitignored": false }, "git": { "branching_strategy": "none", @@ -20,7 +19,6 @@ Configuration options for `.planning/` directory behavior. |--------|---------|-------------| | `commit_docs` | `true` | Whether to commit planning artifacts to git | | `search_gitignored` | `false` | Add `--no-ignore` to broad rg searches | -| `git_tag` | `true` | Create git tags on milestone completion | | `git.branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` | | `git.phase_branch_template` | `"gsd/phase-{phase}-{slug}"` | Branch template for phase strategy | | `git.milestone_branch_template` | `"gsd/{milestone}-{slug}"` | Branch template for milestone strategy | diff --git a/get-shit-done/templates/config.json b/get-shit-done/templates/config.json index f76a3b3cc..744c2f8cd 100644 --- a/get-shit-done/templates/config.json +++ b/get-shit-done/templates/config.json @@ -8,8 +8,7 @@ }, "planning": { "commit_docs": true, - "search_gitignored": false, - "git_tag": true + "search_gitignored": false }, "parallelization": { "enabled": true, diff --git a/get-shit-done/workflows/add-todo.md b/get-shit-done/workflows/add-todo.md index ca69d8da0..39c3737fd 100644 --- a/get-shit-done/workflows/add-todo.md +++ b/get-shit-done/workflows/add-todo.md @@ -19,7 +19,7 @@ Extract from init JSON: `commit_docs`, `date`, `timestamp`, `todo_count`, `todos Ensure directories exist: ```bash -mkdir -p .planning/todos/pending .planning/todos/in-progress .planning/todos/done +mkdir -p .planning/todos/pending .planning/todos/done ``` Note existing areas from the todos array for consistency in infer_area step. diff --git a/get-shit-done/workflows/check-todos.md b/get-shit-done/workflows/check-todos.md index 370a7b5e4..e16d1bed5 100644 --- a/get-shit-done/workflows/check-todos.md +++ b/get-shit-done/workflows/check-todos.md @@ -105,7 +105,7 @@ Use AskUserQuestion: - header: "Action" - question: "This todo relates to Phase [N]: [name]. What would you like to do?" - options: - - "Work on it now" — move to in-progress, start working + - "Work on it now" — move to done, start working - "Add to phase plan" — include when planning Phase [N] - "Brainstorm approach" — think through before deciding - "Put it back" — return to list @@ -116,7 +116,7 @@ Use AskUserQuestion: - header: "Action" - question: "What would you like to do with this todo?" - options: - - "Work on it now" — move to in-progress, start working + - "Work on it now" — move to done, start working - "Create a phase" — /gsd:add-phase with this scope - "Brainstorm approach" — think through before deciding - "Put it back" — return to list @@ -125,7 +125,7 @@ Use AskUserQuestion: **Work on it now:** ```bash -mv ".planning/todos/pending/[filename]" ".planning/todos/in-progress/" +mv ".planning/todos/pending/[filename]" ".planning/todos/done/" ``` Update STATE.md todo count. Present problem/solution context. Begin work or ask how to proceed. @@ -150,11 +150,11 @@ Re-run `init todos` to get updated count, then update STATE.md "### Pending Todo -If todo was moved to in-progress/, commit the change: +If todo was moved to done/, commit the change: ```bash git rm --cached .planning/todos/pending/[filename] 2>/dev/null || true -node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs: start work on todo - [title]" --files .planning/todos/in-progress/[filename] .planning/STATE.md +node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs: start work on todo - [title]" --files .planning/todos/done/[filename] .planning/STATE.md ``` Tool respects `commit_docs` config and gitignore automatically. @@ -172,5 +172,5 @@ Confirm: "Committed: docs: start work on todo - [title]" - [ ] Appropriate actions offered - [ ] Selected action executed - [ ] STATE.md updated if todo count changed -- [ ] Changes committed to git (if todo moved to in-progress/) +- [ ] Changes committed to git (if todo moved to done/) diff --git a/get-shit-done/workflows/complete-milestone.md b/get-shit-done/workflows/complete-milestone.md index 6842938ca..b91e41cc0 100644 --- a/get-shit-done/workflows/complete-milestone.md +++ b/get-shit-done/workflows/complete-milestone.md @@ -531,11 +531,6 @@ fi - -Read `planning.git_tag` from `.planning/config.json` (default: `true` if not set). -If `git_tag` is `false` → skip this step entirely, proceed to `git_commit_milestone`. - - Create git tag: ```bash diff --git a/get-shit-done/workflows/discuss-phase.md b/get-shit-done/workflows/discuss-phase.md index 4f8840795..4f7fb0342 100644 --- a/get-shit-done/workflows/discuss-phase.md +++ b/get-shit-done/workflows/discuss-phase.md @@ -114,7 +114,7 @@ Phase number from argument (required). INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init phase-op "${PHASE}") ``` -Parse JSON for: `commit_docs`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`. +Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`. **If `phase_found` is false:** ``` @@ -124,22 +124,7 @@ Use /gsd:progress to see available phases. ``` Exit workflow. -**If `phase_found` is true:** Continue to handle_branching. - - - -Check `branching_strategy` from init: - -**"none":** Skip, continue on current branch. - -**"phase" or "milestone":** Use pre-computed `branch_name` from init: -```bash -git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" -``` - -This ensures all phase documentation commits (context, research, plans) go to the feature branch, not main. - -Continue to check_existing. +**If `phase_found` is true:** Continue to check_existing. diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 1344b2806..e6fefaa08 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -226,52 +226,94 @@ After all waves: ``` + +Verify phase achieved its GOAL, not just completed tasks. + +``` +Task( + prompt="Verify phase {phase_number} goal achievement. +Phase directory: {phase_dir} +Phase goal: {goal from ROADMAP.md} +Check must_haves against actual codebase. Create VERIFICATION.md.", + subagent_type="gsd-verifier", + model="{verifier_model}" +) +``` + +Read status: +```bash +grep "^status:" "$PHASE_DIR"/*-VERIFICATION.md | cut -d: -f2 | tr -d ' ' +``` + +| Status | Action | +|--------|--------| +| `passed` | → update_roadmap | +| `human_needed` | Present items for human testing, get approval or feedback | +| `gaps_found` | Present gap summary, offer `/gsd:plan-phase {phase} --gaps` | + +**If human_needed:** +``` +## ✓ Phase {X}: {Name} — Human Verification Required + +All automated checks passed. {N} items need human testing: + +{From VERIFICATION.md human_verification section} + +"approved" → continue | Report issues → gap closure +``` + +**If gaps_found:** +``` +## ⚠ Phase {X}: {Name} — Gaps Found + +**Score:** {N}/{M} must-haves verified +**Report:** {phase_dir}/{phase}-VERIFICATION.md + +### What's Missing +{Gap summaries from VERIFICATION.md} + +--- +## ▶ Next Up + +`/gsd:plan-phase {X} --gaps` + +`/clear` first → fresh context window + +Also: `cat {phase_dir}/{phase}-VERIFICATION.md` — full report +Also: `/gsd:verify-work {X}` — manual testing first +``` + +Gap closure cycle: `/gsd:plan-phase {X} --gaps` reads VERIFICATION.md → creates gap plans with `gap_closure: true` → user runs `/gsd:execute-phase {X} --gaps-only` → verifier re-runs. + + + +Mark phase complete in ROADMAP.md (date, status). + +```bash +node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs(phase-{X}): complete phase execution" --files .planning/ROADMAP.md .planning/STATE.md .planning/phases/{phase_dir}/*-VERIFICATION.md .planning/REQUIREMENTS.md +``` + + -**Route based on execution mode:** - -**If `--gaps-only` (gap closure execution):** - +**If more phases:** ``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - GSD ► GAP FIXES EXECUTED -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +## Next Up -Phase {X}: {Name} — gap closure plans executed. +**Phase {X+1}: {Name}** — {Goal} -─────────────────────────────────────────────────────────────── +`/gsd:plan-phase {X+1}` -## ▶ Next Up - -**Re-verify fixes** — confirm gaps are closed - -`/clear` then `/gsd:verify-work {X}` - -─────────────────────────────────────────────────────────────── +`/clear` first for fresh context ``` -**If normal execution (no flags):** - +**If milestone complete:** ``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - GSD ► EXECUTION COMPLETE -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +MILESTONE COMPLETE! -Phase {X}: {Name} — all plans executed. +All {N} phases executed. -─────────────────────────────────────────────────────────────── - -## ▶ Next Up - -**Verify what was built** — manual acceptance testing - -`/clear` then `/gsd:verify-work {X}` - -─────────────────────────────────────────────────────────────── - -**Also available:** -- `/gsd:plan-phase {X+1}` — skip verification, plan next phase -- `/gsd:complete-milestone` — if all phases done +`/gsd:complete-milestone` ``` diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index e298b66d0..7aefc43ab 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -18,25 +18,12 @@ Load all context in one call (include file contents to avoid redundant reads): INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init plan-phase "$PHASE" --include state,roadmap,requirements,context,research,verification,uat) ``` -Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `commit_docs`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`. +Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`. **File contents (from --include):** `state_content`, `roadmap_content`, `requirements_content`, `context_content`, `research_content`, `verification_content`, `uat_content`. These are null if files don't exist. **If `planning_exists` is false:** Error — run `/gsd:new-project` first. -## 1.5. Handle Branching - -Check `branching_strategy` from init: - -**"none":** Skip, continue on current branch. - -**"phase" or "milestone":** Use pre-computed `branch_name` from init: -```bash -git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME" -``` - -This ensures research and planning commits go to the feature branch, not main. If `/gsd:discuss-phase` already created the branch, the `git checkout` fallback switches to it. - ## 2. Parse and Normalize Arguments Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`). diff --git a/get-shit-done/workflows/settings.md b/get-shit-done/workflows/settings.md index 71f75e5a1..8966fecf0 100644 --- a/get-shit-done/workflows/settings.md +++ b/get-shit-done/workflows/settings.md @@ -30,7 +30,6 @@ Parse current values (default to `true` if not present): - `workflow.verifier` — spawn verifier during execute-phase - `model_profile` — which model each agent uses (default: `balanced`) - `git.branching_strategy` — branching approach (default: `"none"`) -- `planning.git_tag` — create git tags on milestone completion (default: `true`) @@ -84,15 +83,6 @@ AskUserQuestion([ { label: "Per Phase", description: "Create branch for each phase (gsd/phase-{N}-{name})" }, { label: "Per Milestone", description: "Create branch for entire milestone (gsd/{version}-{name})" } ] - }, - { - question: "Create git tags on milestone completion?", - header: "Git Tagging", - multiSelect: false, - options: [ - { label: "Yes (Recommended)", description: "Tag releases with version (e.g., v1.0) on milestone completion" }, - { label: "No", description: "Skip git tagging — use if your project doesn't use tags or uses a different convention" } - ] } ]) ``` @@ -110,10 +100,6 @@ Merge new settings into existing config.json: "plan_check": true/false, "verifier": true/false }, - "planning": { - ...existing_planning, - "git_tag": true/false - }, "git": { "branching_strategy": "none" | "phase" | "milestone" } @@ -138,7 +124,6 @@ Display: | Plan Checker | {On/Off} | | Execution Verifier | {On/Off} | | Git Branching | {None/Per Phase/Per Milestone} | -| Git Tagging | {On/Off} | These settings apply to future /gsd:plan-phase and /gsd:execute-phase runs. @@ -154,7 +139,7 @@ Quick commands: - [ ] Current config read -- [ ] User presented with 6 settings (profile + 3 workflow toggles + git branching + git tagging) +- [ ] User presented with 5 settings (profile + 3 workflow toggles + git branching) - [ ] Config updated with model_profile, workflow, and git sections - [ ] Changes confirmed to user diff --git a/get-shit-done/workflows/verify-work.md b/get-shit-done/workflows/verify-work.md index 34cfe6cb3..c3e11886b 100644 --- a/get-shit-done/workflows/verify-work.md +++ b/get-shit-done/workflows/verify-work.md @@ -311,31 +311,7 @@ Present summary: [List from Issues section] ``` -**If issues > 0:** - -``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - GSD ► ISSUES RECORDED -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -{N} issue(s) recorded in UAT.md with structured gaps. - -| # | Test | Severity | Issue | -|---|------|----------|-------| -[table of issues from Gaps section] - -─────────────────────────────────────────────────────────────── - -## ▶ Next Up - -**Plan fixes** in a fresh context (saves tokens): - -`/clear` then `/gsd:plan-phase {phase} --gaps` - -This will read the UAT gaps and create targeted fix plans. - -─────────────────────────────────────────────────────────────── -``` +**If issues > 0:** Proceed to `diagnose_issues` **If issues == 0:** ``` @@ -344,6 +320,203 @@ All tests passed. Ready to continue. - `/gsd:plan-phase {next}` — Plan next phase - `/gsd:execute-phase {next}` — Execute next phase ``` + + + +**Diagnose root causes before planning fixes:** + +``` +--- + +{N} issues found. Diagnosing root causes... + +Spawning parallel debug agents to investigate each issue. +``` + +- Load diagnose-issues workflow +- Follow @~/.claude/get-shit-done/workflows/diagnose-issues.md +- Spawn parallel debug agents for each issue +- Collect root causes +- Update UAT.md with root causes +- Proceed to `plan_gap_closure` + +Diagnosis runs automatically - no user prompt. Parallel agents investigate simultaneously, so overhead is minimal and fixes are more accurate. + + + +**Auto-plan fixes from diagnosed gaps:** + +Display: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► PLANNING FIXES +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +◆ Spawning planner for gap closure... +``` + +Spawn gsd-planner in --gaps mode: + +``` +Task( + prompt=""" + + +**Phase:** {phase_number} +**Mode:** gap_closure + +**UAT with diagnoses:** +@.planning/phases/{phase_dir}/{phase}-UAT.md + +**Project State:** +@.planning/STATE.md + +**Roadmap:** +@.planning/ROADMAP.md + + + + +Output consumed by /gsd:execute-phase +Plans must be executable prompts. + +""", + subagent_type="gsd-planner", + model="{planner_model}", + description="Plan gap fixes for Phase {phase}" +) +``` + +On return: +- **PLANNING COMPLETE:** Proceed to `verify_gap_plans` +- **PLANNING INCONCLUSIVE:** Report and offer manual intervention + + + +**Verify fix plans with checker:** + +Display: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► VERIFYING FIX PLANS +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +◆ Spawning plan checker... +``` + +Initialize: `iteration_count = 1` + +Spawn gsd-plan-checker: + +``` +Task( + prompt=""" + + +**Phase:** {phase_number} +**Phase Goal:** Close diagnosed gaps from UAT + +**Plans to verify:** +@.planning/phases/{phase_dir}/*-PLAN.md + + + + +Return one of: +- ## VERIFICATION PASSED — all checks pass +- ## ISSUES FOUND — structured issue list + +""", + subagent_type="gsd-plan-checker", + model="{checker_model}", + description="Verify Phase {phase} fix plans" +) +``` + +On return: +- **VERIFICATION PASSED:** Proceed to `present_ready` +- **ISSUES FOUND:** Proceed to `revision_loop` + + + +**Iterate planner ↔ checker until plans pass (max 3):** + +**If iteration_count < 3:** + +Display: `Sending back to planner for revision... (iteration {N}/3)` + +Spawn gsd-planner with revision context: + +``` +Task( + prompt=""" + + +**Phase:** {phase_number} +**Mode:** revision + +**Existing plans:** +@.planning/phases/{phase_dir}/*-PLAN.md + +**Checker issues:** +{structured_issues_from_checker} + + + + +Read existing PLAN.md files. Make targeted updates to address checker issues. +Do NOT replan from scratch unless issues are fundamental. + +""", + subagent_type="gsd-planner", + model="{planner_model}", + description="Revise Phase {phase} plans" +) +``` + +After planner returns → spawn checker again (verify_gap_plans logic) +Increment iteration_count + +**If iteration_count >= 3:** + +Display: `Max iterations reached. {N} issues remain.` + +Offer options: +1. Force proceed (execute despite issues) +2. Provide guidance (user gives direction, retry) +3. Abandon (exit, user runs /gsd:plan-phase manually) + +Wait for user response. + + + +**Present completion and next steps:** + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► FIXES READY ✓ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +**Phase {X}: {Name}** — {N} gap(s) diagnosed, {M} fix plan(s) created + +| Gap | Root Cause | Fix Plan | +|-----|------------|----------| +| {truth 1} | {root_cause} | {phase}-04 | +| {truth 2} | {root_cause} | {phase}-04 | + +Plans verified and ready for execution. + +─────────────────────────────────────────────────────────────── + +## ▶ Next Up + +**Execute fixes** — run fix plans + +`/clear` then `/gsd:execute-phase {phase} --gaps-only` + +─────────────────────────────────────────────────────────────── +``` + @@ -389,7 +562,9 @@ Default to **major** if unclear. User can correct if needed. - [ ] Severity inferred from description (never asked) - [ ] Batched writes: on issue, every 5 passes, or completion - [ ] Committed on completion -- [ ] If issues: gaps recorded in structured YAML in UAT.md -- [ ] If issues: user routed to `/gsd:plan-phase {N} --gaps` in fresh context -- [ ] If no issues: user routed to next phase +- [ ] If issues: parallel debug agents diagnose root causes +- [ ] If issues: gsd-planner creates fix plans (gap_closure mode) +- [ ] If issues: gsd-plan-checker verifies fix plans +- [ ] If issues: revision loop until plans pass (max 3 iterations) +- [ ] Ready for `/gsd:execute-phase --gaps-only` when complete diff --git a/gsd-verifier Missing Write Tool — Root Cause Analysis.md b/gsd-verifier Missing Write Tool — Root Cause Analysis.md deleted file mode 100644 index 45e42e31c..000000000 --- a/gsd-verifier Missing Write Tool — Root Cause Analysis.md +++ /dev/null @@ -1,71 +0,0 @@ -Research: gsd-verifier Missing Write Tool — Root Cause Analysis - - Finding: Day-One Bug, NOT a v1.17.0 → v1.18.0 Regression - -The Write tool was never present in gsd-verifier.md. The diff between v1.17.0 and v1.18.0 shows zero changes to this file. Both versions have identical frontmatter: - -tools: Read, Bash, Grep, Glob # Write missing since initial creation - -Timeline -Date: Jan 15, 2026 -Commit: f3f6707 -What Happened: gsd-verifier.md created — tools: Read, Bash, Grep, Glob, model: sonnet. Write was never included. -──────────────────────────────────────── -Date: Jan 15, 2026 -Commit: 8e6ad96→a7986bc→f9edfcf -What Happened: Formatting, re-verification loop, color changes. Tools line unchanged. -──────────────────────────────────────── -Date: Jan 30, 2026 -Commit: 5660b6f -What Happened: Gemini CLI compatibility fix. Tools line unchanged. -──────────────────────────────────────── -Date: Feb 7-8, 2026 -Commit: d44c7dc→1b317de→6a2d1f1 -What Happened: gsd-tools integration refactor. model: sonnet removed from frontmatter, but tools line still unchanged. -The bug has existed through every release since the verifier was introduced. - -Contradiction in the Agent Definition - -The frontmatter says tools: Read, Bash, Grep, Glob (no Write), but: -- The description says: "Creates VERIFICATION.md report" -- The output section instructs: "Create .planning/phases/{phase_dir}/{phase}-VERIFICATION.md" - -Without Write, the agent falls back to Bash with heredoc (cat > file << 'EOF'), which works functionally but causes the permission corruption we saw. - -Comparison with Other Agents (v1.18.0) -┌─────────────────────────┬────────────────────────────────────────────────┬──────────────────────────────┬──────────────┐ -│ Agent │ Tools │ Writes Files? │ Has Write? │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-executor │ Read, Write, Edit, Bash, Grep, Glob │ Yes (code, summaries) │ Yes │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-planner │ Read, Write, Bash, Glob, Grep, WebFetch │ Yes (PLAN.md) │ Yes │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-roadmapper │ Read, Write, Bash, Glob, Grep │ Yes (ROADMAP.md) │ Yes │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-debugger │ Read, Write, Edit, Bash, Grep, Glob, WebSearch │ Yes (debug reports) │ Yes │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-codebase-mapper │ Read, Bash, Grep, Glob, Write │ Yes (analysis docs) │ Yes │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-verifier │ Read, Bash, Grep, Glob │ Yes (VERIFICATION.md) │ NO │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-plan-checker │ Read, Bash, Glob, Grep │ No (returns to orchestrator) │ No (correct) │ -├─────────────────────────┼────────────────────────────────────────────────┼──────────────────────────────┼──────────────┤ -│ gsd-integration-checker │ Read, Bash, Grep, Glob │ Borderline (returns report) │ No │ -└─────────────────────────┴────────────────────────────────────────────────┴──────────────────────────────┴──────────────┘ -Every agent that creates files has Write — except gsd-verifier. This is clearly an oversight. - -Recommended Action - -File an issue at https://github.com/gsd-build/get-shit-done with: - -Title: gsd-verifier agent missing Write tool — falls back to Bash heredoc, corrupts settings.local.json - -Body: -- gsd-verifier.md frontmatter has tools: Read, Bash, Grep, Glob but its instructions tell it to create VERIFICATION.md files -- Without Write, it uses Bash(cat > path << 'EOF' ... EOF) which, when user approves, saves the entire 200+ line markdown report as a permission entry in settings.local.json -- This corrupts settings.local.json and causes a "Settings Error" on every Claude Code startup -- Every other file-creating agent (executor, planner, roadmapper, debugger, codebase-mapper) includes Write -- Fix: change line 4 to tools: Read, Write, Bash, Grep, Glob -- Bug exists since initial creation (commit f3f6707, Jan 15, 2026), not a regression - -Our local patch (~/.claude/agents/gsd-verifier.md line 4) adds Write but will be overwritten by /gsd:update. diff --git a/hooks/gsd-statusline.js b/hooks/gsd-statusline.js index d0d91d349..fa8889f97 100755 --- a/hooks/gsd-statusline.js +++ b/hooks/gsd-statusline.js @@ -1,6 +1,6 @@ #!/usr/bin/env node // Claude Code Statusline - GSD Edition -// Shows: model | current task | git branch | directory | context usage +// Shows: model | current task | directory | context usage const fs = require('fs'); const path = require('path'); @@ -78,54 +78,12 @@ process.stdin.on('end', () => { } catch (e) {} } - // Git branch (traverse up to find .git, supports worktrees) - let branch = ''; - let currentPath = dir; - while (currentPath) { - const gitPath = path.join(currentPath, '.git'); - if (fs.existsSync(gitPath)) { - try { - let gitDir = gitPath; - const stats = fs.statSync(gitPath); - - // If .git is a file (worktree), read the gitdir path - if (stats.isFile()) { - const gitFileContent = fs.readFileSync(gitPath, 'utf8'); - const gitDirMatch = gitFileContent.match(/gitdir:\s*(.+)/); - if (gitDirMatch) { - gitDir = gitDirMatch[1].trim(); - // Handle relative paths in worktree .git files - if (!path.isAbsolute(gitDir)) { - gitDir = path.resolve(currentPath, gitDir); - } - } - } - - // Read HEAD from the actual git directory - const headFile = path.join(gitDir, 'HEAD'); - if (fs.existsSync(headFile)) { - const headContent = fs.readFileSync(headFile, 'utf8'); - const match = headContent.match(/ref: refs\/heads\/(.+)/); - if (match) { - branch = ` \x1b[35m${match[1].trim()}\x1b[0m │`; - } - } - } catch (e) { - // Silently fail on git errors - don't break statusline - } - break; - } - const parentPath = path.dirname(currentPath); - if (parentPath === currentPath) break; - currentPath = parentPath; - } - // Output const dirname = path.basename(dir); if (task) { - process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[1m${task}\x1b[0m │${branch} \x1b[2m${dirname}\x1b[0m${ctx}`); + process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[1m${task}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`); } else { - process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │${branch} \x1b[2m${dirname}\x1b[0m${ctx}`); + process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`); } } catch (e) { // Silent fail - don't break statusline on parse errors diff --git a/opencode/README.md b/opencode/README.md deleted file mode 100644 index 275bcf867..000000000 --- a/opencode/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# OpenCode Installer Package - -This folder contains the OpenCode-specific installer package for GSD. - -- Package location: `opencode/get-shit-done-opencode` -- Install command: `npx get-shit-done-opencode` -- Default install target: `~/.config/opencode` - -Use `--local` to install into `./.opencode` for a single project. diff --git a/opencode/get-shit-done-opencode/bin/install.js b/opencode/get-shit-done-opencode/bin/install.js deleted file mode 100644 index 2de5623a1..000000000 --- a/opencode/get-shit-done-opencode/bin/install.js +++ /dev/null @@ -1,469 +0,0 @@ -#!/usr/bin/env node - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const readline = require("readline"); - -// Colors -const cyan = "\x1b[36m"; -const green = "\x1b[32m"; -const yellow = "\x1b[33m"; -const dim = "\x1b[2m"; -const reset = "\x1b[0m"; - -// Get version from package.json -const pkg = require("../package.json"); - -const banner = ` -${cyan} ██████╗ ███████╗██████╗ - ██╔════╝ ██╔════╝██╔══██╗ - ██║ ███╗███████╗██║ ██║ - ██║ ██║╚════██║██║ ██║ - ╚██████╔╝███████║██████╔╝ - ╚═════╝ ╚══════╝╚═════╝${reset} - - Get Shit Done ${dim}v${pkg.version}${reset} - A meta-prompting, context engineering and spec-driven - development system for OpenCode by TÂCHES. -`; - -// Parse args -const args = process.argv.slice(2); -const hasGlobal = args.includes("--global") || args.includes("-g"); -const hasLocal = args.includes("--local") || args.includes("-l"); - -// Parse --config-dir argument -function parseConfigDirArg() { - const configDirIndex = args.findIndex((arg) => arg === "--config-dir" || arg === "-c"); - if (configDirIndex !== -1) { - const nextArg = args[configDirIndex + 1]; - if (!nextArg || nextArg.startsWith("-")) { - console.error(` ${yellow}--config-dir requires a path argument${reset}`); - process.exit(1); - } - return nextArg; - } - const configDirArg = args.find((arg) => arg.startsWith("--config-dir=") || arg.startsWith("-c=")); - if (configDirArg) { - return configDirArg.split("=")[1]; - } - return null; -} - -const explicitConfigDir = parseConfigDirArg(); -const hasHelp = args.includes("--help") || args.includes("-h"); - -console.log(banner); - -// Show help if requested -if (hasHelp) { - console.log(` ${yellow}Usage:${reset} npx get-shit-done-opencode [options] - - ${yellow}Options:${reset} - ${cyan}-g, --global${reset} Install globally (to OpenCode config directory) - ${cyan}-l, --local${reset} Install locally (to ./.opencode in current directory) - ${cyan}-c, --config-dir ${reset} Specify custom OpenCode config directory - ${cyan}-h, --help${reset} Show this help message - - ${yellow}Examples:${reset} - ${dim}# Install to default ~/.config/opencode directory${reset} - npx get-shit-done-opencode --global - - ${dim}# Install to custom config directory${reset} - npx get-shit-done-opencode --global --config-dir ~/.config/opencode-alt - - ${dim}# Install to current project only${reset} - npx get-shit-done-opencode --local -`); - process.exit(0); -} - -/** - * Expand ~ to home directory - */ -function expandTilde(filePath) { - if (filePath && filePath.startsWith("~/")) { - return path.join(os.homedir(), filePath.slice(2)); - } - return filePath; -} - -function resolveGsdRoot() { - try { - const gsdPkg = require.resolve("get-shit-done-cc/package.json"); - return path.dirname(gsdPkg); - } catch (e) { - const localRoot = path.join(__dirname, "..", "..", ".."); - if (fs.existsSync(path.join(localRoot, "commands"))) return localRoot; - } - console.error(` ${yellow}✗${reset} Failed to locate get-shit-done-cc assets`); - process.exit(1); -} - -function transformHook(content) { - return content - .replace(/get-shit-done-cc/g, "get-shit-done-opencode") - .replace(/['"]\\.claude['"]/g, "'.config/opencode'"); -} - -function colorToHex(value) { - const normalized = value.trim().toLowerCase(); - const map = { - green: "#22c55e", - blue: "#3b82f6", - purple: "#8b5cf6", - cyan: "#06b6d4", - yellow: "#f59e0b", - orange: "#f97316", - }; - if (normalized.startsWith("#")) return normalized; - return map[normalized] || normalized; -} - -function toolsLineToBlock(line) { - const raw = line.split(":").slice(1).join(":").trim(); - if (!raw) return [line]; - const tools = raw - .split(",") - .map((item) => item.trim()) - .filter(Boolean) - .map((item) => item.toLowerCase()); - const entries = tools.map((tool) => { - const key = /^[a-z0-9_]+$/.test(tool) ? tool : `"${tool}"`; - return ` ${key}: true`; - }); - return ["tools:", ...entries]; -} - -function transformMarkdown(content, pathPrefix) { - return content - .replace(/~\/\.claude\//g, pathPrefix) - .replace(/\bgsd:([a-z0-9-]+)/g, "gsd/$1") - .replace(/npx get-shit-done-cc/g, "npx get-shit-done-opencode"); -} - -function transformAgentMarkdown(content, pathPrefix) { - const updated = transformMarkdown(content, pathPrefix); - const match = updated.match(/^---\r?\n([\s\S]*?)\r?\n---/); - if (!match) return updated; - - const lines = match[1].split(/\r?\n/); - const out = []; - - for (const line of lines) { - if (line.startsWith("tools:")) { - out.push(...toolsLineToBlock(line)); - continue; - } - if (line.startsWith("color:")) { - const value = line.split(":").slice(1).join(":").trim(); - const colorValue = colorToHex(value); - out.push(`color: "${colorValue}"`); - continue; - } - out.push(line); - } - - return updated.replace(match[0], `---\n${out.join("\n")}\n---`); -} - -/** - * Recursively copy directory, transforming markdown files - */ -function copyWithTransform(srcDir, destDir, pathPrefix) { - if (fs.existsSync(destDir)) { - fs.rmSync(destDir, { recursive: true }); - } - fs.mkdirSync(destDir, { recursive: true }); - - const entries = fs.readdirSync(srcDir, { withFileTypes: true }); - - for (const entry of entries) { - const srcPath = path.join(srcDir, entry.name); - const destPath = path.join(destDir, entry.name); - - if (entry.isDirectory()) { - copyWithTransform(srcPath, destPath, pathPrefix); - } else if (entry.name.endsWith(".md")) { - let content = fs.readFileSync(srcPath, "utf8"); - content = transformMarkdown(content, pathPrefix); - fs.writeFileSync(destPath, content); - } else { - fs.copyFileSync(srcPath, destPath); - } - } -} - -function copyCommands(srcDir, destRoot, pathPrefix) { - const destDir = path.join(destRoot, "commands", "gsd"); - if (fs.existsSync(destDir)) { - fs.rmSync(destDir, { recursive: true }); - } - fs.mkdirSync(destDir, { recursive: true }); - const entries = fs.readdirSync(srcDir, { withFileTypes: true }); - for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith(".md")) continue; - const srcPath = path.join(srcDir, entry.name); - const destPath = path.join(destDir, entry.name); - let content = fs.readFileSync(srcPath, "utf8"); - content = transformMarkdown(content, pathPrefix); - fs.writeFileSync(destPath, content); - } -} - -/** - * Verify a directory exists and contains files - */ -function verifyInstalled(dirPath, description) { - if (!fs.existsSync(dirPath)) { - console.error(` ${yellow}✗${reset} Failed to install ${description}: directory not created`); - return false; - } - try { - const entries = fs.readdirSync(dirPath); - if (entries.length === 0) { - console.error(` ${yellow}✗${reset} Failed to install ${description}: directory is empty`); - return false; - } - } catch (e) { - console.error(` ${yellow}✗${reset} Failed to install ${description}: ${e.message}`); - return false; - } - return true; -} - -/** - * Verify a file exists - */ -function verifyFileInstalled(filePath, description) { - if (!fs.existsSync(filePath)) { - console.error(` ${yellow}✗${reset} Failed to install ${description}: file not created`); - return false; - } - return true; -} - -function readConfig(configPath) { - if (!fs.existsSync(configPath)) return {}; - try { - return JSON.parse(fs.readFileSync(configPath, "utf8")); - } catch (e) { - return {}; - } -} - -function writeConfig(configPath, config) { - fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n"); -} - -/** - * Install to the specified directory - */ -function install(isGlobal) { - const src = resolveGsdRoot(); - const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.OPENCODE_CONFIG_DIR); - const defaultGlobalDir = configDir || path.join(os.homedir(), ".config", "opencode"); - const opencodeDir = isGlobal ? defaultGlobalDir : path.join(process.cwd(), ".opencode"); - - const locationLabel = isGlobal - ? opencodeDir.replace(os.homedir(), "~") - : opencodeDir.replace(process.cwd(), "."); - - const pathPrefix = isGlobal ? (configDir ? `${opencodeDir}/` : "~/.config/opencode/") : "./.opencode/"; - - console.log(` Installing to ${cyan}${locationLabel}${reset}\n`); - - const failures = []; - - // Create commands directory and copy commands - const gsdCommandsSrc = path.join(src, "commands", "gsd"); - if (fs.existsSync(gsdCommandsSrc)) { - copyCommands(gsdCommandsSrc, opencodeDir, pathPrefix); - const gsdCommandsDest = path.join(opencodeDir, "commands", "gsd"); - if (verifyInstalled(gsdCommandsDest, "commands/gsd")) { - console.log(` ${green}✓${reset} Installed commands/gsd`); - } else { - failures.push("commands/gsd"); - } - } else { - failures.push("commands/gsd"); - } - - // Copy get-shit-done assets with path replacement - const gsdSrc = path.join(src, "get-shit-done"); - const gsdDest = path.join(opencodeDir, "get-shit-done"); - copyWithTransform(gsdSrc, gsdDest, pathPrefix); - if (verifyInstalled(gsdDest, "get-shit-done")) { - console.log(` ${green}✓${reset} Installed get-shit-done`); - } else { - failures.push("get-shit-done"); - } - - // Copy agents to .opencode/agents (replace only gsd-*.md) - const agentsSrc = path.join(src, "agents"); - if (fs.existsSync(agentsSrc)) { - const agentsDest = path.join(opencodeDir, "agents"); - fs.mkdirSync(agentsDest, { recursive: true }); - - if (fs.existsSync(agentsDest)) { - for (const file of fs.readdirSync(agentsDest)) { - if (file.startsWith("gsd-") && file.endsWith(".md")) { - fs.unlinkSync(path.join(agentsDest, file)); - } - } - } - - const agentEntries = fs.readdirSync(agentsSrc, { withFileTypes: true }); - for (const entry of agentEntries) { - if (entry.isFile() && entry.name.endsWith(".md")) { - let content = fs.readFileSync(path.join(agentsSrc, entry.name), "utf8"); - content = transformAgentMarkdown(content, pathPrefix); - fs.writeFileSync(path.join(agentsDest, entry.name), content); - } - } - if (verifyInstalled(agentsDest, "agents")) { - console.log(` ${green}✓${reset} Installed agents`); - } else { - failures.push("agents"); - } - } - - // Copy CHANGELOG.md - const changelogSrc = path.join(src, "CHANGELOG.md"); - const changelogDest = path.join(opencodeDir, "get-shit-done", "CHANGELOG.md"); - if (fs.existsSync(changelogSrc)) { - fs.copyFileSync(changelogSrc, changelogDest); - if (verifyFileInstalled(changelogDest, "CHANGELOG.md")) { - console.log(` ${green}✓${reset} Installed CHANGELOG.md`); - } else { - failures.push("CHANGELOG.md"); - } - } - - // Write VERSION file - const versionDest = path.join(opencodeDir, "get-shit-done", "VERSION"); - fs.writeFileSync(versionDest, pkg.version); - if (verifyFileInstalled(versionDest, "VERSION")) { - console.log(` ${green}✓${reset} Wrote VERSION (${pkg.version})`); - } else { - failures.push("VERSION"); - } - - // Copy update hook and configure opencode.json - const hookSrc = path.join(src, "hooks", "gsd-check-update.js"); - const hookDestDir = path.join(opencodeDir, "get-shit-done", "hooks"); - const hookDest = path.join(hookDestDir, "gsd-check-update.js"); - fs.mkdirSync(hookDestDir, { recursive: true }); - if (fs.existsSync(hookSrc)) { - let content = fs.readFileSync(hookSrc, "utf8"); - content = transformHook(content); - fs.writeFileSync(hookDest, content); - if (verifyFileInstalled(hookDest, "get-shit-done/hooks/gsd-check-update.js")) { - console.log(` ${green}✓${reset} Installed update hook`); - } else { - failures.push("get-shit-done/hooks/gsd-check-update.js"); - } - } else { - failures.push("get-shit-done/hooks/gsd-check-update.js"); - } - - const configPath = path.join(opencodeDir, "opencode.json"); - const config = readConfig(configPath); - if (!config.experimental) config.experimental = {}; - if (!config.experimental.hook) config.experimental.hook = {}; - if (!config.experimental.hook.session_completed) { - config.experimental.hook.session_completed = []; - } - - const hookCommand = ["node", hookDest]; - const hasHook = config.experimental.hook.session_completed.some( - (entry) => - Array.isArray(entry.command) && - entry.command.length === hookCommand.length && - entry.command.every((value, idx) => value === hookCommand[idx]), - ); - if (!hasHook) { - config.experimental.hook.session_completed.push({ command: hookCommand }); - writeConfig(configPath, config); - console.log(` ${green}✓${reset} Configured update hook in opencode.json`); - } - - if (failures.length > 0) { - console.error(`\n ${yellow}Installation incomplete!${reset} Failed: ${failures.join(", ")}`); - console.error( - ` Try running directly: node ~/.npm/_npx/*/node_modules/get-shit-done-opencode/bin/install.js --global\n`, - ); - process.exit(1); - } -} - -/** - * Prompt for install location - */ -function promptLocation() { - if (!process.stdin.isTTY) { - console.log(` ${yellow}Non-interactive terminal detected, defaulting to global install${reset}\n`); - install(true); - finishInstall(); - return; - } - - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - let answered = false; - - rl.on("close", () => { - if (!answered) { - answered = true; - console.log(`\n ${yellow}Input stream closed, defaulting to global install${reset}\n`); - install(true); - finishInstall(); - } - }); - - const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.OPENCODE_CONFIG_DIR); - const globalPath = configDir || path.join(os.homedir(), ".config", "opencode"); - const globalLabel = globalPath.replace(os.homedir(), "~"); - - console.log(` ${yellow}Where would you like to install?${reset} - - ${cyan}1${reset}) Global ${dim}(${globalLabel})${reset} - available in all projects - ${cyan}2${reset}) Local ${dim}(./.opencode)${reset} - this project only -`); - - rl.question(` Choice ${dim}[1]${reset}: `, (answer) => { - answered = true; - rl.close(); - const choice = answer.trim() || "1"; - const isGlobal = choice !== "2"; - install(isGlobal); - finishInstall(); - }); -} - -function finishInstall() { - console.log(` - ${green}Done!${reset} Open OpenCode and run ${cyan}gsd/help${reset}. -`); -} - -// Main -if (hasGlobal && hasLocal) { - console.error(` ${yellow}Cannot specify both --global and --local${reset}`); - process.exit(1); -} else if (explicitConfigDir && hasLocal) { - console.error(` ${yellow}Cannot use --config-dir with --local${reset}`); - process.exit(1); -} else if (hasGlobal) { - install(true); - finishInstall(); -} else if (hasLocal) { - install(false); - finishInstall(); -} else { - promptLocation(); -} diff --git a/opencode/get-shit-done-opencode/package.json b/opencode/get-shit-done-opencode/package.json deleted file mode 100644 index 479669b27..000000000 --- a/opencode/get-shit-done-opencode/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "get-shit-done-opencode", - "version": "1.6.4", - "description": "Get Shit Done installer for OpenCode.", - "bin": { - "get-shit-done-opencode": "bin/install.js" - }, - "files": [ - "bin" - ], - "keywords": [ - "opencode", - "ai", - "meta-prompting", - "context-engineering", - "spec-driven-development" - ], - "author": "TÂCHES", - "license": "MIT", - "dependencies": { - "get-shit-done-cc": "^1.6.4" - }, - "engines": { - "node": ">=16.7.0" - } -} diff --git a/package.json b/package.json index 5a6103338..e02b706c3 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,7 @@ "context-engineering", "spec-driven-development", "gemini", - "gemini-cli", - "mistral-vibe", - "vibe-cli" + "gemini-cli" ], "author": "TÂCHES", "license": "MIT",