feat: extract repetitive bash patterns into gsd-tools commands (#472)
* feat(gsd-tools): add history-digest, atomic state operations, and summary variants Adds new performance-focused commands to gsd-tools and introduces specialized summary templates to reduce context tax: - history-digest: Compiles phase summaries into structured JSON for JIT loading - state get/patch: Enables atomic STATE.md operations instead of full rewrites - template select: Automatically chooses optimal summary template based on plan complexity - Adds minimal, standard, and complex summary templates Part of the "Hydra" architecture for GSD context optimization. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: add project config * docs: define v1 requirements * docs: create roadmap (6 phases) * feat(history-digest): fix nested YAML parsing and add tests - Fix extractFrontmatter() to handle nested YAML structures like dependency-graph.provides, tech-stack.added using stack-based parsing - Add test infrastructure with Node test runner (npm test) - Update gsd-planner to use digest fields directly instead of reading full SUMMARY.md files - Add 6 schema validation tests covering nested fields, merging, malformed files, and backward compatibility Closes: HIST-01, HIST-02, HIST-03, HIST-04 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(planner): use digest for selection, full SUMMARY for understanding The previous commit went too far by eliminating full SUMMARY reads. The digest is an index for smart selection, not a replacement for understanding what actually happened. Two-step approach: 1. Digest to score/select relevant phases (2-4 typically) 2. Full SUMMARY read for selected phases (implementation details) 3. Digest-level context retained for unselected phases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(gsd-tools): add phases, roadmap, and phase commands TDD implementation of three new commands to replace repetitive bash: phases list [--type plans|summaries] [--phase N] - Lists phase directories sorted numerically (handles decimals) - Filter by file type or specific phase - Replaces: ls -d .planning/phases/*/ | sort -V (22 occurrences) roadmap get-phase <N> - Extracts phase section from ROADMAP.md - Returns name, goal, full section content - Replaces: grep -A20 "Phase X:" ROADMAP.md (19 occurrences) phase next-decimal <N> - Calculates next decimal phase (06 → 06.1, 06.2 → 06.3) - Handles gaps, normalizes input - Replaces: complex bash math in insert-phase (3 occurrences) 16 new tests, all passing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: migrate agents/workflows to use gsd-tools commands Replace inline bash patterns with centralized gsd-tools commands: phases list: - audit-milestone.md: ls -d .planning/phases/*/ | sort -V - plan-milestone-gaps.md: ls -d ... | sort -V | tail -1 roadmap get-phase: - plan-phase.md: grep -A5 "Phase X:" ROADMAP.md (2 occurrences) - research-phase.md: grep patterns (2 occurrences) - verify-phase.md: grep -A5 pattern - gsd-verifier.md: grep -A5 pattern - gsd-plan-checker.md: grep -A10 pattern - commands/gsd/research-phase.md: grep patterns (2 occurrences) phase next-decimal: - insert-phase.md: complex bash decimal calculation - decimal-phase-calculation.md: reference doc rewritten phase-argument-parsing.md: updated to reference gsd-tools Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,3 +14,7 @@ hooks/dist/
|
||||
# Animation assets
|
||||
animation/
|
||||
*.gif
|
||||
|
||||
# Internal planning documents
|
||||
reports/
|
||||
RAILROAD_ARCHITECTURE.md
|
||||
|
||||
63
.planning/PROJECT.md
Normal file
63
.planning/PROJECT.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# GSD Context Optimization
|
||||
|
||||
## What This Is
|
||||
|
||||
Optimizing GSD's context loading so agents start lean and stay in their peak quality zone. Reduces prompt bloat through lazy loading, tiered prompts, and compiled artifacts while preserving instructional density.
|
||||
|
||||
## Core Value
|
||||
|
||||
Agents execute at peak quality by starting at 8-12% context instead of 15-25%.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Validated
|
||||
|
||||
(None yet — ship to validate)
|
||||
|
||||
### Active
|
||||
|
||||
- [ ] History loads as structured digest instead of full SUMMARY.md files
|
||||
- [ ] Summary templates match plan complexity (minimal/standard/complex)
|
||||
- [ ] State operations are atomic patches, not full file read/write cycles
|
||||
- [ ] Executor loads references on-demand based on task type
|
||||
- [ ] Planner loads extensions based on planning mode
|
||||
- [ ] Plans can be pre-compiled before execution
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Enhanced semantic queries (Phase 4) — defer unless Phases 1-3 prove insufficient
|
||||
- Compressing instructional content — core methodology must remain intact
|
||||
- Complex cache invalidation — simple mtime checks are sufficient
|
||||
|
||||
## Context
|
||||
|
||||
**Problem:** Claude's quality degrades predictably with context load:
|
||||
- 0-30% context: Peak quality
|
||||
- 30-50%: Good, occasional shortcuts
|
||||
- 50-70%: Degrading, efficiency mode
|
||||
- 70%+: Poor, rushed, misses requirements
|
||||
|
||||
Complex phases currently start agents at 15-25% context from prompt loading alone—before any codebase reading.
|
||||
|
||||
**Prior work:** v1.12.x shipped compound init commands (4,245 net line reduction), proving the direction works.
|
||||
|
||||
**Target state:** Agents start at 8-12% context, preserving peak quality zone for execution.
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Instructional density**: Teaching content cannot be compressed—it's GSD's value
|
||||
- **Adaptive intelligence**: Agents must still handle mid-execution surprises
|
||||
- **Maintainability**: Clear separation between core and extensions
|
||||
- **Debuggability**: Easy to trace which modules loaded for any execution
|
||||
- **TDD**: Use test-driven development for gsd-tools.js changes — write tests first, then implementation
|
||||
|
||||
## Key Decisions
|
||||
|
||||
| Decision | Rationale | Outcome |
|
||||
|----------|-----------|---------|
|
||||
| Lazy loading over railroad architecture | Preserves adaptive intelligence while reducing context | — Pending |
|
||||
| Build on compound-init foundation | v1.12.x proved the pattern works | — Pending |
|
||||
| Three-phase implementation | Incremental, reversible changes | — Pending |
|
||||
|
||||
---
|
||||
*Last updated: 2025-02-07 after initialization*
|
||||
114
.planning/REQUIREMENTS.md
Normal file
114
.planning/REQUIREMENTS.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Requirements: GSD Context Optimization
|
||||
|
||||
**Defined:** 2025-02-07
|
||||
**Core Value:** Agents execute at peak quality by starting at 8-12% context instead of 15-25%
|
||||
|
||||
## v1 Requirements
|
||||
|
||||
### History Digest
|
||||
|
||||
- [ ] **HIST-01**: `gsd-tools history-digest` generates structured JSON from SUMMARY.md frontmatter
|
||||
- [ ] **HIST-02**: Digest includes phases, provides, patterns, affects, decisions, and tech_stack
|
||||
- [ ] **HIST-03**: Planner uses digest instead of reading full SUMMARY.md files
|
||||
- [ ] **HIST-04**: Tests verify digest output matches expected schema
|
||||
|
||||
### Summary Variants
|
||||
|
||||
- [ ] **SUMM-01**: Three summary templates exist: minimal (~30 lines), standard (~60 lines), complex (~100 lines)
|
||||
- [ ] **SUMM-02**: `gsd-tools select-template` returns appropriate template based on plan complexity
|
||||
- [ ] **SUMM-03**: Selection logic considers task count, decision tasks, and file count
|
||||
- [ ] **SUMM-04**: Executor uses selected template for summary creation
|
||||
- [ ] **SUMM-05**: Tests verify template selection logic
|
||||
|
||||
### Atomic State Operations
|
||||
|
||||
- [ ] **STATE-01**: `gsd-tools state get <field>` returns specific STATE.md field value
|
||||
- [ ] **STATE-02**: `gsd-tools state patch --field value` updates specific fields atomically
|
||||
- [ ] **STATE-03**: Agents use atomic ops instead of full STATE.md read/write
|
||||
- [ ] **STATE-04**: Tests verify get/patch operations
|
||||
|
||||
### Lazy-Load Executor
|
||||
|
||||
- [ ] **EXEC-01**: `gsd-executor-core.md` contains base executor (~150 lines)
|
||||
- [ ] **EXEC-02**: `references/executor/` contains modular references (deviation-rules, tdd, checkpoint, continuation, summary-creation)
|
||||
- [ ] **EXEC-03**: Executor loads TDD reference only when task.tdd="true"
|
||||
- [ ] **EXEC-04**: Executor loads checkpoint reference only when task.type="checkpoint:*"
|
||||
- [ ] **EXEC-05**: Executor loads continuation reference only when <completed_tasks> present
|
||||
- [ ] **EXEC-06**: Executor loads summary-creation reference at plan completion
|
||||
|
||||
### Tiered Planner
|
||||
|
||||
- [ ] **PLAN-01**: `gsd-planner-core.md` contains base planner (~300 lines)
|
||||
- [ ] **PLAN-02**: `gsd-planner-ext/` contains extensions (gap-closure, revision, tdd, checkpoints)
|
||||
- [ ] **PLAN-03**: Orchestrator builds prompt dynamically based on flags and context
|
||||
- [ ] **PLAN-04**: Gap-closure extension loads only with --gaps flag
|
||||
- [ ] **PLAN-05**: Revision extension loads only when checker issues exist
|
||||
- [ ] **PLAN-06**: TDD extension loads only when TDD candidates detected
|
||||
|
||||
### Compiled Plans
|
||||
|
||||
- [ ] **COMP-01**: `gsd-tools compile-plan` inlines all @ references
|
||||
- [ ] **COMP-02**: Compilation strips irrelevant sections based on task types
|
||||
- [ ] **COMP-03**: Compiled plans saved as .compiled.md
|
||||
- [ ] **COMP-04**: Executor uses compiled version when available
|
||||
- [ ] **COMP-05**: Staleness detection via mtime comparison
|
||||
- [ ] **COMP-06**: Tests verify compilation output
|
||||
|
||||
## v2 Requirements
|
||||
|
||||
### Enhanced Semantic Queries
|
||||
|
||||
- **SEM-01**: gsd-memory MCP supports structured queries (what_uses, pattern_for, decisions_affecting)
|
||||
|
||||
## Out of Scope
|
||||
|
||||
| Feature | Reason |
|
||||
|---------|--------|
|
||||
| Compressing instructional content | Core methodology must remain intact |
|
||||
| Complex cache invalidation | Simple mtime checks are sufficient |
|
||||
| Railroad architecture | Loses adaptive intelligence |
|
||||
|
||||
## Traceability
|
||||
|
||||
| Requirement | Phase | Status |
|
||||
|-------------|-------|--------|
|
||||
| HIST-01 | Phase 1 | Pending |
|
||||
| HIST-02 | Phase 1 | Pending |
|
||||
| HIST-03 | Phase 1 | Pending |
|
||||
| HIST-04 | Phase 1 | Pending |
|
||||
| SUMM-01 | Phase 2 | Pending |
|
||||
| SUMM-02 | Phase 2 | Pending |
|
||||
| SUMM-03 | Phase 2 | Pending |
|
||||
| SUMM-04 | Phase 2 | Pending |
|
||||
| SUMM-05 | Phase 2 | Pending |
|
||||
| STATE-01 | Phase 3 | Pending |
|
||||
| STATE-02 | Phase 3 | Pending |
|
||||
| STATE-03 | Phase 3 | Pending |
|
||||
| STATE-04 | Phase 3 | Pending |
|
||||
| EXEC-01 | Phase 4 | Pending |
|
||||
| EXEC-02 | Phase 4 | Pending |
|
||||
| EXEC-03 | Phase 4 | Pending |
|
||||
| EXEC-04 | Phase 4 | Pending |
|
||||
| EXEC-05 | Phase 4 | Pending |
|
||||
| EXEC-06 | Phase 4 | Pending |
|
||||
| PLAN-01 | Phase 5 | Pending |
|
||||
| PLAN-02 | Phase 5 | Pending |
|
||||
| PLAN-03 | Phase 5 | Pending |
|
||||
| PLAN-04 | Phase 5 | Pending |
|
||||
| PLAN-05 | Phase 5 | Pending |
|
||||
| PLAN-06 | Phase 5 | Pending |
|
||||
| COMP-01 | Phase 6 | Pending |
|
||||
| COMP-02 | Phase 6 | Pending |
|
||||
| COMP-03 | Phase 6 | Pending |
|
||||
| COMP-04 | Phase 6 | Pending |
|
||||
| COMP-05 | Phase 6 | Pending |
|
||||
| COMP-06 | Phase 6 | Pending |
|
||||
|
||||
**Coverage:**
|
||||
- v1 requirements: 28 total
|
||||
- Mapped to phases: 28
|
||||
- Unmapped: 0 ✓
|
||||
|
||||
---
|
||||
*Requirements defined: 2025-02-07*
|
||||
*Last updated: 2025-02-07 after initial definition*
|
||||
183
.planning/ROADMAP.md
Normal file
183
.planning/ROADMAP.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Roadmap: GSD Context Optimization
|
||||
|
||||
**Created:** 2025-02-07
|
||||
**Phases:** 6
|
||||
**Depth:** Standard
|
||||
**Core Value:** Agents execute at peak quality by starting at 8-12% context instead of 15-25%
|
||||
|
||||
## Overview
|
||||
|
||||
| # | Phase | Goal | Requirements | Status |
|
||||
|---|-------|------|--------------|--------|
|
||||
| 1 | History Digest | Planner loads structured digest instead of full SUMMARY.md | HIST-01, HIST-02, HIST-03, HIST-04 | Pending |
|
||||
| 2 | Summary Variants | Executor produces right-sized summaries | SUMM-01, SUMM-02, SUMM-03, SUMM-04, SUMM-05 | Pending |
|
||||
| 3 | Atomic State | Agents update STATE.md fields atomically | STATE-01, STATE-02, STATE-03, STATE-04 | Pending |
|
||||
| 4 | Lazy Executor | Executor loads references on-demand | EXEC-01, EXEC-02, EXEC-03, EXEC-04, EXEC-05, EXEC-06 | Pending |
|
||||
| 5 | Tiered Planner | Planner prompt built dynamically | PLAN-01, PLAN-02, PLAN-03, PLAN-04, PLAN-05, PLAN-06 | Pending |
|
||||
| 6 | Compiled Plans | Plans pre-compiled for minimal execution context | COMP-01, COMP-02, COMP-03, COMP-04, COMP-05, COMP-06 | Pending |
|
||||
|
||||
## Dependencies
|
||||
|
||||
```
|
||||
Phase 1 ─┐
|
||||
Phase 2 ─┼─► Phase 4 ─► Phase 5 ─► Phase 6
|
||||
Phase 3 ─┘
|
||||
```
|
||||
|
||||
- Phases 1, 2, 3 can run in parallel (no dependencies)
|
||||
- Phase 4 depends on Phase 2 (summary templates) and Phase 3 (state ops)
|
||||
- Phase 5 depends on Phase 4 (executor patterns inform planner structure)
|
||||
- Phase 6 depends on Phase 4 and 5 (compilation targets both agents)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: History Digest
|
||||
|
||||
**Goal:** Planner loads structured digest instead of full SUMMARY.md files
|
||||
|
||||
**Requirements:** HIST-01, HIST-02, HIST-03, HIST-04
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. `gsd-tools history-digest` produces valid JSON with frontmatter fields
|
||||
2. Digest includes: phases, provides, patterns, affects, decisions, tech_stack
|
||||
3. Planner startup no longer reads full SUMMARY.md files
|
||||
4. Tests pass for digest schema validation
|
||||
|
||||
### Approach
|
||||
|
||||
- Add `history-digest` command to gsd-tools.js
|
||||
- Parse SUMMARY.md frontmatter (YAML between `---` markers)
|
||||
- Extract key fields into structured JSON
|
||||
- Update planner to consume digest instead of full files
|
||||
- TDD: Write tests first for digest output schema
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Summary Variants
|
||||
|
||||
**Goal:** Executor produces right-sized summaries based on plan complexity
|
||||
|
||||
**Requirements:** SUMM-01, SUMM-02, SUMM-03, SUMM-04, SUMM-05
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. Three template files exist: summary-minimal.md (~30 lines), summary-standard.md (~60 lines), summary-complex.md (~100 lines)
|
||||
2. `gsd-tools select-template` returns appropriate template path
|
||||
3. Selection considers: task count, decision tasks, file count
|
||||
4. Executor uses selected template for summary creation
|
||||
|
||||
### Approach
|
||||
|
||||
- Create three summary templates with increasing detail
|
||||
- Add `select-template` command with selection logic
|
||||
- Update executor workflow to call select-template
|
||||
- TDD: Write tests first for template selection logic
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Atomic State Operations
|
||||
|
||||
**Goal:** Agents update STATE.md fields atomically without full file read/write
|
||||
|
||||
**Requirements:** STATE-01, STATE-02, STATE-03, STATE-04
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. `gsd-tools state get <field>` returns specific STATE.md field value
|
||||
2. `gsd-tools state patch --field value` updates specific fields atomically
|
||||
3. Agent workflows updated to use atomic ops
|
||||
4. Tests verify get/patch operations preserve file integrity
|
||||
|
||||
### Approach
|
||||
|
||||
- Add `state get` subcommand to read specific fields
|
||||
- Add `state patch` subcommand for atomic field updates
|
||||
- Parse STATE.md as markdown, update in place
|
||||
- Update executor/planner workflows to use atomic ops
|
||||
- TDD: Write tests first for get/patch operations
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Lazy-Load Executor
|
||||
|
||||
**Goal:** Executor loads references on-demand based on task type
|
||||
|
||||
**Requirements:** EXEC-01, EXEC-02, EXEC-03, EXEC-04, EXEC-05, EXEC-06
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. `gsd-executor-core.md` contains base executor (~150 lines)
|
||||
2. `references/executor/` contains: deviation-rules.md, tdd-execution.md, checkpoint-protocol.md, continuation.md, summary-creation.md
|
||||
3. TDD reference loads only when task.tdd="true"
|
||||
4. Checkpoint reference loads only for task.type="checkpoint:*"
|
||||
5. Continuation reference loads only when <completed_tasks> present
|
||||
|
||||
### Approach
|
||||
|
||||
- Extract current executor into core + modular references
|
||||
- Core contains: role, task execution loop, basic flow
|
||||
- References contain: specialized protocols for specific situations
|
||||
- Add conditional @ references based on task attributes
|
||||
- Measure context reduction vs current executor
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Tiered Planner
|
||||
|
||||
**Goal:** Planner prompt built dynamically based on planning mode
|
||||
|
||||
**Requirements:** PLAN-01, PLAN-02, PLAN-03, PLAN-04, PLAN-05, PLAN-06
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. `gsd-planner-core.md` contains base planner (~300 lines)
|
||||
2. `gsd-planner-ext/` contains: gap-closure.md, revision.md, tdd.md, checkpoints.md
|
||||
3. Gap-closure loads only with --gaps flag
|
||||
4. Revision loads only when checker issues exist
|
||||
5. TDD loads only when TDD candidates detected
|
||||
|
||||
### Approach
|
||||
|
||||
- Extract current planner into core + extensions
|
||||
- Core contains: role, philosophy, task breakdown, plan format
|
||||
- Extensions contain: specialized planning modes
|
||||
- Update orchestrator to build prompt dynamically
|
||||
- Measure context reduction vs current planner
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Compiled Plans
|
||||
|
||||
**Goal:** Plans pre-compiled for minimal execution context
|
||||
|
||||
**Requirements:** COMP-01, COMP-02, COMP-03, COMP-04, COMP-05, COMP-06
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. `gsd-tools compile-plan` inlines all @ references
|
||||
2. Compilation strips irrelevant sections based on task types in plan
|
||||
3. Compiled plans saved as .compiled.md alongside original
|
||||
4. Executor uses compiled version when fresh (mtime check)
|
||||
5. Tests verify compilation output matches expected structure
|
||||
|
||||
### Approach
|
||||
|
||||
- Add `compile-plan` command to gsd-tools.js
|
||||
- Resolve all @ references recursively
|
||||
- Strip sections not relevant to this plan's tasks
|
||||
- Save as PLAN.compiled.md
|
||||
- Update executor to prefer compiled version
|
||||
- TDD: Write tests first for compilation logic
|
||||
|
||||
---
|
||||
|
||||
## Coverage
|
||||
|
||||
**v1 Requirements:** 28 total
|
||||
**Mapped to phases:** 28
|
||||
**Unmapped:** 0 ✓
|
||||
|
||||
---
|
||||
*Roadmap created: 2025-02-07*
|
||||
*Last updated: 2025-02-07 after initial creation*
|
||||
45
.planning/STATE.md
Normal file
45
.planning/STATE.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Project State: GSD Context Optimization
|
||||
|
||||
## Current Position
|
||||
|
||||
**Phase:** 1 - History Digest
|
||||
**Status:** Not Started
|
||||
**Plan:** None
|
||||
|
||||
## Project Reference
|
||||
|
||||
See: .planning/PROJECT.md (updated 2025-02-07)
|
||||
|
||||
**Core value:** Agents execute at peak quality by starting at 8-12% context instead of 15-25%
|
||||
**Current focus:** Phase 1 - History Digest
|
||||
|
||||
## Phase Progress
|
||||
|
||||
| Phase | Status | Plans |
|
||||
|-------|--------|-------|
|
||||
| 1 - History Digest | ○ Pending | 0/0 |
|
||||
| 2 - Summary Variants | ○ Pending | 0/0 |
|
||||
| 3 - Atomic State | ○ Pending | 0/0 |
|
||||
| 4 - Lazy Executor | ○ Pending | 0/0 |
|
||||
| 5 - Tiered Planner | ○ Pending | 0/0 |
|
||||
| 6 - Compiled Plans | ○ Pending | 0/0 |
|
||||
|
||||
## Recent Activity
|
||||
|
||||
- 2025-02-07: Project initialized
|
||||
- 2025-02-07: Requirements defined (28 total)
|
||||
- 2025-02-07: Roadmap created (6 phases)
|
||||
|
||||
## Blockers
|
||||
|
||||
None
|
||||
|
||||
## Key Decisions
|
||||
|
||||
| Decision | Phase | Rationale |
|
||||
|----------|-------|-----------|
|
||||
| TDD for gsd-tools changes | All | Write tests first to ensure behavior matches expectations |
|
||||
| Lazy loading over railroad | All | Preserves adaptive intelligence while reducing context |
|
||||
|
||||
---
|
||||
*Last updated: 2025-02-07 after initialization*
|
||||
12
.planning/config.json
Normal file
12
.planning/config.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"mode": "yolo",
|
||||
"depth": "standard",
|
||||
"parallelization": true,
|
||||
"commit_docs": true,
|
||||
"model_profile": "quality",
|
||||
"workflow": {
|
||||
"research": false,
|
||||
"plan_check": true,
|
||||
"verifier": true
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@ Orchestrator provides CONTEXT.md content in the verification prompt. If provided
|
||||
|
||||
```bash
|
||||
ls "$phase_dir"/*-PLAN.md 2>/dev/null
|
||||
grep -A 10 "Phase $phase_number" .planning/ROADMAP.md | head -15
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "$phase_number"
|
||||
ls "$phase_dir"/*-BRIEF.md 2>/dev/null
|
||||
```
|
||||
|
||||
|
||||
@@ -885,26 +885,40 @@ Apply discovery level protocol (see discovery_levels section).
|
||||
</step>
|
||||
|
||||
<step name="read_project_history">
|
||||
**Intelligent context assembly from frontmatter dependency graph:**
|
||||
**Two-step context assembly: digest for selection, full read for understanding.**
|
||||
|
||||
1. Scan all summary frontmatter:
|
||||
**Step 1 — Generate digest index:**
|
||||
```bash
|
||||
for f in .planning/phases/*/*-SUMMARY.md; do
|
||||
sed -n '1,/^---$/p; /^---$/q' "$f" | head -30
|
||||
done
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js history-digest
|
||||
```
|
||||
|
||||
2. Build dependency graph for current phase:
|
||||
- `affects` field: Which prior phases affect current?
|
||||
- `subsystem`: Which prior phases share same subsystem?
|
||||
- `requires` chains: Transitive dependencies
|
||||
- Roadmap: Any phases marked as dependencies?
|
||||
**Step 2 — Select relevant phases (typically 2-4):**
|
||||
|
||||
3. Select relevant summaries (typically 2-4 prior phases)
|
||||
Score each phase by relevance to current work:
|
||||
- `affects` overlap: Does it touch same subsystems?
|
||||
- `provides` dependency: Does current phase need what it created?
|
||||
- `patterns`: Are its patterns applicable?
|
||||
- Roadmap: Marked as explicit dependency?
|
||||
|
||||
4. Extract from frontmatter: tech available, patterns established, key files, decisions.
|
||||
Select top 2-4 phases. Skip phases with no relevance signal.
|
||||
|
||||
5. Read FULL summaries only for selected relevant phases.
|
||||
**Step 3 — Read full SUMMARYs for selected phases:**
|
||||
```bash
|
||||
cat .planning/phases/{selected-phase}/*-SUMMARY.md
|
||||
```
|
||||
|
||||
From full SUMMARYs extract:
|
||||
- How things were implemented (file patterns, code structure)
|
||||
- Why decisions were made (context, tradeoffs)
|
||||
- What problems were solved (avoid repeating)
|
||||
- Actual artifacts created (realistic expectations)
|
||||
|
||||
**Step 4 — Keep digest-level context for unselected phases:**
|
||||
|
||||
For phases not selected, retain from digest:
|
||||
- `tech_stack`: Available libraries
|
||||
- `decisions`: Constraints on approach
|
||||
- `patterns`: Conventions to follow
|
||||
|
||||
**From STATE.md:** Decisions → constrain approach. Pending todos → candidates.
|
||||
</step>
|
||||
|
||||
@@ -54,7 +54,7 @@ Set `is_re_verification = false`, proceed with Step 1.
|
||||
```bash
|
||||
ls "$PHASE_DIR"/*-PLAN.md 2>/dev/null
|
||||
ls "$PHASE_DIR"/*-SUMMARY.md 2>/dev/null
|
||||
grep -A 5 "Phase $PHASE_NUM" .planning/ROADMAP.md
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "$PHASE_NUM"
|
||||
grep -E "^| $PHASE_NUM" .planning/REQUIREMENTS.md 2>/dev/null
|
||||
```
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ RESEARCHER_MODEL=$(node ~/.claude/get-shit-done/bin/gsd-tools.js resolve-model g
|
||||
## 1. Validate Phase
|
||||
|
||||
```bash
|
||||
grep -A5 "Phase ${phase_number}:" .planning/ROADMAP.md 2>/dev/null
|
||||
PHASE_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${phase_number}")
|
||||
```
|
||||
|
||||
**If not found (phase_found=false):** Error and exit. **If found:** Extract phase number, name, description.
|
||||
**If `found` is false:** Error and exit. **If `found` is true:** Extract `phase_number`, `phase_name`, `goal` from JSON.
|
||||
|
||||
## 2. Check Existing Research
|
||||
|
||||
@@ -65,7 +65,8 @@ ls .planning/phases/${PHASE}-*/RESEARCH.md 2>/dev/null
|
||||
## 3. Gather Phase Context
|
||||
|
||||
```bash
|
||||
grep -A20 "Phase ${PHASE}:" .planning/ROADMAP.md
|
||||
# Phase section already loaded in PHASE_INFO
|
||||
echo "$PHASE_INFO" | jq -r '.section'
|
||||
cat .planning/REQUIREMENTS.md 2>/dev/null
|
||||
cat .planning/phases/${PHASE}-*/*-CONTEXT.md 2>/dev/null
|
||||
grep -A30 "### Decisions Made" .planning/STATE.md 2>/dev/null
|
||||
|
||||
@@ -146,6 +146,81 @@ function normalizePhaseName(phase) {
|
||||
return parts.length > 1 ? `${padded}.${parts[1]}` : padded;
|
||||
}
|
||||
|
||||
function extractFrontmatter(content) {
|
||||
const frontmatter = {};
|
||||
const match = content.match(/^---\n([\s\S]+?)\n---/);
|
||||
if (!match) return frontmatter;
|
||||
|
||||
const yaml = match[1];
|
||||
const lines = yaml.split('\n');
|
||||
|
||||
// Stack to track nested objects: [{obj, key, indent}]
|
||||
// obj = object to write to, key = current key collecting array items, indent = indentation level
|
||||
let stack = [{ obj: frontmatter, key: null, indent: -1 }];
|
||||
|
||||
for (const line of lines) {
|
||||
// Skip empty lines
|
||||
if (line.trim() === '') continue;
|
||||
|
||||
// Calculate indentation (number of leading spaces)
|
||||
const indentMatch = line.match(/^(\s*)/);
|
||||
const indent = indentMatch ? indentMatch[1].length : 0;
|
||||
|
||||
// Pop stack back to appropriate level
|
||||
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
const current = stack[stack.length - 1];
|
||||
|
||||
// Check for key: value pattern
|
||||
const keyMatch = line.match(/^(\s*)([a-zA-Z0-9_-]+):\s*(.*)/);
|
||||
if (keyMatch) {
|
||||
const key = keyMatch[2];
|
||||
const value = keyMatch[3].trim();
|
||||
|
||||
if (value === '' || value === '[') {
|
||||
// Key with no value or opening bracket — could be nested object or array
|
||||
// We'll determine based on next lines, for now create placeholder
|
||||
current.obj[key] = value === '[' ? [] : {};
|
||||
current.key = null;
|
||||
// Push new context for potential nested content
|
||||
stack.push({ obj: current.obj[key], key: null, indent });
|
||||
} else if (value.startsWith('[') && value.endsWith(']')) {
|
||||
// Inline array: key: [a, b, c]
|
||||
current.obj[key] = value.slice(1, -1).split(',').map(s => s.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
|
||||
current.key = null;
|
||||
} else {
|
||||
// Simple key: value
|
||||
current.obj[key] = value.replace(/^["']|["']$/g, '');
|
||||
current.key = null;
|
||||
}
|
||||
} else if (line.trim().startsWith('- ')) {
|
||||
// Array item
|
||||
const itemValue = line.trim().slice(2).replace(/^["']|["']$/g, '');
|
||||
|
||||
// If current context is an empty object, convert to array
|
||||
if (typeof current.obj === 'object' && !Array.isArray(current.obj) && Object.keys(current.obj).length === 0) {
|
||||
// Find the key in parent that points to this object and convert it
|
||||
const parent = stack.length > 1 ? stack[stack.length - 2] : null;
|
||||
if (parent) {
|
||||
for (const k of Object.keys(parent.obj)) {
|
||||
if (parent.obj[k] === current.obj) {
|
||||
parent.obj[k] = [itemValue];
|
||||
current.obj = parent.obj[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(current.obj)) {
|
||||
current.obj.push(itemValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frontmatter;
|
||||
}
|
||||
|
||||
function output(result, raw, rawValue) {
|
||||
if (raw && rawValue !== undefined) {
|
||||
process.stdout.write(String(rawValue));
|
||||
@@ -296,6 +371,290 @@ function cmdConfigEnsureSection(cwd, raw) {
|
||||
}
|
||||
}
|
||||
|
||||
function cmdHistoryDigest(cwd, raw) {
|
||||
const phasesDir = path.join(cwd, '.planning', 'phases');
|
||||
const digest = { phases: {}, decisions: [], tech_stack: new Set() };
|
||||
|
||||
if (!fs.existsSync(phasesDir)) {
|
||||
digest.tech_stack = [];
|
||||
output(digest, raw);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const phaseDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
|
||||
.filter(e => e.isDirectory())
|
||||
.map(e => e.name)
|
||||
.sort();
|
||||
|
||||
for (const dir of phaseDirs) {
|
||||
const dirPath = path.join(phasesDir, dir);
|
||||
const summaries = fs.readdirSync(dirPath).filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
|
||||
|
||||
for (const summary of summaries) {
|
||||
try {
|
||||
const content = fs.readFileSync(path.join(dirPath, summary), 'utf-8');
|
||||
const fm = extractFrontmatter(content);
|
||||
|
||||
const phaseNum = fm.phase || dir.split('-')[0];
|
||||
|
||||
if (!digest.phases[phaseNum]) {
|
||||
digest.phases[phaseNum] = {
|
||||
name: fm.name || dir.split('-').slice(1).join(' ') || 'Unknown',
|
||||
provides: new Set(),
|
||||
affects: new Set(),
|
||||
patterns: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
// Merge provides
|
||||
if (fm['dependency-graph'] && fm['dependency-graph'].provides) {
|
||||
fm['dependency-graph'].provides.forEach(p => digest.phases[phaseNum].provides.add(p));
|
||||
} else if (fm.provides) {
|
||||
fm.provides.forEach(p => digest.phases[phaseNum].provides.add(p));
|
||||
}
|
||||
|
||||
// Merge affects
|
||||
if (fm['dependency-graph'] && fm['dependency-graph'].affects) {
|
||||
fm['dependency-graph'].affects.forEach(a => digest.phases[phaseNum].affects.add(a));
|
||||
}
|
||||
|
||||
// Merge patterns
|
||||
if (fm['patterns-established']) {
|
||||
fm['patterns-established'].forEach(p => digest.phases[phaseNum].patterns.add(p));
|
||||
}
|
||||
|
||||
// Merge decisions
|
||||
if (fm['key-decisions']) {
|
||||
fm['key-decisions'].forEach(d => {
|
||||
digest.decisions.push({ phase: phaseNum, decision: d });
|
||||
});
|
||||
}
|
||||
|
||||
// Merge tech stack
|
||||
if (fm['tech-stack'] && fm['tech-stack'].added) {
|
||||
fm['tech-stack'].added.forEach(t => digest.tech_stack.add(typeof t === 'string' ? t : t.name));
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// Skip malformed summaries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Sets to Arrays for JSON output
|
||||
Object.keys(digest.phases).forEach(p => {
|
||||
digest.phases[p].provides = [...digest.phases[p].provides];
|
||||
digest.phases[p].affects = [...digest.phases[p].affects];
|
||||
digest.phases[p].patterns = [...digest.phases[p].patterns];
|
||||
});
|
||||
digest.tech_stack = [...digest.tech_stack];
|
||||
|
||||
output(digest, raw);
|
||||
} catch (e) {
|
||||
error('Failed to generate history digest: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function cmdPhasesList(cwd, options, raw) {
|
||||
const phasesDir = path.join(cwd, '.planning', 'phases');
|
||||
const { type, phase } = options;
|
||||
|
||||
// If no phases directory, return empty
|
||||
if (!fs.existsSync(phasesDir)) {
|
||||
if (type) {
|
||||
output({ files: [], count: 0 }, raw, '');
|
||||
} else {
|
||||
output({ directories: [], count: 0 }, raw, '');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get all phase directories
|
||||
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
||||
let dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
||||
|
||||
// Sort numerically (handles decimals: 01, 02, 02.1, 02.2, 03)
|
||||
dirs.sort((a, b) => {
|
||||
const aNum = parseFloat(a.match(/^(\d+(?:\.\d+)?)/)?.[1] || '0');
|
||||
const bNum = parseFloat(b.match(/^(\d+(?:\.\d+)?)/)?.[1] || '0');
|
||||
return aNum - bNum;
|
||||
});
|
||||
|
||||
// If filtering by phase number
|
||||
if (phase) {
|
||||
const normalized = normalizePhaseName(phase);
|
||||
const match = dirs.find(d => d.startsWith(normalized));
|
||||
if (!match) {
|
||||
output({ files: [], count: 0, phase_dir: null, error: 'Phase not found' }, raw, '');
|
||||
return;
|
||||
}
|
||||
dirs = [match];
|
||||
}
|
||||
|
||||
// If listing files of a specific type
|
||||
if (type) {
|
||||
const files = [];
|
||||
for (const dir of dirs) {
|
||||
const dirPath = path.join(phasesDir, dir);
|
||||
const dirFiles = fs.readdirSync(dirPath);
|
||||
|
||||
let filtered;
|
||||
if (type === 'plans') {
|
||||
filtered = dirFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
|
||||
} else if (type === 'summaries') {
|
||||
filtered = dirFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
|
||||
} else {
|
||||
filtered = dirFiles;
|
||||
}
|
||||
|
||||
files.push(...filtered.sort());
|
||||
}
|
||||
|
||||
const result = {
|
||||
files,
|
||||
count: files.length,
|
||||
phase_dir: phase ? dirs[0].replace(/^\d+(?:\.\d+)?-?/, '') : null,
|
||||
};
|
||||
output(result, raw, files.join('\n'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: list directories
|
||||
output({ directories: dirs, count: dirs.length }, raw, dirs.join('\n'));
|
||||
} catch (e) {
|
||||
error('Failed to list phases: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
||||
const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
|
||||
|
||||
if (!fs.existsSync(roadmapPath)) {
|
||||
output({ found: false, error: 'ROADMAP.md not found' }, raw, '');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
||||
|
||||
// Escape special regex chars in phase number, handle decimal
|
||||
const escapedPhase = phaseNum.replace(/\./g, '\\.');
|
||||
|
||||
// Match "### Phase X:" or "### Phase X.Y:" with optional name
|
||||
const phasePattern = new RegExp(
|
||||
`###\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
|
||||
'i'
|
||||
);
|
||||
const headerMatch = content.match(phasePattern);
|
||||
|
||||
if (!headerMatch) {
|
||||
output({ found: false, phase_number: phaseNum }, raw, '');
|
||||
return;
|
||||
}
|
||||
|
||||
const phaseName = headerMatch[1].trim();
|
||||
const headerIndex = headerMatch.index;
|
||||
|
||||
// Find the end of this section (next ### or end of file)
|
||||
const restOfContent = content.slice(headerIndex);
|
||||
const nextHeaderMatch = restOfContent.match(/\n###\s+Phase\s+\d/i);
|
||||
const sectionEnd = nextHeaderMatch
|
||||
? headerIndex + nextHeaderMatch.index
|
||||
: content.length;
|
||||
|
||||
const section = content.slice(headerIndex, sectionEnd).trim();
|
||||
|
||||
// Extract goal if present
|
||||
const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
|
||||
const goal = goalMatch ? goalMatch[1].trim() : null;
|
||||
|
||||
output(
|
||||
{
|
||||
found: true,
|
||||
phase_number: phaseNum,
|
||||
phase_name: phaseName,
|
||||
goal,
|
||||
section,
|
||||
},
|
||||
raw,
|
||||
section
|
||||
);
|
||||
} catch (e) {
|
||||
error('Failed to read ROADMAP.md: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function cmdPhaseNextDecimal(cwd, basePhase, raw) {
|
||||
const phasesDir = path.join(cwd, '.planning', 'phases');
|
||||
const normalized = normalizePhaseName(basePhase);
|
||||
|
||||
// Check if phases directory exists
|
||||
if (!fs.existsSync(phasesDir)) {
|
||||
output(
|
||||
{
|
||||
found: false,
|
||||
base_phase: normalized,
|
||||
next: `${normalized}.1`,
|
||||
existing: [],
|
||||
},
|
||||
raw,
|
||||
`${normalized}.1`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
||||
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
||||
|
||||
// Check if base phase exists
|
||||
const baseExists = dirs.some(d => d.startsWith(normalized + '-') || d === normalized);
|
||||
|
||||
// Find existing decimal phases for this base
|
||||
const decimalPattern = new RegExp(`^${normalized}\\.(\\d+)`);
|
||||
const existingDecimals = [];
|
||||
|
||||
for (const dir of dirs) {
|
||||
const match = dir.match(decimalPattern);
|
||||
if (match) {
|
||||
existingDecimals.push(`${normalized}.${match[1]}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort numerically
|
||||
existingDecimals.sort((a, b) => {
|
||||
const aNum = parseFloat(a);
|
||||
const bNum = parseFloat(b);
|
||||
return aNum - bNum;
|
||||
});
|
||||
|
||||
// Calculate next decimal
|
||||
let nextDecimal;
|
||||
if (existingDecimals.length === 0) {
|
||||
nextDecimal = `${normalized}.1`;
|
||||
} else {
|
||||
const lastDecimal = existingDecimals[existingDecimals.length - 1];
|
||||
const lastNum = parseInt(lastDecimal.split('.')[1], 10);
|
||||
nextDecimal = `${normalized}.${lastNum + 1}`;
|
||||
}
|
||||
|
||||
output(
|
||||
{
|
||||
found: baseExists,
|
||||
base_phase: normalized,
|
||||
next: nextDecimal,
|
||||
existing: existingDecimals,
|
||||
},
|
||||
raw,
|
||||
nextDecimal
|
||||
);
|
||||
} catch (e) {
|
||||
error('Failed to calculate next decimal phase: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function cmdStateLoad(cwd, raw) {
|
||||
const config = loadConfig(cwd);
|
||||
const planningDir = path.join(cwd, '.planning');
|
||||
@@ -341,6 +700,69 @@ function cmdStateLoad(cwd, raw) {
|
||||
output(result);
|
||||
}
|
||||
|
||||
function cmdStateGet(cwd, section, raw) {
|
||||
const statePath = path.join(cwd, '.planning', 'STATE.md');
|
||||
try {
|
||||
const content = fs.readFileSync(statePath, 'utf-8');
|
||||
|
||||
if (!section) {
|
||||
output({ content }, raw, content);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to find markdown section or field
|
||||
const fieldEscaped = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
// Check for **field:** value
|
||||
const fieldPattern = new RegExp(`\\*\\*${fieldEscaped}:\\*\\*\\s*(.*)`, 'i');
|
||||
const fieldMatch = content.match(fieldPattern);
|
||||
if (fieldMatch) {
|
||||
output({ [section]: fieldMatch[1].trim() }, raw, fieldMatch[1].trim());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for ## Section
|
||||
const sectionPattern = new RegExp(`##\\s*${fieldEscaped}\\s*\n([\\s\\S]*?)(?=\\n##|$)`, 'i');
|
||||
const sectionMatch = content.match(sectionPattern);
|
||||
if (sectionMatch) {
|
||||
output({ [section]: sectionMatch[1].trim() }, raw, sectionMatch[1].trim());
|
||||
return;
|
||||
}
|
||||
|
||||
output({ error: `Section or field "${section}" not found` }, raw, '');
|
||||
} catch {
|
||||
error('STATE.md not found');
|
||||
}
|
||||
}
|
||||
|
||||
function cmdStatePatch(cwd, patches, raw) {
|
||||
const statePath = path.join(cwd, '.planning', 'STATE.md');
|
||||
try {
|
||||
let content = fs.readFileSync(statePath, 'utf-8');
|
||||
const results = { updated: [], failed: [] };
|
||||
|
||||
for (const [field, value] of Object.entries(patches)) {
|
||||
const fieldEscaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`(\\*\\*${fieldEscaped}:\\*\\*\\s*)(.*)`, 'i');
|
||||
|
||||
if (pattern.test(content)) {
|
||||
content = content.replace(pattern, `$1${value}`);
|
||||
results.updated.push(field);
|
||||
} else {
|
||||
results.failed.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.updated.length > 0) {
|
||||
fs.writeFileSync(statePath, content, 'utf-8');
|
||||
}
|
||||
|
||||
output(results, raw, results.updated.length > 0 ? 'true' : 'false');
|
||||
} catch {
|
||||
error('STATE.md not found');
|
||||
}
|
||||
}
|
||||
|
||||
function cmdStateUpdate(cwd, field, value) {
|
||||
if (!field || value === undefined) {
|
||||
error('field and value required for state update');
|
||||
@@ -570,6 +992,52 @@ function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
|
||||
output(result, raw, passed ? 'passed' : 'failed');
|
||||
}
|
||||
|
||||
function cmdTemplateSelect(cwd, planPath, raw) {
|
||||
if (!planPath) {
|
||||
error('plan-path required');
|
||||
}
|
||||
|
||||
try {
|
||||
const fullPath = path.join(cwd, planPath);
|
||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||
|
||||
// Simple heuristics
|
||||
const taskMatch = content.match(/###\s*Task\s*\d+/g) || [];
|
||||
const taskCount = taskMatch.length;
|
||||
|
||||
const decisionMatch = content.match(/decision/gi) || [];
|
||||
const hasDecisions = decisionMatch.length > 0;
|
||||
|
||||
// Count file mentions
|
||||
const fileMentions = new Set();
|
||||
const filePattern = /`([^`]+\.[a-zA-Z]+)`/g;
|
||||
let m;
|
||||
while ((m = filePattern.exec(content)) !== null) {
|
||||
if (m[1].includes('/') && !m[1].startsWith('http')) {
|
||||
fileMentions.add(m[1]);
|
||||
}
|
||||
}
|
||||
const fileCount = fileMentions.size;
|
||||
|
||||
let template = 'templates/summary-standard.md';
|
||||
let type = 'standard';
|
||||
|
||||
if (taskCount <= 2 && fileCount <= 3 && !hasDecisions) {
|
||||
template = 'templates/summary-minimal.md';
|
||||
type = 'minimal';
|
||||
} else if (hasDecisions || fileCount > 6 || taskCount > 5) {
|
||||
template = 'templates/summary-complex.md';
|
||||
type = 'complex';
|
||||
}
|
||||
|
||||
const result = { template, type, taskCount, fileCount, hasDecisions };
|
||||
output(result, raw, template);
|
||||
} catch (e) {
|
||||
// Fallback to standard
|
||||
output({ template: 'templates/summary-standard.md', type: 'standard', error: e.message }, raw, 'templates/summary-standard.md');
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Compound Commands ────────────────────────────────────────────────────────
|
||||
|
||||
function resolveModelInternal(cwd, agentType) {
|
||||
@@ -1238,6 +1706,18 @@ function main() {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'update') {
|
||||
cmdStateUpdate(cwd, args[2], args[3]);
|
||||
} else if (subcommand === 'get') {
|
||||
cmdStateGet(cwd, args[2], raw);
|
||||
} else if (subcommand === 'patch') {
|
||||
const patches = {};
|
||||
for (let i = 2; i < args.length; i += 2) {
|
||||
const key = args[i].replace(/^--/, '');
|
||||
const value = args[i + 1];
|
||||
if (key && value !== undefined) {
|
||||
patches[key] = value;
|
||||
}
|
||||
}
|
||||
cmdStatePatch(cwd, patches, raw);
|
||||
} else {
|
||||
cmdStateLoad(cwd, raw);
|
||||
}
|
||||
@@ -1271,6 +1751,14 @@ function main() {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'template': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'select') {
|
||||
cmdTemplateSelect(cwd, args[2], raw);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'generate-slug': {
|
||||
cmdGenerateSlug(args[1], raw);
|
||||
break;
|
||||
@@ -1296,6 +1784,47 @@ function main() {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'history-digest': {
|
||||
cmdHistoryDigest(cwd, raw);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'phases': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'list') {
|
||||
const typeIndex = args.indexOf('--type');
|
||||
const phaseIndex = args.indexOf('--phase');
|
||||
const options = {
|
||||
type: typeIndex !== -1 ? args[typeIndex + 1] : null,
|
||||
phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
|
||||
};
|
||||
cmdPhasesList(cwd, options, raw);
|
||||
} else {
|
||||
error('Unknown phases subcommand. Available: list');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'roadmap': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'get-phase') {
|
||||
cmdRoadmapGetPhase(cwd, args[2], raw);
|
||||
} else {
|
||||
error('Unknown roadmap subcommand. Available: get-phase');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'phase': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'next-decimal') {
|
||||
cmdPhaseNextDecimal(cwd, args[2], raw);
|
||||
} else {
|
||||
error('Unknown phase subcommand. Available: next-decimal');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'init': {
|
||||
const workflow = args[1];
|
||||
switch (workflow) {
|
||||
|
||||
599
get-shit-done/bin/gsd-tools.test.js
Normal file
599
get-shit-done/bin/gsd-tools.test.js
Normal file
@@ -0,0 +1,599 @@
|
||||
/**
|
||||
* GSD Tools Tests — Schema validation for history-digest command
|
||||
*/
|
||||
|
||||
const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const TOOLS_PATH = path.join(__dirname, 'gsd-tools.js');
|
||||
|
||||
// Helper to run gsd-tools command
|
||||
function runGsdTools(args, cwd = process.cwd()) {
|
||||
try {
|
||||
const result = execSync(`node "${TOOLS_PATH}" ${args}`, {
|
||||
cwd,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
return { success: true, output: result.trim() };
|
||||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
output: err.stdout?.toString().trim() || '',
|
||||
error: err.stderr?.toString().trim() || err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Create temp directory structure
|
||||
function createTempProject() {
|
||||
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'gsd-test-'));
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true });
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
function cleanup(tmpDir) {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
describe('history-digest command', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('empty phases directory returns valid schema', () => {
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
|
||||
assert.deepStrictEqual(digest.phases, {}, 'phases should be empty object');
|
||||
assert.deepStrictEqual(digest.decisions, [], 'decisions should be empty array');
|
||||
assert.deepStrictEqual(digest.tech_stack, [], 'tech_stack should be empty array');
|
||||
});
|
||||
|
||||
test('nested frontmatter fields extracted correctly', () => {
|
||||
// Create phase directory with SUMMARY containing nested frontmatter
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
const summaryContent = `---
|
||||
phase: "01"
|
||||
name: "Foundation Setup"
|
||||
dependency-graph:
|
||||
provides:
|
||||
- "Database schema"
|
||||
- "Auth system"
|
||||
affects:
|
||||
- "API layer"
|
||||
tech-stack:
|
||||
added:
|
||||
- "prisma"
|
||||
- "jose"
|
||||
patterns-established:
|
||||
- "Repository pattern"
|
||||
- "JWT auth flow"
|
||||
key-decisions:
|
||||
- "Use Prisma over Drizzle"
|
||||
- "JWT in httpOnly cookies"
|
||||
---
|
||||
|
||||
# Summary content here
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-SUMMARY.md'), summaryContent);
|
||||
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
|
||||
// Check nested dependency-graph.provides
|
||||
assert.ok(digest.phases['01'], 'Phase 01 should exist');
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].provides.sort(),
|
||||
['Auth system', 'Database schema'],
|
||||
'provides should contain nested values'
|
||||
);
|
||||
|
||||
// Check nested dependency-graph.affects
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].affects,
|
||||
['API layer'],
|
||||
'affects should contain nested values'
|
||||
);
|
||||
|
||||
// Check nested tech-stack.added
|
||||
assert.deepStrictEqual(
|
||||
digest.tech_stack.sort(),
|
||||
['jose', 'prisma'],
|
||||
'tech_stack should contain nested values'
|
||||
);
|
||||
|
||||
// Check patterns-established (flat array)
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].patterns.sort(),
|
||||
['JWT auth flow', 'Repository pattern'],
|
||||
'patterns should be extracted'
|
||||
);
|
||||
|
||||
// Check key-decisions
|
||||
assert.strictEqual(digest.decisions.length, 2, 'Should have 2 decisions');
|
||||
assert.ok(
|
||||
digest.decisions.some(d => d.decision === 'Use Prisma over Drizzle'),
|
||||
'Should contain first decision'
|
||||
);
|
||||
});
|
||||
|
||||
test('multiple phases merged into single digest', () => {
|
||||
// Create phase 01
|
||||
const phase01Dir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
fs.mkdirSync(phase01Dir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(phase01Dir, '01-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "01"
|
||||
name: "Foundation"
|
||||
provides:
|
||||
- "Database"
|
||||
patterns-established:
|
||||
- "Pattern A"
|
||||
key-decisions:
|
||||
- "Decision 1"
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
// Create phase 02
|
||||
const phase02Dir = path.join(tmpDir, '.planning', 'phases', '02-api');
|
||||
fs.mkdirSync(phase02Dir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(phase02Dir, '02-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "02"
|
||||
name: "API"
|
||||
provides:
|
||||
- "REST endpoints"
|
||||
patterns-established:
|
||||
- "Pattern B"
|
||||
key-decisions:
|
||||
- "Decision 2"
|
||||
tech-stack:
|
||||
added:
|
||||
- "zod"
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
|
||||
// Both phases present
|
||||
assert.ok(digest.phases['01'], 'Phase 01 should exist');
|
||||
assert.ok(digest.phases['02'], 'Phase 02 should exist');
|
||||
|
||||
// Decisions merged
|
||||
assert.strictEqual(digest.decisions.length, 2, 'Should have 2 decisions total');
|
||||
|
||||
// Tech stack merged
|
||||
assert.deepStrictEqual(digest.tech_stack, ['zod'], 'tech_stack should have zod');
|
||||
});
|
||||
|
||||
test('malformed SUMMARY.md skipped gracefully', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
// Valid summary
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "01"
|
||||
provides:
|
||||
- "Valid feature"
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
// Malformed summary (no frontmatter)
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-02-SUMMARY.md'),
|
||||
`# Just a heading
|
||||
No frontmatter here
|
||||
`
|
||||
);
|
||||
|
||||
// Another malformed summary (broken YAML)
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-03-SUMMARY.md'),
|
||||
`---
|
||||
broken: [unclosed
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command should succeed despite malformed files: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
assert.ok(digest.phases['01'], 'Phase 01 should exist');
|
||||
assert.ok(
|
||||
digest.phases['01'].provides.includes('Valid feature'),
|
||||
'Valid feature should be extracted'
|
||||
);
|
||||
});
|
||||
|
||||
test('flat provides field still works (backward compatibility)', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "01"
|
||||
provides:
|
||||
- "Direct provides"
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].provides,
|
||||
['Direct provides'],
|
||||
'Direct provides should work'
|
||||
);
|
||||
});
|
||||
|
||||
test('inline array syntax supported', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(phaseDir, '01-01-SUMMARY.md'),
|
||||
`---
|
||||
phase: "01"
|
||||
provides: [Feature A, Feature B]
|
||||
patterns-established: ["Pattern X", "Pattern Y"]
|
||||
---
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].provides.sort(),
|
||||
['Feature A', 'Feature B'],
|
||||
'Inline array should work'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
digest.phases['01'].patterns.sort(),
|
||||
['Pattern X', 'Pattern Y'],
|
||||
'Inline quoted array should work'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// phases list command
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('phases list command', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('empty phases directory returns empty array', () => {
|
||||
const result = runGsdTools('phases list', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(output.directories, [], 'directories should be empty');
|
||||
assert.strictEqual(output.count, 0, 'count should be 0');
|
||||
});
|
||||
|
||||
test('lists phase directories sorted numerically', () => {
|
||||
// Create out-of-order directories
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '10-final'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-api'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-foundation'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phases list', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.count, 3, 'should have 3 directories');
|
||||
assert.deepStrictEqual(
|
||||
output.directories,
|
||||
['01-foundation', '02-api', '10-final'],
|
||||
'should be sorted numerically'
|
||||
);
|
||||
});
|
||||
|
||||
test('handles decimal phases in sort order', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02-api'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02.1-hotfix'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '02.2-patch'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '03-ui'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phases list', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
output.directories,
|
||||
['02-api', '02.1-hotfix', '02.2-patch', '03-ui'],
|
||||
'decimal phases should sort correctly between whole numbers'
|
||||
);
|
||||
});
|
||||
|
||||
test('--type plans lists only PLAN.md files', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), '# Plan 1');
|
||||
fs.writeFileSync(path.join(phaseDir, '01-02-PLAN.md'), '# Plan 2');
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-SUMMARY.md'), '# Summary');
|
||||
fs.writeFileSync(path.join(phaseDir, 'RESEARCH.md'), '# Research');
|
||||
|
||||
const result = runGsdTools('phases list --type plans', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
output.files.sort(),
|
||||
['01-01-PLAN.md', '01-02-PLAN.md'],
|
||||
'should list only PLAN files'
|
||||
);
|
||||
});
|
||||
|
||||
test('--type summaries lists only SUMMARY.md files', () => {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-test');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-PLAN.md'), '# Plan');
|
||||
fs.writeFileSync(path.join(phaseDir, '01-01-SUMMARY.md'), '# Summary 1');
|
||||
fs.writeFileSync(path.join(phaseDir, '01-02-SUMMARY.md'), '# Summary 2');
|
||||
|
||||
const result = runGsdTools('phases list --type summaries', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(
|
||||
output.files.sort(),
|
||||
['01-01-SUMMARY.md', '01-02-SUMMARY.md'],
|
||||
'should list only SUMMARY files'
|
||||
);
|
||||
});
|
||||
|
||||
test('--phase filters to specific phase directory', () => {
|
||||
const phase01 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
|
||||
const phase02 = path.join(tmpDir, '.planning', 'phases', '02-api');
|
||||
fs.mkdirSync(phase01, { recursive: true });
|
||||
fs.mkdirSync(phase02, { recursive: true });
|
||||
fs.writeFileSync(path.join(phase01, '01-01-PLAN.md'), '# Plan');
|
||||
fs.writeFileSync(path.join(phase02, '02-01-PLAN.md'), '# Plan');
|
||||
|
||||
const result = runGsdTools('phases list --type plans --phase 01', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(output.files, ['01-01-PLAN.md'], 'should only list phase 01 plans');
|
||||
assert.strictEqual(output.phase_dir, 'foundation', 'should report phase name without number prefix');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// roadmap get-phase command
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('roadmap get-phase command', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('extracts phase section from ROADMAP.md', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap v1.0
|
||||
|
||||
## Phases
|
||||
|
||||
### Phase 1: Foundation
|
||||
**Goal:** Set up project infrastructure
|
||||
**Plans:** 2 plans
|
||||
|
||||
Some description here.
|
||||
|
||||
### Phase 2: API
|
||||
**Goal:** Build REST API
|
||||
**Plans:** 3 plans
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('roadmap get-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, true, 'phase should be found');
|
||||
assert.strictEqual(output.phase_number, '1', 'phase number correct');
|
||||
assert.strictEqual(output.phase_name, 'Foundation', 'phase name extracted');
|
||||
assert.strictEqual(output.goal, 'Set up project infrastructure', 'goal extracted');
|
||||
});
|
||||
|
||||
test('returns not found for missing phase', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap v1.0
|
||||
|
||||
### Phase 1: Foundation
|
||||
**Goal:** Set up project
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('roadmap get-phase 5', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, false, 'phase should not be found');
|
||||
});
|
||||
|
||||
test('handles decimal phase numbers', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap
|
||||
|
||||
### Phase 2: Main
|
||||
**Goal:** Main work
|
||||
|
||||
### Phase 2.1: Hotfix
|
||||
**Goal:** Emergency fix
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('roadmap get-phase 2.1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, true, 'decimal phase should be found');
|
||||
assert.strictEqual(output.phase_name, 'Hotfix', 'phase name correct');
|
||||
assert.strictEqual(output.goal, 'Emergency fix', 'goal extracted');
|
||||
});
|
||||
|
||||
test('extracts full section content', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
||||
`# Roadmap
|
||||
|
||||
### Phase 1: Setup
|
||||
**Goal:** Initialize everything
|
||||
|
||||
This phase covers:
|
||||
- Database setup
|
||||
- Auth configuration
|
||||
- CI/CD pipeline
|
||||
|
||||
### Phase 2: Build
|
||||
**Goal:** Build features
|
||||
`
|
||||
);
|
||||
|
||||
const result = runGsdTools('roadmap get-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.ok(output.section.includes('Database setup'), 'section includes description');
|
||||
assert.ok(output.section.includes('CI/CD pipeline'), 'section includes all bullets');
|
||||
assert.ok(!output.section.includes('Phase 2'), 'section does not include next phase');
|
||||
});
|
||||
|
||||
test('handles missing ROADMAP.md gracefully', () => {
|
||||
const result = runGsdTools('roadmap get-phase 1', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, false, 'should return not found');
|
||||
assert.strictEqual(output.error, 'ROADMAP.md not found', 'should explain why');
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// phase next-decimal command
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('phase next-decimal command', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('returns X.1 when no decimal phases exist', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06-feature'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '07-next'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phase next-decimal 06', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.next, '06.1', 'should return 06.1');
|
||||
assert.deepStrictEqual(output.existing, [], 'no existing decimals');
|
||||
});
|
||||
|
||||
test('increments from existing decimal phases', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06-feature'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.1-hotfix'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.2-patch'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phase next-decimal 06', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.next, '06.3', 'should return 06.3');
|
||||
assert.deepStrictEqual(output.existing, ['06.1', '06.2'], 'lists existing decimals');
|
||||
});
|
||||
|
||||
test('handles gaps in decimal sequence', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06-feature'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.1-first'), { recursive: true });
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06.3-third'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phase next-decimal 06', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
// Should take next after highest, not fill gap
|
||||
assert.strictEqual(output.next, '06.4', 'should return 06.4, not fill gap at 06.2');
|
||||
});
|
||||
|
||||
test('handles single-digit phase input', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '06-feature'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phase next-decimal 6', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.next, '06.1', 'should normalize to 06.1');
|
||||
assert.strictEqual(output.base_phase, '06', 'base phase should be padded');
|
||||
});
|
||||
|
||||
test('returns error if base phase does not exist', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases', '01-start'), { recursive: true });
|
||||
|
||||
const result = runGsdTools('phase next-decimal 06', tmpDir);
|
||||
assert.ok(result.success, `Command should succeed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.found, false, 'base phase not found');
|
||||
assert.strictEqual(output.next, '06.1', 'should still suggest 06.1');
|
||||
});
|
||||
});
|
||||
@@ -2,40 +2,45 @@
|
||||
|
||||
Calculate the next decimal phase number for urgent insertions.
|
||||
|
||||
## Find Existing Decimals
|
||||
|
||||
For a given integer phase, find all existing decimal phases:
|
||||
## Using gsd-tools
|
||||
|
||||
```bash
|
||||
# Find decimal phases after integer phase N (e.g., 06.1, 06.2)
|
||||
AFTER_PHASE=$1 # e.g., 6
|
||||
|
||||
# Pad to 2 digits
|
||||
PADDED=$(printf "%02d" "$AFTER_PHASE")
|
||||
|
||||
# Find existing decimals
|
||||
EXISTING=$(ls -d .planning/phases/${PADDED}.*-* 2>/dev/null | \
|
||||
xargs -I{} basename {} | \
|
||||
grep -oE '^[0-9]+\.[0-9]+' | \
|
||||
sort -V)
|
||||
# Get next decimal phase after phase 6
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js phase next-decimal 6
|
||||
```
|
||||
|
||||
## Calculate Next Decimal
|
||||
Output:
|
||||
```json
|
||||
{
|
||||
"found": true,
|
||||
"base_phase": "06",
|
||||
"next": "06.1",
|
||||
"existing": []
|
||||
}
|
||||
```
|
||||
|
||||
Find the highest decimal suffix and increment:
|
||||
With existing decimals:
|
||||
```json
|
||||
{
|
||||
"found": true,
|
||||
"base_phase": "06",
|
||||
"next": "06.3",
|
||||
"existing": ["06.1", "06.2"]
|
||||
}
|
||||
```
|
||||
|
||||
## Extract Values
|
||||
|
||||
```bash
|
||||
if [ -z "$EXISTING" ]; then
|
||||
# No decimals exist, start at .1
|
||||
NEXT_DECIMAL="1"
|
||||
else
|
||||
# Get highest decimal suffix
|
||||
MAX_SUFFIX=$(echo "$EXISTING" | tail -1 | grep -oE '\.[0-9]+$' | tr -d '.')
|
||||
NEXT_DECIMAL=$((MAX_SUFFIX + 1))
|
||||
fi
|
||||
DECIMAL_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase next-decimal "${AFTER_PHASE}")
|
||||
DECIMAL_PHASE=$(echo "$DECIMAL_INFO" | jq -r '.next')
|
||||
BASE_PHASE=$(echo "$DECIMAL_INFO" | jq -r '.base_phase')
|
||||
```
|
||||
|
||||
# Format: 06.1, 06.2, etc.
|
||||
DECIMAL_PHASE="${PADDED}.${NEXT_DECIMAL}"
|
||||
Or with --raw flag:
|
||||
```bash
|
||||
DECIMAL_PHASE=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase next-decimal "${AFTER_PHASE}" --raw)
|
||||
# Returns just: 06.1
|
||||
```
|
||||
|
||||
## Examples
|
||||
@@ -45,13 +50,14 @@ DECIMAL_PHASE="${PADDED}.${NEXT_DECIMAL}"
|
||||
| 06 only | 06.1 |
|
||||
| 06, 06.1 | 06.2 |
|
||||
| 06, 06.1, 06.2 | 06.3 |
|
||||
| 06, 06.1, 06.3 (gap) | 06.4 |
|
||||
|
||||
## Directory Naming
|
||||
|
||||
Decimal phase directories use the full decimal number:
|
||||
|
||||
```bash
|
||||
SLUG=$(echo "$DESCRIPTION" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
|
||||
SLUG=$(node ~/.claude/get-shit-done/bin/gsd-tools.js generate-slug "$DESCRIPTION" --raw)
|
||||
PHASE_DIR=".planning/phases/${DECIMAL_PHASE}-${SLUG}"
|
||||
mkdir -p "$PHASE_DIR"
|
||||
```
|
||||
|
||||
@@ -9,7 +9,23 @@ From `$ARGUMENTS`:
|
||||
- Extract flags (prefixed with `--`)
|
||||
- Remaining text is description (for insert/add commands)
|
||||
|
||||
## Normalization
|
||||
## Using gsd-tools
|
||||
|
||||
The `find-phase` command handles normalization and validation in one step:
|
||||
|
||||
```bash
|
||||
PHASE_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js find-phase "${PHASE}")
|
||||
```
|
||||
|
||||
Returns JSON with:
|
||||
- `found`: true/false
|
||||
- `directory`: Full path to phase directory
|
||||
- `phase_number`: Normalized number (e.g., "06", "06.1")
|
||||
- `phase_name`: Name portion (e.g., "foundation")
|
||||
- `plans`: Array of PLAN.md files
|
||||
- `summaries`: Array of SUMMARY.md files
|
||||
|
||||
## Manual Normalization (Legacy)
|
||||
|
||||
Zero-pad integer phases to 2 digits. Preserve decimal suffixes.
|
||||
|
||||
@@ -24,35 +40,22 @@ elif [[ "$PHASE" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
|
||||
fi
|
||||
```
|
||||
|
||||
## Auto-Detection
|
||||
|
||||
When no phase number provided, detect the next unplanned phase:
|
||||
|
||||
```bash
|
||||
# Find phases without PLAN.md files
|
||||
for dir in .planning/phases/*/; do
|
||||
if ! ls "$dir"/*-PLAN.md 2>/dev/null | head -1 >/dev/null; then
|
||||
PHASE=$(basename "$dir" | grep -oE '^[0-9.]+')
|
||||
break
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
After normalization, verify phase exists in ROADMAP.md:
|
||||
Use `roadmap get-phase` to validate phase exists:
|
||||
|
||||
```bash
|
||||
grep -q "### Phase ${PHASE}:" .planning/ROADMAP.md || {
|
||||
PHASE_CHECK=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}")
|
||||
if [ "$(echo "$PHASE_CHECK" | jq -r '.found')" = "false" ]; then
|
||||
echo "ERROR: Phase ${PHASE} not found in roadmap"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
```
|
||||
|
||||
## Directory Lookup
|
||||
|
||||
Find the phase directory using the normalized phase number:
|
||||
Use `find-phase` for directory lookup:
|
||||
|
||||
```bash
|
||||
PHASE_DIR=$(ls -d .planning/phases/${PHASE}-* 2>/dev/null | head -1)
|
||||
PHASE_DIR=$(node ~/.claude/get-shit-done/bin/gsd-tools.js find-phase "${PHASE}" --raw)
|
||||
```
|
||||
|
||||
59
get-shit-done/templates/summary-complex.md
Normal file
59
get-shit-done/templates/summary-complex.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
phase: XX-name
|
||||
plan: YY
|
||||
subsystem: [primary category]
|
||||
tags: [searchable tech]
|
||||
requires:
|
||||
- phase: [prior phase]
|
||||
provides: [what that phase built]
|
||||
provides:
|
||||
- [bullet list of what was built/delivered]
|
||||
affects: [list of phase names or keywords]
|
||||
tech-stack:
|
||||
added: [libraries/tools]
|
||||
patterns: [architectural/code patterns]
|
||||
key-files:
|
||||
created: [important files created]
|
||||
modified: [important files modified]
|
||||
key-decisions:
|
||||
- "Decision 1"
|
||||
patterns-established:
|
||||
- "Pattern 1: description"
|
||||
duration: Xmin
|
||||
completed: YYYY-MM-DD
|
||||
---
|
||||
|
||||
# Phase [X]: [Name] Summary (Complex)
|
||||
|
||||
**[Substantive one-liner describing outcome]**
|
||||
|
||||
## Performance
|
||||
- **Duration:** [time]
|
||||
- **Tasks:** [count completed]
|
||||
- **Files modified:** [count]
|
||||
|
||||
## Accomplishments
|
||||
- [Key outcome 1]
|
||||
- [Key outcome 2]
|
||||
|
||||
## Task Commits
|
||||
1. **Task 1: [task name]** - `hash`
|
||||
2. **Task 2: [task name]** - `hash`
|
||||
3. **Task 3: [task name]** - `hash`
|
||||
|
||||
## Files Created/Modified
|
||||
- `path/to/file.ts` - What it does
|
||||
- `path/to/another.ts` - What it does
|
||||
|
||||
## Decisions Made
|
||||
[Key decisions with brief rationale]
|
||||
|
||||
## Deviations from Plan (Auto-fixed)
|
||||
[Detailed auto-fix records per GSD deviation rules]
|
||||
|
||||
## Issues Encountered
|
||||
[Problems during planned work and resolutions]
|
||||
|
||||
## Next Phase Readiness
|
||||
[What's ready for next phase]
|
||||
[Blockers or concerns]
|
||||
41
get-shit-done/templates/summary-minimal.md
Normal file
41
get-shit-done/templates/summary-minimal.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
phase: XX-name
|
||||
plan: YY
|
||||
subsystem: [primary category]
|
||||
tags: [searchable tech]
|
||||
provides:
|
||||
- [bullet list of what was built/delivered]
|
||||
affects: [list of phase names or keywords]
|
||||
tech-stack:
|
||||
added: [libraries/tools]
|
||||
patterns: [architectural/code patterns]
|
||||
key-files:
|
||||
created: [important files created]
|
||||
modified: [important files modified]
|
||||
key-decisions: []
|
||||
duration: Xmin
|
||||
completed: YYYY-MM-DD
|
||||
---
|
||||
|
||||
# Phase [X]: [Name] Summary (Minimal)
|
||||
|
||||
**[Substantive one-liner describing outcome]**
|
||||
|
||||
## Performance
|
||||
- **Duration:** [time]
|
||||
- **Tasks:** [count]
|
||||
- **Files modified:** [count]
|
||||
|
||||
## Accomplishments
|
||||
- [Most important outcome]
|
||||
- [Second key accomplishment]
|
||||
|
||||
## Task Commits
|
||||
1. **Task 1: [task name]** - `hash`
|
||||
2. **Task 2: [task name]** - `hash`
|
||||
|
||||
## Files Created/Modified
|
||||
- `path/to/file.ts` - What it does
|
||||
|
||||
## Next Phase Readiness
|
||||
[Ready for next phase]
|
||||
48
get-shit-done/templates/summary-standard.md
Normal file
48
get-shit-done/templates/summary-standard.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
phase: XX-name
|
||||
plan: YY
|
||||
subsystem: [primary category]
|
||||
tags: [searchable tech]
|
||||
provides:
|
||||
- [bullet list of what was built/delivered]
|
||||
affects: [list of phase names or keywords]
|
||||
tech-stack:
|
||||
added: [libraries/tools]
|
||||
patterns: [architectural/code patterns]
|
||||
key-files:
|
||||
created: [important files created]
|
||||
modified: [important files modified]
|
||||
key-decisions:
|
||||
- "Decision 1"
|
||||
duration: Xmin
|
||||
completed: YYYY-MM-DD
|
||||
---
|
||||
|
||||
# Phase [X]: [Name] Summary
|
||||
|
||||
**[Substantive one-liner describing outcome]**
|
||||
|
||||
## Performance
|
||||
- **Duration:** [time]
|
||||
- **Tasks:** [count completed]
|
||||
- **Files modified:** [count]
|
||||
|
||||
## Accomplishments
|
||||
- [Key outcome 1]
|
||||
- [Key outcome 2]
|
||||
|
||||
## Task Commits
|
||||
1. **Task 1: [task name]** - `hash`
|
||||
2. **Task 2: [task name]** - `hash`
|
||||
3. **Task 3: [task name]** - `hash`
|
||||
|
||||
## Files Created/Modified
|
||||
- `path/to/file.ts` - What it does
|
||||
- `path/to/another.ts` - What it does
|
||||
|
||||
## Decisions & Deviations
|
||||
[Key decisions or "None - followed plan as specified"]
|
||||
[Minor deviations if any, or "None"]
|
||||
|
||||
## Next Phase Readiness
|
||||
[What's ready for next phase]
|
||||
@@ -24,8 +24,8 @@ CHECKER_MODEL=$(node ~/.claude/get-shit-done/bin/gsd-tools.js resolve-model gsd-
|
||||
## 1. Determine Milestone Scope
|
||||
|
||||
```bash
|
||||
# Get phases in milestone
|
||||
ls -d .planning/phases/*/ | sort -V
|
||||
# Get phases in milestone (sorted numerically, handles decimals)
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js phases list
|
||||
```
|
||||
|
||||
- Parse version from arguments or detect current from ROADMAP.md
|
||||
|
||||
@@ -77,20 +77,26 @@ Verify that the target phase exists in the roadmap:
|
||||
</step>
|
||||
|
||||
<step name="find_existing_decimals">
|
||||
Find existing decimal phases after the target phase:
|
||||
Calculate next decimal phase number:
|
||||
|
||||
1. Search for all "### Phase {after_phase}.N:" headings
|
||||
2. Extract decimal suffixes (e.g., for Phase 72: find 72.1, 72.2, 72.3)
|
||||
3. Find the highest decimal suffix
|
||||
4. Calculate next decimal: max + 1
|
||||
```bash
|
||||
DECIMAL_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phase next-decimal "${after_phase}")
|
||||
```
|
||||
|
||||
Extract from JSON:
|
||||
- `next`: The next available decimal (e.g., "06.1", "06.3")
|
||||
- `existing`: Array of existing decimals (e.g., ["06.1", "06.2"])
|
||||
- `base_phase`: Normalized base phase (e.g., "06")
|
||||
|
||||
Store the result:
|
||||
```bash
|
||||
decimal_phase=$(echo "$DECIMAL_INFO" | jq -r '.next')
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- Phase 72 with no decimals -> next is 72.1
|
||||
- Phase 72 with 72.1 -> next is 72.2
|
||||
- Phase 72 with 72.1, 72.2 -> next is 72.3
|
||||
|
||||
Store as: `decimal_phase="$(printf "%02d" $after_phase).${next_decimal}"`
|
||||
</step>
|
||||
|
||||
<step name="generate_slug">
|
||||
|
||||
@@ -64,7 +64,9 @@ Gap: Flow "View dashboard" broken at data fetch
|
||||
|
||||
Find highest existing phase:
|
||||
```bash
|
||||
ls -d .planning/phases/*/ | sort -V | tail -1
|
||||
# Get sorted phase list, extract last one
|
||||
PHASES=$(node ~/.claude/get-shit-done/bin/gsd-tools.js phases list)
|
||||
HIGHEST=$(echo "$PHASES" | jq -r '.directories[-1]')
|
||||
```
|
||||
|
||||
New phases continue from there:
|
||||
|
||||
@@ -38,10 +38,10 @@ mkdir -p ".planning/phases/${padded_phase}-${phase_slug}"
|
||||
## 3. Validate Phase
|
||||
|
||||
```bash
|
||||
grep -A5 "Phase ${PHASE}:" .planning/ROADMAP.md 2>/dev/null
|
||||
PHASE_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}")
|
||||
```
|
||||
|
||||
**If not found:** Error with available phases. **If found:** Extract phase number, name, description.
|
||||
**If `found` is false:** Error with available phases. **If `found` is true:** Extract `phase_number`, `phase_name`, `goal` from JSON.
|
||||
|
||||
## 4. Load CONTEXT.md
|
||||
|
||||
@@ -73,7 +73,7 @@ Display banner:
|
||||
### Spawn gsd-phase-researcher
|
||||
|
||||
```bash
|
||||
PHASE_DESC=$(grep -A3 "Phase ${PHASE}:" .planning/ROADMAP.md)
|
||||
PHASE_DESC=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}" | jq -r '.section')
|
||||
REQUIREMENTS=$(cat .planning/REQUIREMENTS.md 2>/dev/null | grep -A100 "## Requirements" | head -50)
|
||||
DECISIONS=$(grep -A20 "### Decisions Made" .planning/STATE.md 2>/dev/null)
|
||||
```
|
||||
|
||||
@@ -18,10 +18,10 @@ Resolve model for:
|
||||
@~/.claude/get-shit-done/references/phase-argument-parsing.md
|
||||
|
||||
```bash
|
||||
grep -A5 "Phase ${PHASE}:" .planning/ROADMAP.md 2>/dev/null
|
||||
PHASE_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}")
|
||||
```
|
||||
|
||||
If not found: Error and exit.
|
||||
If `found` is false: Error and exit.
|
||||
|
||||
## Step 2: Check Existing Research
|
||||
|
||||
@@ -34,7 +34,8 @@ If exists: Offer update/view/skip options.
|
||||
## Step 3: Gather Phase Context
|
||||
|
||||
```bash
|
||||
grep -A20 "Phase ${PHASE}:" .planning/ROADMAP.md
|
||||
# Phase section from roadmap (already loaded in PHASE_INFO)
|
||||
echo "$PHASE_INFO" | jq -r '.section'
|
||||
cat .planning/REQUIREMENTS.md 2>/dev/null
|
||||
cat .planning/phases/${PHASE}-*/*-CONTEXT.md 2>/dev/null
|
||||
grep -A30 "### Decisions Made" .planning/STATE.md 2>/dev/null
|
||||
|
||||
@@ -35,7 +35,7 @@ Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`, `has_plans`,
|
||||
|
||||
Then load phase details and list plans/summaries:
|
||||
```bash
|
||||
grep -A 5 "Phase ${phase_number}" .planning/ROADMAP.md
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${phase_number}"
|
||||
grep -E "^| ${phase_number}" .planning/REQUIREMENTS.md 2>/dev/null
|
||||
ls "$phase_dir"/*-SUMMARY.md "$phase_dir"/*-PLAN.md 2>/dev/null
|
||||
```
|
||||
|
||||
245
optimisation-ideas/ACTION-PLAN.md
Normal file
245
optimisation-ideas/ACTION-PLAN.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# GSD Context Optimization: Action Plan
|
||||
|
||||
## Consensus Across All Syntheses
|
||||
|
||||
All three proposals agree on the first two phases. Divergence begins at Phase 3 (how far to push deterministic orchestration). This plan executes the consensus first, then evaluates before committing to architectural changes.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Quick Wins (Do First)
|
||||
**Why first:** Highest ROI, lowest risk, validates the approach before bigger changes.
|
||||
|
||||
### 1.1 History Digest
|
||||
**What:** Add `gsd-tools history-digest` command that extracts frontmatter from SUMMARY.md files into a structured JSON.
|
||||
|
||||
**Why:** 10 summaries = 500-1000 lines → 2KB JSON. 80-90% reduction in history context.
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// bin/gsd-tools.js
|
||||
case 'history-digest':
|
||||
const summaries = glob.sync('.planning/phases/*/*-SUMMARY.md');
|
||||
const digest = { phases: {}, decisions: [], tech_stack: new Set() };
|
||||
|
||||
for (const file of summaries) {
|
||||
const frontmatter = extractFrontmatter(file);
|
||||
// Extract: provides, patterns, affects, decisions, tech-stack
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(digest, null, 2));
|
||||
```
|
||||
|
||||
**Acceptance:** Planner uses digest instead of reading full summaries.
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Atomic State Operations
|
||||
**What:** Add `gsd-tools state get <section>` and `gsd-tools state patch --key value`.
|
||||
|
||||
**Why:** Agents currently hold full STATE.md (50-100 lines) throughout execution. Patch operations read/write only what's needed.
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
case 'state':
|
||||
if (args[1] === 'get') {
|
||||
// Return specific section as JSON
|
||||
}
|
||||
if (args[1] === 'patch') {
|
||||
// Parse flags, apply atomic updates
|
||||
}
|
||||
```
|
||||
|
||||
**Acceptance:** Executor uses `state patch` instead of rewriting full file.
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Summary Template Variants
|
||||
**What:** Create three templates: `summary-minimal.md` (~30 lines), `summary-standard.md` (~60 lines), `summary-complex.md` (~100 lines).
|
||||
|
||||
**Why:** A 2-task config change doesn't need a 114-line summary template.
|
||||
|
||||
**Selection logic:**
|
||||
- Minimal: ≤2 tasks, ≤3 files, no decisions
|
||||
- Complex: Has decisions OR >6 files
|
||||
- Standard: Everything else
|
||||
|
||||
**Acceptance:** Executor selects template based on plan metadata.
|
||||
|
||||
---
|
||||
|
||||
### 1.4 Finish Compound Init Sweep
|
||||
**What:** Ensure all major workflows use compound init calls.
|
||||
|
||||
**Why:** Already proven in v1.12.x. Consolidates 5-10 atomic calls into single payload.
|
||||
|
||||
**Targets:** `new-project.md`, `complete-milestone.md`, `verify-work.md`, `transition.md`, `help.md`, `discuss-phase.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Tiered Agent Architecture (Do Second)
|
||||
**Why second:** Addresses the biggest context hogs (planner: 55KB, executor: 19KB) but requires more careful refactoring.
|
||||
|
||||
### 2.1 Split Executor
|
||||
**What:** Extract protocols into separate reference files, load conditionally.
|
||||
|
||||
**Before:**
|
||||
```
|
||||
agents/gsd-executor.md (382 lines, ~19KB)
|
||||
- deviation_rules (60 lines) - always loaded
|
||||
- checkpoint_protocol (40 lines) - always loaded
|
||||
- tdd_execution (30 lines) - always loaded
|
||||
- continuation_handling (20 lines) - always loaded
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
agents/gsd-executor-core.md (~150 lines)
|
||||
|
||||
references/executor/
|
||||
deviation-rules.md # Always loaded (core identity)
|
||||
tdd-execution.md # Loaded if task.tdd="true"
|
||||
checkpoint-protocol.md # Loaded if task.type="checkpoint:*"
|
||||
continuation.md # Loaded if <completed_tasks> present
|
||||
```
|
||||
|
||||
**Loading logic:** Executor uses `@` references conditionally based on task metadata.
|
||||
|
||||
**Acceptance:** Executor base context drops from ~19KB to ~8KB.
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Split Planner
|
||||
**What:** Extract mode-specific logic into extensions.
|
||||
|
||||
**Before:**
|
||||
```
|
||||
agents/gsd-planner.md (1,116 lines, ~55KB)
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
agents/gsd-planner-core.md (~300 lines)
|
||||
- role, philosophy, context_fidelity
|
||||
- task_breakdown (core)
|
||||
- plan_format
|
||||
- execution_flow (standard path)
|
||||
|
||||
agents/gsd-planner-ext/
|
||||
gap-closure.md # Loaded if --gaps flag
|
||||
revision.md # Loaded if checker issues
|
||||
tdd.md # Loaded if TDD candidates detected
|
||||
checkpoints.md # Loaded if phase has checkpoints
|
||||
```
|
||||
|
||||
**Orchestrator assembles prompt dynamically based on flags and phase characteristics.**
|
||||
|
||||
**Acceptance:** Planner base context drops from ~55KB to ~20KB.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Compiled Plans (Do Third)
|
||||
**Why third:** Depends on stable tiered agents. Provides incremental gains on top of Phase 2.
|
||||
|
||||
### 3.1 Implement compile-plan
|
||||
**What:** Add `gsd-tools compile-plan <path>` that:
|
||||
1. Inlines all `@` references
|
||||
2. Strips sections unused by this plan (no TDD tasks → remove TDD sections)
|
||||
3. Outputs `.compiled.md` ready for execution
|
||||
|
||||
**Why:** Eliminates runtime `@` resolution. Pre-strips unused protocols.
|
||||
|
||||
**Staleness handling:**
|
||||
- Check mtime before execution
|
||||
- `.compiled.md` in .gitignore
|
||||
- Recompile on GSD update
|
||||
|
||||
**Acceptance:** Plans execute from compiled form, ~30-40% context reduction.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Evaluate Railroad (Decide Later)
|
||||
**Why later:** High effort, architectural risk. Phases 1-3 may provide sufficient improvement.
|
||||
|
||||
**The question:** Should `gsd-tools` become a state machine that tells Claude "do this next" instead of Claude reading workflow files?
|
||||
|
||||
**Arguments for:**
|
||||
- Maximum context reduction
|
||||
- Deterministic execution (can't skip steps)
|
||||
- LLM focuses purely on task, not process
|
||||
|
||||
**Arguments against:**
|
||||
- Loses adaptive intelligence
|
||||
- Can't handle mid-execution surprises
|
||||
- Debugging split across code and prompts
|
||||
- Near-total rewrite
|
||||
|
||||
**Decision point:** After Phase 3, measure context usage. If still >40% at task start, consider Railroad. If <40%, skip.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Semantic Queries (Optional)
|
||||
**Why optional:** gsd-memory MCP exists. Only invest here if grep-based lookups prove insufficient after other optimizations.
|
||||
|
||||
**What:** Extend gsd-memory with structured queries:
|
||||
```javascript
|
||||
gsd_memory_what_uses({ symbol: "User", type: "model" })
|
||||
gsd_memory_pattern_for({ domain: "auth" })
|
||||
```
|
||||
|
||||
**Decision point:** After Phase 3, evaluate if history/pattern lookups are still a bottleneck.
|
||||
|
||||
---
|
||||
|
||||
## Execution Order Summary
|
||||
|
||||
```
|
||||
IMMEDIATE (Phase 1) - 1-2 days
|
||||
├── 1.1 history-digest command
|
||||
├── 1.2 state get/patch commands
|
||||
├── 1.3 Summary template variants
|
||||
└── 1.4 Compound init sweep completion
|
||||
|
||||
NEXT (Phase 2) - 3-5 days
|
||||
├── 2.1 Split gsd-executor into core + references
|
||||
└── 2.2 Split gsd-planner into core + extensions
|
||||
|
||||
THEN (Phase 3) - 3-5 days
|
||||
└── 3.1 compile-plan command
|
||||
|
||||
EVALUATE (Phase 4) - Decision point
|
||||
└── Measure: Is context <40%?
|
||||
└── Yes → Done
|
||||
└── No → Consider Railroad architecture
|
||||
|
||||
OPTIONAL (Phase 5)
|
||||
└── Semantic MCP queries (only if needed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
| Metric | Current | Phase 1 | Phase 2 | Phase 3 | Target |
|
||||
|--------|---------|---------|---------|---------|--------|
|
||||
| Executor base | ~19KB | ~19KB | ~8KB | ~6KB | <8KB |
|
||||
| Planner base | ~55KB | ~55KB | ~20KB | ~18KB | <20KB |
|
||||
| History (10 phases) | ~20KB | ~2KB | ~2KB | ~2KB | <3KB |
|
||||
| Task start context | ~25% | ~18% | ~12% | ~10% | <15% |
|
||||
|
||||
---
|
||||
|
||||
## What NOT To Do
|
||||
|
||||
1. **Don't implement Railroad before Phase 3 measurement.** The architectural cost is high; validate simpler solutions first.
|
||||
|
||||
2. **Don't compress instructional content.** The teaching content is GSD's value. Optimize *when* it loads, not *what* it says.
|
||||
|
||||
3. **Don't over-engineer caching.** Simple mtime checks work. No cache invalidation logic until proven necessary.
|
||||
|
||||
4. **Don't parallelize phases.** Each phase validates assumptions for the next. Sequential execution reduces risk.
|
||||
|
||||
---
|
||||
|
||||
## Next Action
|
||||
|
||||
Start Phase 1.1: Implement `gsd-tools history-digest`.
|
||||
580
optimisation-ideas/codex.md
Normal file
580
optimisation-ideas/codex.md
Normal file
@@ -0,0 +1,580 @@
|
||||
# GSD Context Optimization Strategy
|
||||
|
||||
> Reducing context load while maintaining (or improving) execution quality.
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Context Budget Reality
|
||||
|
||||
| File Type | Total Size | Files | Loaded When |
|
||||
|-----------|-----------|-------|-------------|
|
||||
| Agents | ~197KB | 11 | Per subagent spawn |
|
||||
| Workflows | ~266KB | 30 | Per command invocation |
|
||||
| References | ~88KB | ~10 | Inlined in agents/workflows |
|
||||
| Templates | ~114KB | ~8 | Per document creation |
|
||||
|
||||
**Heaviest subagents:**
|
||||
- `gsd-planner`: 1,116 lines (~55KB)
|
||||
- `gsd-executor`: 382 lines (~19KB)
|
||||
- `gsd-phase-researcher`: ~400 lines (~20KB)
|
||||
|
||||
**Problem:** A simple 2-task plan execution loads the full 382-line executor prompt, including TDD flows, gap closure protocols, and checkpoint handling it won't use.
|
||||
|
||||
### The Quality Degradation Curve
|
||||
|
||||
| Context Usage | Quality | Behavior |
|
||||
|---------------|---------|----------|
|
||||
| 0-30% | PEAK | Thorough, comprehensive, follows all instructions |
|
||||
| 30-50% | GOOD | Solid work, occasional shortcuts |
|
||||
| 50-70% | DEGRADING | Efficiency mode, skips optional steps |
|
||||
| 70%+ | POOR | Rushed, minimal, misses requirements |
|
||||
|
||||
**Current reality:** Complex phases start agents at 15-25% context just from prompt loading, before any codebase reading. This leaves less headroom for actual work.
|
||||
|
||||
---
|
||||
|
||||
## Optimization Principles
|
||||
|
||||
### 1. Demand Loading Over Eager Loading
|
||||
|
||||
Load what IS needed when it's needed, not what MIGHT be needed upfront.
|
||||
|
||||
**Compiler analogy:**
|
||||
- Dead code elimination → Don't load TDD protocol if no TDD tasks
|
||||
- Lazy evaluation → Load checkpoint protocol when checkpoint encountered
|
||||
- Incremental compilation → Compile plans once, reuse compiled form
|
||||
|
||||
### 2. Separation of Concerns
|
||||
|
||||
Base prompts define WHAT to do. Extension modules define HOW for specific scenarios.
|
||||
|
||||
### 3. Semantic Over Syntactic
|
||||
|
||||
Query for meaning ("what patterns exist for auth?") instead of text ("grep for auth in all summaries").
|
||||
|
||||
---
|
||||
|
||||
## Optimization Opportunities
|
||||
|
||||
### O1: Lazy-Load Reference Sections
|
||||
|
||||
**Priority:** 1 (High impact, medium effort)
|
||||
|
||||
**Current state:** Agents inline all protocols regardless of need.
|
||||
|
||||
```markdown
|
||||
# gsd-executor.md currently has:
|
||||
<deviation_rules> # 60 lines - always loaded
|
||||
<checkpoint_protocol> # 40 lines - always loaded
|
||||
<tdd_execution> # 30 lines - always loaded
|
||||
<continuation_handling> # 20 lines - always loaded
|
||||
```
|
||||
|
||||
**Proposed state:** Base agent with conditional loading.
|
||||
|
||||
```markdown
|
||||
# gsd-executor-core.md (~150 lines)
|
||||
<role>...</role>
|
||||
<execution_flow>
|
||||
<step name="execute_tasks">
|
||||
For each task:
|
||||
1. If `tdd="true"`: @~/.claude/get-shit-done/references/tdd-execution.md
|
||||
2. If `type="checkpoint:*"`: @~/.claude/get-shit-done/references/checkpoint-protocol.md
|
||||
3. Execute task...
|
||||
</step>
|
||||
</execution_flow>
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```
|
||||
agents/
|
||||
gsd-executor.md → gsd-executor-core.md (base, ~150 lines)
|
||||
|
||||
references/
|
||||
executor/
|
||||
deviation-rules.md # Loaded always (core to executor identity)
|
||||
tdd-execution.md # Loaded if task.tdd="true"
|
||||
checkpoint-protocol.md # Loaded if task.type starts with "checkpoint:"
|
||||
continuation.md # Loaded if <completed_tasks> in prompt
|
||||
summary-creation.md # Loaded at plan completion
|
||||
```
|
||||
|
||||
**Savings:** 40-50% reduction in executor base context. TDD, checkpoint, continuation references load only when task type requires them.
|
||||
|
||||
**Risk:** @ resolution adds file I/O. Mitigated by keeping references small (<50 lines each).
|
||||
|
||||
---
|
||||
|
||||
### O2: Tiered Agent Prompts
|
||||
|
||||
**Priority:** 2 (High impact, medium effort)
|
||||
|
||||
**Current state:** Planner has 1,116 lines covering all modes.
|
||||
|
||||
```markdown
|
||||
# gsd-planner.md sections:
|
||||
<context_fidelity> # 55 lines
|
||||
<philosophy> # 40 lines
|
||||
<discovery_levels> # 50 lines
|
||||
<task_breakdown> # 120 lines
|
||||
<dependency_graph> # 80 lines
|
||||
<scope_estimation> # 60 lines
|
||||
<plan_format> # 110 lines
|
||||
<goal_backward> # 100 lines
|
||||
<checkpoints> # 90 lines
|
||||
<tdd_integration> # 50 lines
|
||||
<gap_closure_mode> # 80 lines
|
||||
<revision_mode> # 100 lines
|
||||
<execution_flow> # 200 lines
|
||||
<structured_returns> # 60 lines
|
||||
```
|
||||
|
||||
**Proposed state:** Base + extensions.
|
||||
|
||||
```
|
||||
agents/
|
||||
gsd-planner-core.md # ~300 lines (always loaded)
|
||||
- role, philosophy, context_fidelity
|
||||
- task_breakdown (core)
|
||||
- plan_format
|
||||
- execution_flow (standard path)
|
||||
- structured_returns
|
||||
|
||||
gsd-planner-ext/
|
||||
discovery.md # ~50 lines (if new dependencies detected)
|
||||
goal-backward.md # ~100 lines (always for now, core methodology)
|
||||
gap-closure.md # ~80 lines (if --gaps flag)
|
||||
revision.md # ~100 lines (if checker issues provided)
|
||||
tdd.md # ~50 lines (if TDD candidates detected)
|
||||
checkpoints.md # ~90 lines (if checkpoints in phase)
|
||||
```
|
||||
|
||||
**Orchestrator logic:**
|
||||
|
||||
```bash
|
||||
PLANNER_PROMPT="@~/.claude/agents/gsd-planner-core.md"
|
||||
|
||||
if [[ "$FLAGS" == *"--gaps"* ]]; then
|
||||
PLANNER_PROMPT+="\n@~/.claude/agents/gsd-planner-ext/gap-closure.md"
|
||||
fi
|
||||
|
||||
if [[ -n "$CHECKER_ISSUES" ]]; then
|
||||
PLANNER_PROMPT+="\n@~/.claude/agents/gsd-planner-ext/revision.md"
|
||||
fi
|
||||
|
||||
# Detect TDD candidates from phase description
|
||||
if echo "$PHASE_DESC" | grep -qiE "business logic|validation|algorithm|transform"; then
|
||||
PLANNER_PROMPT+="\n@~/.claude/agents/gsd-planner-ext/tdd.md"
|
||||
fi
|
||||
```
|
||||
|
||||
**Savings:** ~60% reduction in typical planning context. Gap closure and revision modes loaded only when triggered.
|
||||
|
||||
---
|
||||
|
||||
### O3: Frontmatter-Only History Digest
|
||||
|
||||
**Priority:** 3 (Medium impact, low effort)
|
||||
|
||||
**Current state:** Planner reads full SUMMARY.md files to understand project history.
|
||||
|
||||
```bash
|
||||
# gsd-planner step: read_project_history
|
||||
for f in .planning/phases/*/*-SUMMARY.md; do
|
||||
cat "$f" # Full file, 50-100 lines each
|
||||
done
|
||||
```
|
||||
|
||||
**Problem:** 10 prior summaries = 500-1000 lines of context. Most is prose, deviation docs, commit lists — not needed for dependency analysis.
|
||||
|
||||
**Proposed state:** gsd-tools generates digest.
|
||||
|
||||
```bash
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js history-digest
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```json
|
||||
{
|
||||
"phases": {
|
||||
"01-setup": {
|
||||
"plans": ["01-01", "01-02"],
|
||||
"provides": ["Next.js app", "Prisma schema", "Auth utils"],
|
||||
"patterns": ["App Router", "Server Actions", "jose for JWT"],
|
||||
"affects": ["api", "components", "lib"]
|
||||
},
|
||||
"02-core": {
|
||||
"plans": ["02-01", "02-02", "02-03"],
|
||||
"provides": ["User CRUD", "Project CRUD", "Dashboard"],
|
||||
"patterns": ["Zod validation", "React Query"],
|
||||
"affects": ["api", "components", "hooks"]
|
||||
}
|
||||
},
|
||||
"decisions": [
|
||||
{"phase": "01-01", "decision": "Use jose over jsonwebtoken for Edge compatibility"},
|
||||
{"phase": "02-01", "decision": "Server components for data fetching, client for interactivity"}
|
||||
],
|
||||
"tech_stack": ["next.js", "prisma", "tailwind", "jose", "zod", "react-query"]
|
||||
}
|
||||
```
|
||||
|
||||
**Planner loads:**
|
||||
- 2KB digest instead of 20KB of full summaries
|
||||
- Full summary only for directly-dependent phases (from `affects` field)
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```javascript
|
||||
// bin/gsd-tools.js
|
||||
case 'history-digest':
|
||||
const summaries = glob.sync('.planning/phases/*/*-SUMMARY.md');
|
||||
const digest = { phases: {}, decisions: [], tech_stack: new Set() };
|
||||
|
||||
for (const file of summaries) {
|
||||
const frontmatter = extractFrontmatter(file);
|
||||
const phase = frontmatter.phase;
|
||||
|
||||
digest.phases[phase] = digest.phases[phase] || { plans: [], provides: [], patterns: [], affects: [] };
|
||||
digest.phases[phase].plans.push(frontmatter.plan);
|
||||
digest.phases[phase].provides.push(...(frontmatter['dependency-graph']?.provides || []));
|
||||
digest.phases[phase].patterns.push(...(frontmatter['patterns-established'] || []));
|
||||
digest.phases[phase].affects.push(...(frontmatter['dependency-graph']?.affects || []));
|
||||
|
||||
if (frontmatter['key-decisions']) {
|
||||
digest.decisions.push(...frontmatter['key-decisions'].map(d => ({ phase, decision: d })));
|
||||
}
|
||||
|
||||
(frontmatter['tech-stack']?.added || []).forEach(t => digest.tech_stack.add(t.name));
|
||||
}
|
||||
|
||||
digest.tech_stack = [...digest.tech_stack];
|
||||
console.log(JSON.stringify(digest, null, 2));
|
||||
break;
|
||||
```
|
||||
|
||||
**Savings:** 80-90% reduction in history context for complex projects.
|
||||
|
||||
---
|
||||
|
||||
### O4: Compiled Plans
|
||||
|
||||
**Priority:** 4 (Medium impact, medium effort)
|
||||
|
||||
**Current state:** Every plan has `@` references resolved at runtime.
|
||||
|
||||
```markdown
|
||||
# 03-01-PLAN.md
|
||||
<execution_context>
|
||||
@~/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@~/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/STATE.md
|
||||
</context>
|
||||
```
|
||||
|
||||
**Problem:** Every executor agent re-resolves the same references. execute-plan.md alone is 354 lines.
|
||||
|
||||
**Proposed state:** Pre-compile plans before execution.
|
||||
|
||||
```bash
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js compile-plan .planning/phases/03-auth/03-01-PLAN.md
|
||||
# Produces .planning/phases/03-auth/03-01-PLAN.compiled.md
|
||||
```
|
||||
|
||||
**Compilation steps:**
|
||||
|
||||
1. Inline all `@` references
|
||||
2. Strip sections irrelevant to this plan's tasks:
|
||||
- No TDD tasks? Remove TDD sections
|
||||
- No checkpoints? Remove checkpoint sections
|
||||
- No external services? Remove user_setup handling
|
||||
3. Minify prose (optional): Remove example sections, reduce explanatory text
|
||||
4. Output ready-to-execute prompt
|
||||
|
||||
**Executor spawning:**
|
||||
|
||||
```bash
|
||||
# Before: Agent resolves references
|
||||
PLAN_CONTENT=$(cat "$PLAN_PATH")
|
||||
|
||||
# After: Pre-compiled, ready to execute
|
||||
if [[ -f "${PLAN_PATH%.md}.compiled.md" ]]; then
|
||||
PLAN_CONTENT=$(cat "${PLAN_PATH%.md}.compiled.md")
|
||||
else
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js compile-plan "$PLAN_PATH"
|
||||
PLAN_CONTENT=$(cat "${PLAN_PATH%.md}.compiled.md")
|
||||
fi
|
||||
```
|
||||
|
||||
**Savings:** Eliminates file I/O and @-resolution overhead per agent. Pre-strips unused protocols. Estimate: 30-40% reduction in executor context for simple plans.
|
||||
|
||||
**Trade-off:** Compiled plans become stale if source references change. Mitigated by:
|
||||
- Compile on-demand (check mtime)
|
||||
- `.compiled.md` files in .gitignore
|
||||
- Recompile on GSD update
|
||||
|
||||
---
|
||||
|
||||
### O5: Atomic State Operations
|
||||
|
||||
**Priority:** 5 (Medium impact, low effort)
|
||||
|
||||
**Current state:** Agents read full STATE.md, hold in context, modify at end.
|
||||
|
||||
```bash
|
||||
# Start of execution
|
||||
STATE=$(cat .planning/STATE.md) # ~50-100 lines in context
|
||||
|
||||
# ... 50% context later ...
|
||||
|
||||
# End of execution
|
||||
# Modify STATE in memory, write back
|
||||
```
|
||||
|
||||
**Problem:**
|
||||
- Full STATE.md in context throughout execution
|
||||
- Risk of stale reads if parallel agents update
|
||||
- Merge conflicts on concurrent writes
|
||||
|
||||
**Proposed state:** Patch-based operations.
|
||||
|
||||
```bash
|
||||
# Read only what's needed
|
||||
POSITION=$(node ~/.claude/get-shit-done/bin/gsd-tools.js state get position)
|
||||
DECISIONS=$(node ~/.claude/get-shit-done/bin/gsd-tools.js state get decisions)
|
||||
|
||||
# Atomic updates
|
||||
node ~/.claude/get-shit-done/bin/gsd-tools.js state patch \
|
||||
--position "Phase: 03, Plan: 02, Status: Complete" \
|
||||
--add-decision "Used jose for JWT per Edge runtime constraints" \
|
||||
--set-session "Last: 2024-01-15, Stopped: 03-02, Resume: 03-03-PLAN.md"
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```javascript
|
||||
// bin/gsd-tools.js
|
||||
case 'state':
|
||||
const subcommand = args[1]; // 'get' or 'patch'
|
||||
const statePath = '.planning/STATE.md';
|
||||
|
||||
if (subcommand === 'get') {
|
||||
const section = args[2]; // 'position', 'decisions', 'session', etc.
|
||||
const content = fs.readFileSync(statePath, 'utf8');
|
||||
const parsed = parseStateSection(content, section);
|
||||
console.log(JSON.stringify(parsed));
|
||||
}
|
||||
|
||||
if (subcommand === 'patch') {
|
||||
// Parse --position, --add-decision, --set-session flags
|
||||
// Read file, apply patches, write back atomically
|
||||
// Use file locking for concurrent safety
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
**Savings:** Agents don't hold full STATE.md in context. Reduces context by ~1-2KB per agent. Prevents concurrent update conflicts.
|
||||
|
||||
---
|
||||
|
||||
### O6: Smart Summary Templates
|
||||
|
||||
**Priority:** 6 (Low impact, low effort)
|
||||
|
||||
**Current state:** One summary template for all plan types.
|
||||
|
||||
```markdown
|
||||
# summary.md template - 114 lines
|
||||
# Includes: frontmatter, title, overview, task details, deviations,
|
||||
# auth gates, verification, self-check, key files, next steps
|
||||
```
|
||||
|
||||
**Problem:** A simple config change produces the same verbose summary as a complex auth implementation.
|
||||
|
||||
**Proposed state:** Template variants.
|
||||
|
||||
```
|
||||
templates/
|
||||
summary-minimal.md # ~30 lines - config, simple CRUD
|
||||
summary-standard.md # ~60 lines - typical features
|
||||
summary-complex.md # ~100 lines - architectural changes, decisions
|
||||
```
|
||||
|
||||
**Selection heuristic:**
|
||||
|
||||
```javascript
|
||||
function selectSummaryTemplate(plan) {
|
||||
const taskCount = plan.tasks.length;
|
||||
const hasDecisions = plan.tasks.some(t => t.type.includes('decision'));
|
||||
const hasDeviations = plan.deviations?.length > 0;
|
||||
const fileCount = plan.files_modified?.length || 0;
|
||||
|
||||
if (taskCount <= 2 && fileCount <= 3 && !hasDecisions) {
|
||||
return 'summary-minimal.md';
|
||||
}
|
||||
if (hasDecisions || hasDeviations || fileCount > 6) {
|
||||
return 'summary-complex.md';
|
||||
}
|
||||
return 'summary-standard.md';
|
||||
}
|
||||
```
|
||||
|
||||
**Savings:** Reduces summary creation context by 30-60% for simple plans. Executor doesn't load 114-line template for a 2-task config change.
|
||||
|
||||
---
|
||||
|
||||
### O7: MCP Semantic Queries
|
||||
|
||||
**Priority:** 7 (Medium impact, high effort)
|
||||
|
||||
**Current state:** Context loading via grep and file reads.
|
||||
|
||||
```bash
|
||||
# Find what uses User model
|
||||
grep -r "User" .planning/phases/*/*-SUMMARY.md
|
||||
|
||||
# Find auth patterns
|
||||
grep -r "auth\|jwt\|session" .planning/phases/*/*-SUMMARY.md
|
||||
```
|
||||
|
||||
**Problem:** Text matching returns noise. "User" matches "user experience", "user-facing", etc.
|
||||
|
||||
**Proposed state:** Extend gsd-memory MCP with semantic queries.
|
||||
|
||||
```javascript
|
||||
// New MCP tools
|
||||
gsd_memory_what_uses({ symbol: "User", type: "model" })
|
||||
// Returns: ["03-01-SUMMARY.md", "04-02-SUMMARY.md"] with context
|
||||
|
||||
gsd_memory_pattern_for({ domain: "auth" })
|
||||
// Returns: { library: "jose", approach: "httpOnly cookies", refresh: "7d rotation" }
|
||||
|
||||
gsd_memory_decisions_affecting({ subsystem: "api" })
|
||||
// Returns: [{ phase: "01-02", decision: "Server Actions over API routes" }]
|
||||
```
|
||||
|
||||
**Implementation approach:**
|
||||
|
||||
1. Index summaries on write (post-commit hook or gsd-tools trigger)
|
||||
2. Store in SQLite with FTS5 for text search + JSON fields for structured data
|
||||
3. Expose via MCP tools
|
||||
4. Agents query instead of grep
|
||||
|
||||
**Savings:** Precise context retrieval. Agent gets exactly what it needs, no noise. Estimate: 50% reduction in history-loading context for large projects.
|
||||
|
||||
**Trade-off:** Significant implementation effort. Index maintenance. SQLite dependency.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Phase 1: Quick Wins (1-2 days)
|
||||
|
||||
1. **O3: Frontmatter digest** - Add `gsd-tools history-digest`
|
||||
2. **O5: State patch operations** - Add `gsd-tools state get/patch`
|
||||
3. **O6: Summary template variants** - Create minimal/standard/complex
|
||||
|
||||
### Phase 2: Architecture Changes (3-5 days)
|
||||
|
||||
4. **O1: Lazy-load references** - Restructure executor into core + references
|
||||
5. **O2: Tiered agent prompts** - Restructure planner into core + extensions
|
||||
|
||||
### Phase 3: Build System (3-5 days)
|
||||
|
||||
6. **O4: Compiled plans** - Add `gsd-tools compile-plan` with smart stripping
|
||||
|
||||
### Phase 4: Semantic Layer (5-10 days)
|
||||
|
||||
7. **O7: MCP semantic queries** - Extend gsd-memory with structured queries
|
||||
|
||||
---
|
||||
|
||||
## Metrics
|
||||
|
||||
### Before/After Tracking
|
||||
|
||||
| Metric | Current | Target | How to Measure |
|
||||
|--------|---------|--------|----------------|
|
||||
| Executor base context | ~19KB | ~8KB | `wc -c agents/gsd-executor*.md` |
|
||||
| Planner base context | ~55KB | ~20KB | `wc -c agents/gsd-planner*.md` |
|
||||
| History loading (10 phases) | ~20KB | ~2KB | Digest size vs full summaries |
|
||||
| Plan execution start context | ~25% | ~12% | Log context % at first task |
|
||||
|
||||
### Quality Indicators
|
||||
|
||||
- Plans completing within 50% context (target: 95%+)
|
||||
- Verification pass rate (should stay same or improve)
|
||||
- Deviation rate (should stay same)
|
||||
- User checkpoint fatigue (fewer "skip" responses)
|
||||
|
||||
---
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Lazy loading adds latency | Keep references <50 lines, cache resolved content |
|
||||
| Tiered prompts miss edge cases | Comprehensive testing, fallback to full prompt |
|
||||
| Compiled plans become stale | Mtime checks, recompile on source change |
|
||||
| Semantic queries require maintenance | Auto-index on summary creation, periodic reindex |
|
||||
| Breaking changes during refactor | Feature flags, A/B testing old vs new paths |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. **Context efficiency:** Average plan execution uses <40% context (down from ~55%)
|
||||
2. **Quality maintenance:** Verification pass rate stays ≥95%
|
||||
3. **Speed:** No measurable latency increase from lazy loading
|
||||
4. **Maintainability:** Clear separation between core and extensions
|
||||
5. **Debuggability:** Easy to trace which modules were loaded for any execution
|
||||
|
||||
---
|
||||
|
||||
## Appendix: File Structure After Optimization
|
||||
|
||||
```
|
||||
agents/
|
||||
gsd-executor-core.md # Base executor (~150 lines)
|
||||
gsd-planner-core.md # Base planner (~300 lines)
|
||||
gsd-planner-ext/
|
||||
discovery.md
|
||||
gap-closure.md
|
||||
revision.md
|
||||
tdd.md
|
||||
checkpoints.md
|
||||
gsd-verifier.md # Already lean
|
||||
gsd-phase-researcher.md # Candidate for similar treatment
|
||||
...
|
||||
|
||||
references/
|
||||
executor/
|
||||
deviation-rules.md
|
||||
tdd-execution.md
|
||||
checkpoint-protocol.md
|
||||
continuation.md
|
||||
summary-creation.md
|
||||
planner/
|
||||
goal-backward.md # Core methodology, always loaded
|
||||
checkpoints.md # Existing, used by multiple agents
|
||||
tdd.md # Existing
|
||||
|
||||
templates/
|
||||
summary-minimal.md
|
||||
summary-standard.md
|
||||
summary-complex.md
|
||||
...
|
||||
|
||||
bin/
|
||||
gsd-tools.js
|
||||
# Existing commands
|
||||
+ history-digest # O3
|
||||
+ state get/patch # O5
|
||||
+ compile-plan # O4
|
||||
+ select-template # O6
|
||||
```
|
||||
103
optimisation-ideas/gemini.md
Normal file
103
optimisation-ideas/gemini.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# The "Railroad" Architecture: GSD Framework Evolution
|
||||
|
||||
## Executive Summary
|
||||
This document outlines the strategic evolution of the Get Shit Done (GSD) framework from a "Prompt Engineering" reliance to a **"Software Engineering"** foundation. The core objective is to reduce token consumption ("context tax") and increase reliability by shifting workflow logic from LLM context to deterministic code.
|
||||
|
||||
## 1. The Core Shift: "Railroad" State Machine
|
||||
|
||||
### Current State
|
||||
Currently, the LLM acts as the **Workflow Engine**. It reads a verbose Markdown file (e.g., `new-project.md`) containing logic like:
|
||||
> "If the user says X, then ask Y. If they say Z, execute tool A."
|
||||
|
||||
This relies on "soft state management" where the LLM must perfectly recall and adhere to written instructions.
|
||||
|
||||
### Proposed State ("Railroad")
|
||||
The LLM becomes an **Intelligent Worker**, while `gsd-tools` becomes the **Workflow Engine**.
|
||||
|
||||
* **Logic in Code:** Complex branching logic is moved to `gsd-tools`.
|
||||
* **The "Next Step" Pattern:**
|
||||
Instead of reading a map, the Agent simply asks: "What is next?"
|
||||
|
||||
```bash
|
||||
# Agent calls:
|
||||
node gsd-tools.js next-step --workflow new-project --state-file .planning/state.json
|
||||
```
|
||||
|
||||
**Tool Returns:**
|
||||
```json
|
||||
{
|
||||
"action": "ask_user",
|
||||
"prompt": "What do you want to build?",
|
||||
"context_files": []
|
||||
}
|
||||
```
|
||||
|
||||
* **Benefit:** The Agent needs *zero* knowledge of the overall workflow. It focuses 100% of its attention on executing the single, immediate task. It cannot "skip" steps because it doesn't know they exist until the tool assigns them.
|
||||
|
||||
## 2. Just-in-Time (JIT) Context Loading
|
||||
|
||||
### Current State
|
||||
We load all templates, references, and instructions at the start of a session.
|
||||
* *Example:* `new-project` loads `questioning.md`, `project.md` template, `requirements.md` template, etc.
|
||||
|
||||
### Proposed State
|
||||
Context is injected only when needed for the specific step.
|
||||
|
||||
* **Step 1 (Questioning):** Load `questioning.md`. Do NOT load `requirements.md` template.
|
||||
* **Step 2 (Requirements):** Unload `questioning.md`. Load `requirements.md` template.
|
||||
* **Step 3 (Roadmap):** Load `ROADMAP.md` template.
|
||||
|
||||
**Mechanism:**
|
||||
The `gsd-tools next-step` response includes a `context_files` array. The wrapper script or system prompt dynamically reads these files into the context window for that turn only.
|
||||
|
||||
## 3. Native Interventions (Bypassing the LLM)
|
||||
|
||||
### Current State
|
||||
The LLM acts as a text-based menu system.
|
||||
> "User, please choose: 1. YOLO Mode, 2. Interactive Mode."
|
||||
|
||||
This burns tokens on input tokens (instructions on how to ask), output tokens (asking the question), and input tokens (processing the user's "1").
|
||||
|
||||
### Proposed State
|
||||
Standardize discrete choices into CLI interactions managed by `gsd-tools` or the wrapper.
|
||||
|
||||
* **Configuration Wizard:** `gsd-tools init new-project` runs an interactive CLI wizard (using `inquirer` or `readline`) to gather `mode`, `depth`, `parallelization` preferences *before* the LLM is even invoked.
|
||||
* **Structured Output:** The LLM receives the result as a finalized JSON configuration, not a conversation transcript.
|
||||
|
||||
## 4. Micro-Agents & Chained Execution
|
||||
|
||||
### Current State
|
||||
Monolithic workflows (`new-project`) maintain a single context window that grows indefinitely. By the time the agent reaches "Roadmap Creation" (Step 9), the context is polluted with "Questioning" (Step 3) transcripts.
|
||||
|
||||
### Proposed State
|
||||
Break workflows into atomic, chainable units.
|
||||
|
||||
1. **Process:** `gsd:new-project`
|
||||
2. **Chain:**
|
||||
* **Agent A (Scope):** Interviews user $\rightarrow$ Outputs `PROJECT.md` $\rightarrow$ **Exits.**
|
||||
* *Context Flush*
|
||||
* **Agent B (Research):** Reads `PROJECT.md` $\rightarrow$ Outputs `research/` $\rightarrow$ **Exits.**
|
||||
* *Context Flush*
|
||||
* **Agent C (Roadmap):** Reads `PROJECT.md` + `research/` $\rightarrow$ Outputs `ROADMAP.md`.
|
||||
|
||||
**Implementation:**
|
||||
The `gsd-tools` utility manages the hand-off. When Agent A finishes, it returns a specific exit code or signal. The outer loop detects this, clears context, and spawns Agent B with the specific inputs generated by A.
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
1. **Expand `gsd-tools`:**
|
||||
* Implement `next-step` state machine logic.
|
||||
* Add interactive CLI prompts for configuration.
|
||||
2. **Refactor Workflows:**
|
||||
* Strip `workflows/*.md` to bare essentials (or remove them entirely in favor of code definitions).
|
||||
3. **Update Agent Definitions:**
|
||||
* Direct agents to rely on `gsd-tools` for guidance rather than internal reasoning about the process.
|
||||
|
||||
## Summary of Benefits
|
||||
|
||||
| Metric | Current | Railroad Architecture |
|
||||
| :--- | :--- | :--- |
|
||||
| **Context Usage** | High (Full workflow + History) | Minimal (Current Step + Immediate Inputs) |
|
||||
| **Reliability** | Variable (LLM can hallucinate process) | Deterministic (Code enforces process) |
|
||||
| **Speed** | Slower (Reading/generating verbose text) | Faster (Code execution + concise LLM tasks) |
|
||||
| **Maintenance** | Distributed (Markdown files) | Centralized (JS Logic) |
|
||||
123
optimisation-ideas/syntheses/codex.md
Normal file
123
optimisation-ideas/syntheses/codex.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# GSD Context Optimization Proposal
|
||||
Goal: Reduce unnecessary context load while preserving or improving execution quality and speed.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
GSD is already moving toward leaner orchestration and centralized tooling. The next step is to systematically shift logic out of prompts and into deterministic tooling, while loading only the instructions needed for the current step. This proposal delivers that outcome through a phased approach:
|
||||
|
||||
1. Immediate context diet with fast ROI and minimal risk.
|
||||
2. Prompt modularization to reduce baseline agent size.
|
||||
3. Precompiled execution artifacts to strip unused protocols.
|
||||
4. Railroad (state machine) architecture for long-term determinism.
|
||||
|
||||
This balances speed, quality, and maintainability while protecting GSD's core instructional content.
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
GSD's effectiveness depends on rich instructional content, but its current context load includes large, often unused sections. As context usage grows, quality degrades and speed drops. The system needs a precision-loading strategy: only bring into context what's needed for the next step, and let deterministic tooling enforce the process.
|
||||
|
||||
---
|
||||
|
||||
## Design Principles
|
||||
- Demand-load over eager-load: Load instructions only when needed.
|
||||
- Deterministic orchestration: Move branching logic into code.
|
||||
- Stable core, modular extensions: Keep minimal core prompts and load extensions conditionally.
|
||||
- Measurable quality: Reductions must not degrade verification pass rates.
|
||||
|
||||
---
|
||||
|
||||
## Proposed Strategy
|
||||
|
||||
### 1) Immediate Context Diet (Quick Wins)
|
||||
- History digest: Load structured summaries instead of full history.
|
||||
- State patch ops: Read only the state sections required.
|
||||
- Summary template variants: Choose minimal or standard templates based on complexity.
|
||||
- Compound init sweep: Consolidate context loading into a single structured payload.
|
||||
|
||||
Impact: Faster startup, lower context load, minimal refactor risk.
|
||||
|
||||
---
|
||||
|
||||
### 2) Prompt Modularity (High Impact, Moderate Effort)
|
||||
- Split large agents into core + extensions.
|
||||
- Load extensions only when triggered by plan characteristics or flags.
|
||||
- Add a context budget mode (min|std|full) with escalation when needed.
|
||||
|
||||
Impact: Large baseline prompt reduction without behavior regression.
|
||||
|
||||
---
|
||||
|
||||
### 3) Compiled Plans (Performance + Precision)
|
||||
- Pre-compile plans by resolving references and stripping unused protocols.
|
||||
- Cache compiled outputs; recompile on source change.
|
||||
|
||||
Impact: Significant runtime context reduction and faster execution start.
|
||||
|
||||
---
|
||||
|
||||
### 4) Railroad Architecture (Long-Term Determinism)
|
||||
- gsd-tools becomes a state machine, returning the next action step.
|
||||
- Agents only see current step + immediate context.
|
||||
- Enables JIT context and prevents step skipping.
|
||||
|
||||
Impact: Maximum context efficiency and reliability, higher implementation cost.
|
||||
|
||||
---
|
||||
|
||||
## Phased Roadmap
|
||||
|
||||
### Phase 1: Quick Wins (1-2 days)
|
||||
1. Add history-digest to gsd-tools.
|
||||
2. Add state get/patch operations.
|
||||
3. Add summary template variants.
|
||||
4. Finish compound init sweep.
|
||||
|
||||
### Phase 2: Prompt Modularity (3-5 days)
|
||||
1. Split gsd-executor into core + conditional references.
|
||||
2. Split gsd-planner into core + extensions.
|
||||
3. Add context budget mode.
|
||||
|
||||
### Phase 3: Compiled Plans (3-5 days)
|
||||
1. Implement compile-plan.
|
||||
2. Strip unused protocols based on plan metadata.
|
||||
3. Cache compiled outputs with mtime checks.
|
||||
|
||||
### Phase 4: Railroad Architecture (5-10 days)
|
||||
1. Implement next-step state machine.
|
||||
2. Migrate workflows to deterministic code.
|
||||
3. Add micro-agent chaining with context flush.
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
- Average context usage under 40% at task start.
|
||||
- Verification pass rate >= 95%.
|
||||
- No measurable latency increase from dynamic loading.
|
||||
- Stable or improved deviation rate.
|
||||
- Reduced user "skip" frequency during checkpoints.
|
||||
|
||||
---
|
||||
|
||||
## Risks and Mitigations
|
||||
- Missing edge-case instructions
|
||||
Mitigation: fallback to full prompt under --context=full.
|
||||
- Compiled plan staleness
|
||||
Mitigation: mtime checks and auto-recompile.
|
||||
- Railroad refactor complexity
|
||||
Mitigation: incremental rollout with feature flags.
|
||||
|
||||
---
|
||||
|
||||
## Recommendation
|
||||
Start with Phase 1 and Phase 2 immediately. These deliver the largest context reductions per unit of risk and pave the way for compiled plans and the Railroad architecture. Once stable, proceed to Phase 3 and Phase 4.
|
||||
|
||||
---
|
||||
|
||||
## Proposed Next Steps
|
||||
1. Approve Phase 1 scope and timeline.
|
||||
2. Decide acceptable tolerance for architectural refactors.
|
||||
3. Choose quality metrics and thresholds for go/no-go.
|
||||
|
||||
If you want, I can turn this into actionable tickets with acceptance criteria and an initial implementation plan for gsd-tools and prompt splits.
|
||||
72
optimisation-ideas/syntheses/gemini.md
Normal file
72
optimisation-ideas/syntheses/gemini.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# GSD Performance Architecture: The "Hydra" Engine
|
||||
> **Objective:** Transition GSD from a context-taxed "Prompt-Driven" system to a "Software-Engineered" Agentic Framework.
|
||||
|
||||
## 1. The Core Philosophy: "Attention Density"
|
||||
In LLM systems, **Context is Noise**. Every irrelevant line of TDD instruction loaded during a simple documentation task isn't just a "token cost"—it is **attention dilution**. The "Hydra" architecture ensures that for any given turn, the LLM has **100% relevance density**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Structural Synthesis: The Three Pillars
|
||||
|
||||
### Pillar I: The "Railroad" Spine (Deterministic Logic)
|
||||
We will move workflow management out of the LLM's head and into `gsd-tools.js`.
|
||||
- **The Shift:** Instead of an Agent reading a 300-line `execute-phase.md` to figure out what to do next, it calls `gsd-tools next-step`.
|
||||
- **The Result:** The LLM's context window is freed from "Process Overhead" and reserved entirely for "Task Logic."
|
||||
|
||||
### Pillar II: "Lazy-Load" Context (The JIT Memory)
|
||||
We will implement **Just-In-Time (JIT)** injection for all instructional content.
|
||||
- **Tiered Agents:** `gsd-planner-core` (The what) + `gsd-planner-tdd` (The how, loaded only when code changes are detected).
|
||||
- **Frontmatter Digests:** Instead of reading 10 full summaries (20KB), the Agent reads a single JSON digest (2KB) of dependencies and patterns.
|
||||
- **Compiled Plans:** Plans are "pre-baked" by `gsd-tools` to strip unused protocols (e.g., removing Checkpoint rules if the plan has no checkpoints).
|
||||
|
||||
### Pillar III: Chained Execution (Context Flushing)
|
||||
We will move away from monolithic sessions.
|
||||
- **Micro-Agents:** Break `new-project` into: `Interviewer` → `Architect` → `Roadmapper`.
|
||||
- **Hand-offs:** Each agent performs its task, writes to disk, and **terminates**. The next agent starts with a **clean context window**, reading only the necessary outputs from the previous step. This eliminates "Transcript Bloat."
|
||||
|
||||
---
|
||||
|
||||
## 3. The Target State Execution Flow
|
||||
*Example: Running `gsd execute-phase`*
|
||||
|
||||
1. **State Check:** `gsd-tools` reads `.planning/state.json`.
|
||||
2. **Context Assembly:**
|
||||
- Loads `gsd-executor-core.md` (Base identity).
|
||||
- Detects "TDD" flag in task: Injects `references/tdd.md`.
|
||||
- Detects "API" change: Injects `history-digest.json` filtered for "API" tags.
|
||||
3. **Execution:** Agent executes the task with **< 15% context usage**.
|
||||
4. **Atomic Update:** Agent reports success. `gsd-tools` patches `STATE.md` and `SUMMARY.md` via deterministic code, not LLM rewriting.
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementation Roadmap (The 4-Phase Sprint)
|
||||
|
||||
### Phase 1: The "Low-Hanging" Pruning (Quick Wins)
|
||||
* **Action:** Implement `gsd-tools history-digest` and `gsd-tools state patch`.
|
||||
* **Impact:** Immediate 40% reduction in history/state overhead.
|
||||
* **Timeline:** 1 Day.
|
||||
|
||||
### Phase 2: The "Lazy-Load" Restructuring
|
||||
* **Action:** Split `gsd-executor` and `gsd-planner` into `-core` and `-ext` modules.
|
||||
* **Action:** Update wrapper scripts to dynamically assemble prompts based on task metadata.
|
||||
* **Impact:** Massive reduction in "Instructional Noise."
|
||||
* **Timeline:** 3 Days.
|
||||
|
||||
### Phase 3: The "Railroad" Migration
|
||||
* **Action:** Convert `workflows/*.md` logic into a JSON-based state machine in `gsd-tools`.
|
||||
* **Action:** Implement the `next-step` pattern.
|
||||
* **Impact:** Elimination of "Process Hallucination" and workflow skipping.
|
||||
* **Timeline:** 5 Days.
|
||||
|
||||
### Phase 4: Semantic Intelligence (The MCP Layer)
|
||||
* **Action:** Extend `gsd-memory` MCP to index summaries.
|
||||
* **Action:** Replace `grep` searches with semantic queries.
|
||||
* **Impact:** Precise, noise-free context retrieval for large codebases.
|
||||
* **Timeline:** 7 Days.
|
||||
|
||||
---
|
||||
|
||||
## 5. Risk Assessment: "The Instruction Guardrail"
|
||||
You noted that **instructional content is crucial**.
|
||||
- **Mitigation:** We do not "slim" the instructions; we **modularize** them.
|
||||
- **Example:** The TDD protocol remains exactly as detailed as it is today, but it simply *does not exist* in the LLM's mind when it is renaming a file or updating a README. This actually **improves** TDD fidelity because the LLM isn't distracted by other protocols.
|
||||
@@ -42,6 +42,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build:hooks": "node scripts/build-hooks.js",
|
||||
"prepublishOnly": "npm run build:hooks"
|
||||
"prepublishOnly": "npm run build:hooks",
|
||||
"test": "node --test get-shit-done/bin/gsd-tools.test.js"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user