@ahmed118glitch/get-shit-done-codex 1.18.4 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-roadmapper.md +39 -5
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +65 -8
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/reapply-patches.md +3 -3
- package/commands/gsd/verify-work.md +1 -1
- package/get-shit-done/bin/gsd-tools.js +106 -19
- package/get-shit-done/bin/gsd-tools.test.js +44 -0
- package/get-shit-done/references/model-profile-resolution.md +4 -2
- package/get-shit-done/references/model-profiles.md +3 -0
- package/get-shit-done/references/questioning.md +1 -0
- package/get-shit-done/templates/UAT.md +1 -1
- package/get-shit-done/templates/context.md +2 -2
- package/get-shit-done/templates/planner-subagent-prompt.md +4 -4
- package/get-shit-done/templates/research.md +2 -2
- package/get-shit-done/templates/verification-report.md +1 -1
- package/get-shit-done/workflows/diagnose-issues.md +1 -1
- package/get-shit-done/workflows/discovery-phase.md +1 -1
- package/get-shit-done/workflows/discuss-phase.md +3 -3
- package/get-shit-done/workflows/execute-phase.md +51 -2
- package/get-shit-done/workflows/new-milestone.md +1 -1
- package/get-shit-done/workflows/new-project.md +3 -3
- package/get-shit-done/workflows/plan-phase.md +1 -1
- package/get-shit-done/workflows/progress.md +2 -2
- package/get-shit-done/workflows/update.md +4 -2
- package/get-shit-done/workflows/verify-work.md +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.19.0] - 2026-02-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Brave Search integration for researchers (requires BRAVE_API_KEY environment variable)
|
|
13
|
+
- GitHub issue templates for bug reports and feature requests
|
|
14
|
+
- Security policy for responsible disclosure
|
|
15
|
+
- Auto-labeling workflow for new issues
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- UAT gaps and debug sessions now auto-resolve after gap-closure phase execution (#580)
|
|
19
|
+
- Fall back to ROADMAP.md when phase directory missing (#521)
|
|
20
|
+
- Template hook paths for OpenCode/Gemini runtimes (#585)
|
|
21
|
+
- Accept both `##` and `###` phase headers, detect malformed ROADMAPs (#598, #599)
|
|
22
|
+
- Use `{phase_num}` instead of ambiguous `{phase}` for filenames (#601)
|
|
23
|
+
- Add package.json to prevent ESM inheritance issues (#602)
|
|
24
|
+
|
|
9
25
|
## [1.18.0] - 2026-02-08
|
|
10
26
|
|
|
11
27
|
### Added
|
|
@@ -1206,7 +1222,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
1206
1222
|
- YOLO mode for autonomous execution
|
|
1207
1223
|
- Interactive mode with checkpoints
|
|
1208
1224
|
|
|
1209
|
-
[Unreleased]: https://github.com/glittercowboy/get-shit-done/compare/v1.
|
|
1225
|
+
[Unreleased]: https://github.com/glittercowboy/get-shit-done/compare/v1.19.0...HEAD
|
|
1226
|
+
[1.19.0]: https://github.com/glittercowboy/get-shit-done/releases/tag/v1.19.0
|
|
1210
1227
|
[1.18.0]: https://github.com/glittercowboy/get-shit-done/releases/tag/v1.18.0
|
|
1211
1228
|
[1.17.0]: https://github.com/glittercowboy/get-shit-done/releases/tag/v1.17.0
|
|
1212
1229
|
[1.16.0]: https://github.com/glittercowboy/get-shit-done/releases/tag/v1.16.0
|
|
@@ -177,7 +177,7 @@ Priority: Context7 > Official Docs > Official GitHub > Verified WebSearch > Unve
|
|
|
177
177
|
|
|
178
178
|
## RESEARCH.md Structure
|
|
179
179
|
|
|
180
|
-
**Location:** `.planning/phases/XX-name/{
|
|
180
|
+
**Location:** `.planning/phases/XX-name/{phase_num}-RESEARCH.md`
|
|
181
181
|
|
|
182
182
|
```markdown
|
|
183
183
|
# Phase [X]: [Name] - Research
|
package/agents/gsd-roadmapper.md
CHANGED
|
@@ -286,12 +286,46 @@ After roadmap creation, REQUIREMENTS.md gets updated with phase mappings:
|
|
|
286
286
|
|
|
287
287
|
## ROADMAP.md Structure
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
**CRITICAL: ROADMAP.md requires TWO phase representations. Both are mandatory.**
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
-
|
|
291
|
+
### 1. Summary Checklist (under `## Phases`)
|
|
292
|
+
|
|
293
|
+
```markdown
|
|
294
|
+
- [ ] **Phase 1: Name** - One-line description
|
|
295
|
+
- [ ] **Phase 2: Name** - One-line description
|
|
296
|
+
- [ ] **Phase 3: Name** - One-line description
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### 2. Detail Sections (under `## Phase Details`)
|
|
300
|
+
|
|
301
|
+
```markdown
|
|
302
|
+
### Phase 1: Name
|
|
303
|
+
**Goal**: What this phase delivers
|
|
304
|
+
**Depends on**: Nothing (first phase)
|
|
305
|
+
**Requirements**: REQ-01, REQ-02
|
|
306
|
+
**Success Criteria** (what must be TRUE):
|
|
307
|
+
1. Observable behavior from user perspective
|
|
308
|
+
2. Observable behavior from user perspective
|
|
309
|
+
**Plans**: TBD
|
|
310
|
+
|
|
311
|
+
### Phase 2: Name
|
|
312
|
+
**Goal**: What this phase delivers
|
|
313
|
+
**Depends on**: Phase 1
|
|
314
|
+
...
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**The `### Phase X:` headers are parsed by downstream tools.** If you only write the summary checklist, phase lookups will fail.
|
|
318
|
+
|
|
319
|
+
### 3. Progress Table
|
|
320
|
+
|
|
321
|
+
```markdown
|
|
322
|
+
| Phase | Plans Complete | Status | Completed |
|
|
323
|
+
|-------|----------------|--------|-----------|
|
|
324
|
+
| 1. Name | 0/3 | Not started | - |
|
|
325
|
+
| 2. Name | 0/2 | Not started | - |
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Reference full template: `~/.claude/get-shit-done/templates/roadmap.md`
|
|
295
329
|
|
|
296
330
|
## STATE.md Structure
|
|
297
331
|
|
package/agents/gsd-verifier.md
CHANGED
|
@@ -317,7 +317,7 @@ gaps:
|
|
|
317
317
|
|
|
318
318
|
## Create VERIFICATION.md
|
|
319
319
|
|
|
320
|
-
Create `.planning/phases/{phase_dir}/{
|
|
320
|
+
Create `.planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md`:
|
|
321
321
|
|
|
322
322
|
```markdown
|
|
323
323
|
---
|
|
@@ -411,7 +411,7 @@ Return with:
|
|
|
411
411
|
|
|
412
412
|
**Status:** {passed | gaps_found | human_needed}
|
|
413
413
|
**Score:** {N}/{M} must-haves verified
|
|
414
|
-
**Report:** .planning/phases/{phase_dir}/{
|
|
414
|
+
**Report:** .planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md
|
|
415
415
|
|
|
416
416
|
{If passed:}
|
|
417
417
|
All must-haves verified. Phase goal achieved. Ready to proceed.
|
package/bin/install.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
@@ -46,6 +46,27 @@ function getDirName(runtime) {
|
|
|
46
46
|
return '.claude';
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Get the config directory path relative to home directory for a runtime
|
|
51
|
+
* Used for templating hooks that use path.join(homeDir, '<configDir>', ...)
|
|
52
|
+
* @param {string} runtime - 'claude', 'opencode', or 'gemini'
|
|
53
|
+
* @param {boolean} isGlobal - Whether this is a global install
|
|
54
|
+
*/
|
|
55
|
+
function getConfigDirFromHome(runtime, isGlobal) {
|
|
56
|
+
if (!isGlobal) {
|
|
57
|
+
// Local installs use the same dir name pattern
|
|
58
|
+
return getDirName(runtime);
|
|
59
|
+
}
|
|
60
|
+
// Global installs - OpenCode uses XDG path structure
|
|
61
|
+
if (runtime === 'opencode') {
|
|
62
|
+
// OpenCode: ~/.config/opencode -> '.config', 'opencode'
|
|
63
|
+
// Return as comma-separated for path.join() replacement
|
|
64
|
+
return "'.config', 'opencode'";
|
|
65
|
+
}
|
|
66
|
+
if (runtime === 'gemini') return "'.gemini'";
|
|
67
|
+
return "'.claude'";
|
|
68
|
+
}
|
|
69
|
+
|
|
49
70
|
/**
|
|
50
71
|
* Get the global config directory for OpenCode
|
|
51
72
|
* OpenCode follows XDG Base Directory spec and uses ~/.config/opencode/
|
|
@@ -626,9 +647,11 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
|
|
|
626
647
|
const destPath = path.join(destDir, destName);
|
|
627
648
|
|
|
628
649
|
let content = fs.readFileSync(srcPath, 'utf8');
|
|
629
|
-
const
|
|
650
|
+
const globalClaudeRegex = /~\/\.claude\//g;
|
|
651
|
+
const localClaudeRegex = /\.\/\.claude\//g;
|
|
630
652
|
const opencodeDirRegex = /~\/\.opencode\//g;
|
|
631
|
-
content = content.replace(
|
|
653
|
+
content = content.replace(globalClaudeRegex, pathPrefix);
|
|
654
|
+
content = content.replace(localClaudeRegex, `./${getDirName(runtime)}/`);
|
|
632
655
|
content = content.replace(opencodeDirRegex, pathPrefix);
|
|
633
656
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
634
657
|
content = convertClaudeToOpencodeFrontmatter(content);
|
|
@@ -665,10 +688,12 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
|
|
|
665
688
|
if (entry.isDirectory()) {
|
|
666
689
|
copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime);
|
|
667
690
|
} else if (entry.name.endsWith('.md')) {
|
|
668
|
-
//
|
|
691
|
+
// Replace ~/.claude/ and ./.claude/ with runtime-appropriate paths
|
|
669
692
|
let content = fs.readFileSync(srcPath, 'utf8');
|
|
670
|
-
const
|
|
671
|
-
|
|
693
|
+
const globalClaudeRegex = /~\/\.claude\//g;
|
|
694
|
+
const localClaudeRegex = /\.\/\.claude\//g;
|
|
695
|
+
content = content.replace(globalClaudeRegex, pathPrefix);
|
|
696
|
+
content = content.replace(localClaudeRegex, `./${dirName}/`);
|
|
672
697
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
673
698
|
|
|
674
699
|
// Convert frontmatter for opencode compatibility
|
|
@@ -867,7 +892,23 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
867
892
|
}
|
|
868
893
|
}
|
|
869
894
|
|
|
870
|
-
// 5.
|
|
895
|
+
// 5. Remove GSD package.json (CommonJS mode marker)
|
|
896
|
+
const pkgJsonPath = path.join(targetDir, 'package.json');
|
|
897
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
898
|
+
try {
|
|
899
|
+
const content = fs.readFileSync(pkgJsonPath, 'utf8').trim();
|
|
900
|
+
// Only remove if it's our minimal CommonJS marker
|
|
901
|
+
if (content === '{"type":"commonjs"}') {
|
|
902
|
+
fs.unlinkSync(pkgJsonPath);
|
|
903
|
+
removedCount++;
|
|
904
|
+
console.log(` ${green}✓${reset} Removed GSD package.json`);
|
|
905
|
+
}
|
|
906
|
+
} catch (e) {
|
|
907
|
+
// Ignore read errors
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// 6. Clean up settings.json (remove GSD hooks and statusline)
|
|
871
912
|
const settingsPath = path.join(targetDir, 'settings.json');
|
|
872
913
|
if (fs.existsSync(settingsPath)) {
|
|
873
914
|
let settings = readSettings(settingsPath);
|
|
@@ -1405,17 +1446,33 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1405
1446
|
failures.push('VERSION');
|
|
1406
1447
|
}
|
|
1407
1448
|
|
|
1449
|
+
// Write package.json to force CommonJS mode for GSD scripts
|
|
1450
|
+
// Prevents "require is not defined" errors when project has "type": "module"
|
|
1451
|
+
// Node.js walks up looking for package.json - this stops inheritance from project
|
|
1452
|
+
const pkgJsonDest = path.join(targetDir, 'package.json');
|
|
1453
|
+
fs.writeFileSync(pkgJsonDest, '{"type":"commonjs"}\n');
|
|
1454
|
+
console.log(` ${green}✓${reset} Wrote package.json (CommonJS mode)`);
|
|
1455
|
+
|
|
1408
1456
|
// Copy hooks from dist/ (bundled with dependencies)
|
|
1457
|
+
// Template paths for the target runtime (replaces '.claude' with correct config dir)
|
|
1409
1458
|
const hooksSrc = path.join(src, 'hooks', 'dist');
|
|
1410
1459
|
if (fs.existsSync(hooksSrc)) {
|
|
1411
1460
|
const hooksDest = path.join(targetDir, 'hooks');
|
|
1412
1461
|
fs.mkdirSync(hooksDest, { recursive: true });
|
|
1413
1462
|
const hookEntries = fs.readdirSync(hooksSrc);
|
|
1463
|
+
const configDirReplacement = getConfigDirFromHome(runtime, isGlobal);
|
|
1414
1464
|
for (const entry of hookEntries) {
|
|
1415
1465
|
const srcFile = path.join(hooksSrc, entry);
|
|
1416
1466
|
if (fs.statSync(srcFile).isFile()) {
|
|
1417
1467
|
const destFile = path.join(hooksDest, entry);
|
|
1418
|
-
|
|
1468
|
+
// Template .js files to replace '.claude' with runtime-specific config dir
|
|
1469
|
+
if (entry.endsWith('.js')) {
|
|
1470
|
+
let content = fs.readFileSync(srcFile, 'utf8');
|
|
1471
|
+
content = content.replace(/'\.claude'/g, configDirReplacement);
|
|
1472
|
+
fs.writeFileSync(destFile, content);
|
|
1473
|
+
} else {
|
|
1474
|
+
fs.copyFileSync(srcFile, destFile);
|
|
1475
|
+
}
|
|
1419
1476
|
}
|
|
1420
1477
|
}
|
|
1421
1478
|
if (verifyInstalled(hooksDest, 'hooks')) {
|
|
@@ -20,7 +20,7 @@ Extract implementation decisions that downstream agents need — researcher and
|
|
|
20
20
|
3. Deep-dive each selected area until satisfied
|
|
21
21
|
4. Create CONTEXT.md with decisions that guide research and planning
|
|
22
22
|
|
|
23
|
-
**Output:** `{
|
|
23
|
+
**Output:** `{phase_num}-CONTEXT.md` — decisions clear enough that downstream agents can act without asking the user again
|
|
24
24
|
</objective>
|
|
25
25
|
|
|
26
26
|
<execution_context>
|
|
@@ -14,11 +14,11 @@ After a GSD update wipes and reinstalls files, this command merges user's previo
|
|
|
14
14
|
Check for local patches directory:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
# Global install
|
|
18
|
-
PATCHES_DIR
|
|
17
|
+
# Global install (path templated at install time)
|
|
18
|
+
PATCHES_DIR=~/.claude/gsd-local-patches
|
|
19
19
|
# Local install fallback
|
|
20
20
|
if [ ! -d "$PATCHES_DIR" ]; then
|
|
21
|
-
PATCHES_DIR
|
|
21
|
+
PATCHES_DIR=./.claude/gsd-local-patches
|
|
22
22
|
fi
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -16,7 +16,7 @@ Validate built features through conversational testing with persistent state.
|
|
|
16
16
|
|
|
17
17
|
Purpose: Confirm what Claude built actually works from user's perspective. One test at a time, plain text responses, no interrogation. When issues are found, automatically diagnose, plan fixes, and prepare for execution.
|
|
18
18
|
|
|
19
|
-
Output: {
|
|
19
|
+
Output: {phase_num}-UAT.md tracking all test results. If issues found: diagnosed gaps, verified fix plans ready for /gsd:execute-phase
|
|
20
20
|
</objective>
|
|
21
21
|
|
|
22
22
|
<execution_context>
|
|
@@ -835,14 +835,33 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
835
835
|
// Escape special regex chars in phase number, handle decimal
|
|
836
836
|
const escapedPhase = phaseNum.replace(/\./g, '\\.');
|
|
837
837
|
|
|
838
|
-
// Match "
|
|
838
|
+
// Match "## Phase X:" or "### Phase X:" with optional name
|
|
839
839
|
const phasePattern = new RegExp(
|
|
840
|
-
|
|
840
|
+
`#{2,3}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
|
|
841
841
|
'i'
|
|
842
842
|
);
|
|
843
843
|
const headerMatch = content.match(phasePattern);
|
|
844
844
|
|
|
845
845
|
if (!headerMatch) {
|
|
846
|
+
// Fallback: check if phase exists in summary list but missing detail section
|
|
847
|
+
const checklistPattern = new RegExp(
|
|
848
|
+
`-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${escapedPhase}:\\s*([^*]+)\\*\\*`,
|
|
849
|
+
'i'
|
|
850
|
+
);
|
|
851
|
+
const checklistMatch = content.match(checklistPattern);
|
|
852
|
+
|
|
853
|
+
if (checklistMatch) {
|
|
854
|
+
// Phase exists in summary but missing detail section - malformed ROADMAP
|
|
855
|
+
output({
|
|
856
|
+
found: false,
|
|
857
|
+
phase_number: phaseNum,
|
|
858
|
+
phase_name: checklistMatch[1].trim(),
|
|
859
|
+
error: 'malformed_roadmap',
|
|
860
|
+
message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
|
|
861
|
+
}, raw, '');
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
|
|
846
865
|
output({ found: false, phase_number: phaseNum }, raw, '');
|
|
847
866
|
return;
|
|
848
867
|
}
|
|
@@ -850,9 +869,9 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
850
869
|
const phaseName = headerMatch[1].trim();
|
|
851
870
|
const headerIndex = headerMatch.index;
|
|
852
871
|
|
|
853
|
-
// Find the end of this section (next ### or end of file)
|
|
872
|
+
// Find the end of this section (next ## or ### phase header, or end of file)
|
|
854
873
|
const restOfContent = content.slice(headerIndex);
|
|
855
|
-
const nextHeaderMatch = restOfContent.match(/\n
|
|
874
|
+
const nextHeaderMatch = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
|
|
856
875
|
const sectionEnd = nextHeaderMatch
|
|
857
876
|
? headerIndex + nextHeaderMatch.index
|
|
858
877
|
: content.length;
|
|
@@ -1328,7 +1347,8 @@ function cmdResolveModel(cwd, agentType, raw) {
|
|
|
1328
1347
|
return;
|
|
1329
1348
|
}
|
|
1330
1349
|
|
|
1331
|
-
const
|
|
1350
|
+
const resolved = agentModels[profile] || agentModels['balanced'] || 'sonnet';
|
|
1351
|
+
const model = resolved === 'opus' ? 'inherit' : resolved;
|
|
1332
1352
|
const result = { model, profile };
|
|
1333
1353
|
output(result, raw, model);
|
|
1334
1354
|
}
|
|
@@ -2430,8 +2450,8 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
2430
2450
|
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
2431
2451
|
const phasesDir = path.join(cwd, '.planning', 'phases');
|
|
2432
2452
|
|
|
2433
|
-
// Extract all phase headings: ### Phase N: Name
|
|
2434
|
-
const phasePattern =
|
|
2453
|
+
// Extract all phase headings: ## Phase N: Name or ### Phase N: Name
|
|
2454
|
+
const phasePattern = /#{2,3}\s*Phase\s+(\d+(?:\.\d+)?)\s*:\s*([^\n]+)/gi;
|
|
2435
2455
|
const phases = [];
|
|
2436
2456
|
let match;
|
|
2437
2457
|
|
|
@@ -2442,7 +2462,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
2442
2462
|
// Extract goal from the section
|
|
2443
2463
|
const sectionStart = match.index;
|
|
2444
2464
|
const restOfContent = content.slice(sectionStart);
|
|
2445
|
-
const nextHeader = restOfContent.match(/\n
|
|
2465
|
+
const nextHeader = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
|
|
2446
2466
|
const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
|
|
2447
2467
|
const section = content.slice(sectionStart, sectionEnd);
|
|
2448
2468
|
|
|
@@ -2520,6 +2540,16 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
2520
2540
|
const totalSummaries = phases.reduce((sum, p) => sum + p.summary_count, 0);
|
|
2521
2541
|
const completedPhases = phases.filter(p => p.disk_status === 'complete').length;
|
|
2522
2542
|
|
|
2543
|
+
// Detect phases in summary list without detail sections (malformed ROADMAP)
|
|
2544
|
+
const checklistPattern = /-\s*\[[ x]\]\s*\*\*Phase\s+(\d+(?:\.\d+)?)/gi;
|
|
2545
|
+
const checklistPhases = new Set();
|
|
2546
|
+
let checklistMatch;
|
|
2547
|
+
while ((checklistMatch = checklistPattern.exec(content)) !== null) {
|
|
2548
|
+
checklistPhases.add(checklistMatch[1]);
|
|
2549
|
+
}
|
|
2550
|
+
const detailPhases = new Set(phases.map(p => p.number));
|
|
2551
|
+
const missingDetails = [...checklistPhases].filter(p => !detailPhases.has(p));
|
|
2552
|
+
|
|
2523
2553
|
const result = {
|
|
2524
2554
|
milestones,
|
|
2525
2555
|
phases,
|
|
@@ -2530,6 +2560,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
2530
2560
|
progress_percent: totalPlans > 0 ? Math.round((totalSummaries / totalPlans) * 100) : 0,
|
|
2531
2561
|
current_phase: currentPhase ? currentPhase.number : null,
|
|
2532
2562
|
next_phase: nextPhase ? nextPhase.number : null,
|
|
2563
|
+
missing_phase_details: missingDetails.length > 0 ? missingDetails : null,
|
|
2533
2564
|
};
|
|
2534
2565
|
|
|
2535
2566
|
output(result, raw);
|
|
@@ -2551,7 +2582,7 @@ function cmdPhaseAdd(cwd, description, raw) {
|
|
|
2551
2582
|
const slug = generateSlugInternal(description);
|
|
2552
2583
|
|
|
2553
2584
|
// Find highest integer phase number
|
|
2554
|
-
const phasePattern =
|
|
2585
|
+
const phasePattern = /#{2,3}\s*Phase\s+(\d+)(?:\.\d+)?:/gi;
|
|
2555
2586
|
let maxPhase = 0;
|
|
2556
2587
|
let m;
|
|
2557
2588
|
while ((m = phasePattern.exec(content)) !== null) {
|
|
@@ -2609,7 +2640,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
2609
2640
|
|
|
2610
2641
|
// Verify target phase exists
|
|
2611
2642
|
const afterPhaseEscaped = afterPhase.replace(/\./g, '\\.');
|
|
2612
|
-
const targetPattern = new RegExp(
|
|
2643
|
+
const targetPattern = new RegExp(`#{2,3}\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
|
|
2613
2644
|
if (!targetPattern.test(content)) {
|
|
2614
2645
|
error(`Phase ${afterPhase} not found in ROADMAP.md`);
|
|
2615
2646
|
}
|
|
@@ -2641,7 +2672,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
2641
2672
|
const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${decimalPhase} to break down)\n`;
|
|
2642
2673
|
|
|
2643
2674
|
// Insert after the target phase section
|
|
2644
|
-
const headerPattern = new RegExp(`(
|
|
2675
|
+
const headerPattern = new RegExp(`(#{2,3}\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
|
|
2645
2676
|
const headerMatch = content.match(headerPattern);
|
|
2646
2677
|
if (!headerMatch) {
|
|
2647
2678
|
error(`Could not find Phase ${afterPhase} header`);
|
|
@@ -2649,7 +2680,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
2649
2680
|
|
|
2650
2681
|
const headerIdx = content.indexOf(headerMatch[0]);
|
|
2651
2682
|
const afterHeader = content.slice(headerIdx + headerMatch[0].length);
|
|
2652
|
-
const nextPhaseMatch = afterHeader.match(/\n
|
|
2683
|
+
const nextPhaseMatch = afterHeader.match(/\n#{2,3}\s+Phase\s+\d/i);
|
|
2653
2684
|
|
|
2654
2685
|
let insertIdx;
|
|
2655
2686
|
if (nextPhaseMatch) {
|
|
@@ -2832,7 +2863,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
2832
2863
|
// Remove the target phase section
|
|
2833
2864
|
const targetEscaped = targetPhase.replace(/\./g, '\\.');
|
|
2834
2865
|
const sectionPattern = new RegExp(
|
|
2835
|
-
`\\n
|
|
2866
|
+
`\\n?#{2,3}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,3}\\s+Phase\\s+\\d|$)`,
|
|
2836
2867
|
'i'
|
|
2837
2868
|
);
|
|
2838
2869
|
roadmapContent = roadmapContent.replace(sectionPattern, '');
|
|
@@ -2858,9 +2889,9 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
2858
2889
|
const oldPad = oldStr.padStart(2, '0');
|
|
2859
2890
|
const newPad = newStr.padStart(2, '0');
|
|
2860
2891
|
|
|
2861
|
-
// Phase headings: ### Phase 18: → ### Phase 17:
|
|
2892
|
+
// Phase headings: ## Phase 18: or ### Phase 18: → ## Phase 17: or ### Phase 17:
|
|
2862
2893
|
roadmapContent = roadmapContent.replace(
|
|
2863
|
-
new RegExp(`(
|
|
2894
|
+
new RegExp(`(#{2,3}\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'),
|
|
2864
2895
|
`$1${newStr}$2`
|
|
2865
2896
|
);
|
|
2866
2897
|
|
|
@@ -2971,7 +3002,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
2971
3002
|
|
|
2972
3003
|
// Update plan count in phase section
|
|
2973
3004
|
const planCountPattern = new RegExp(
|
|
2974
|
-
`(
|
|
3005
|
+
`(#{2,3}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
|
|
2975
3006
|
'i'
|
|
2976
3007
|
);
|
|
2977
3008
|
roadmapContent = roadmapContent.replace(
|
|
@@ -3206,7 +3237,7 @@ function cmdValidateConsistency(cwd, raw) {
|
|
|
3206
3237
|
|
|
3207
3238
|
// Extract phases from ROADMAP
|
|
3208
3239
|
const roadmapPhases = new Set();
|
|
3209
|
-
const phasePattern =
|
|
3240
|
+
const phasePattern = /#{2,3}\s*Phase\s+(\d+(?:\.\d+)?)\s*:/gi;
|
|
3210
3241
|
let m;
|
|
3211
3242
|
while ((m = phasePattern.exec(roadmapContent)) !== null) {
|
|
3212
3243
|
roadmapPhases.add(m[1]);
|
|
@@ -3479,7 +3510,8 @@ function resolveModelInternal(cwd, agentType) {
|
|
|
3479
3510
|
const profile = config.model_profile || 'balanced';
|
|
3480
3511
|
const agentModels = MODEL_PROFILES[agentType];
|
|
3481
3512
|
if (!agentModels) return 'sonnet';
|
|
3482
|
-
|
|
3513
|
+
const resolved = agentModels[profile] || agentModels['balanced'] || 'sonnet';
|
|
3514
|
+
return resolved === 'opus' ? 'inherit' : resolved;
|
|
3483
3515
|
}
|
|
3484
3516
|
|
|
3485
3517
|
function findPhaseInternal(cwd, phase) {
|
|
@@ -3533,6 +3565,40 @@ function findPhaseInternal(cwd, phase) {
|
|
|
3533
3565
|
}
|
|
3534
3566
|
}
|
|
3535
3567
|
|
|
3568
|
+
function getRoadmapPhaseInternal(cwd, phaseNum) {
|
|
3569
|
+
if (!phaseNum) return null;
|
|
3570
|
+
const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
|
|
3571
|
+
if (!fs.existsSync(roadmapPath)) return null;
|
|
3572
|
+
|
|
3573
|
+
try {
|
|
3574
|
+
const content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
3575
|
+
const escapedPhase = phaseNum.toString().replace(/\./g, '\\.');
|
|
3576
|
+
const phasePattern = new RegExp(`#{2,3}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
|
3577
|
+
const headerMatch = content.match(phasePattern);
|
|
3578
|
+
if (!headerMatch) return null;
|
|
3579
|
+
|
|
3580
|
+
const phaseName = headerMatch[1].trim();
|
|
3581
|
+
const headerIndex = headerMatch.index;
|
|
3582
|
+
const restOfContent = content.slice(headerIndex);
|
|
3583
|
+
const nextHeaderMatch = restOfContent.match(/\n#{2,3}\s+Phase\s+\d/i);
|
|
3584
|
+
const sectionEnd = nextHeaderMatch ? headerIndex + nextHeaderMatch.index : content.length;
|
|
3585
|
+
const section = content.slice(headerIndex, sectionEnd).trim();
|
|
3586
|
+
|
|
3587
|
+
const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
|
|
3588
|
+
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
3589
|
+
|
|
3590
|
+
return {
|
|
3591
|
+
found: true,
|
|
3592
|
+
phase_number: phaseNum.toString(),
|
|
3593
|
+
phase_name: phaseName,
|
|
3594
|
+
goal,
|
|
3595
|
+
section,
|
|
3596
|
+
};
|
|
3597
|
+
} catch {
|
|
3598
|
+
return null;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3536
3602
|
function pathExistsInternal(cwd, targetPath) {
|
|
3537
3603
|
const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
|
|
3538
3604
|
try {
|
|
@@ -3918,7 +3984,28 @@ function cmdInitVerifyWork(cwd, phase, raw) {
|
|
|
3918
3984
|
|
|
3919
3985
|
function cmdInitPhaseOp(cwd, phase, raw) {
|
|
3920
3986
|
const config = loadConfig(cwd);
|
|
3921
|
-
|
|
3987
|
+
let phaseInfo = findPhaseInternal(cwd, phase);
|
|
3988
|
+
|
|
3989
|
+
// Fallback to ROADMAP.md if no directory exists (e.g., Plans: TBD)
|
|
3990
|
+
if (!phaseInfo) {
|
|
3991
|
+
const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
|
|
3992
|
+
if (roadmapPhase?.found) {
|
|
3993
|
+
const phaseName = roadmapPhase.phase_name;
|
|
3994
|
+
phaseInfo = {
|
|
3995
|
+
found: true,
|
|
3996
|
+
directory: null,
|
|
3997
|
+
phase_number: roadmapPhase.phase_number,
|
|
3998
|
+
phase_name: phaseName,
|
|
3999
|
+
phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null,
|
|
4000
|
+
plans: [],
|
|
4001
|
+
summaries: [],
|
|
4002
|
+
incomplete_plans: [],
|
|
4003
|
+
has_research: false,
|
|
4004
|
+
has_context: false,
|
|
4005
|
+
has_verification: false,
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
3922
4009
|
|
|
3923
4010
|
const result = {
|
|
3924
4011
|
// Config
|
|
@@ -520,6 +520,50 @@ This phase covers:
|
|
|
520
520
|
assert.strictEqual(output.found, false, 'should return not found');
|
|
521
521
|
assert.strictEqual(output.error, 'ROADMAP.md not found', 'should explain why');
|
|
522
522
|
});
|
|
523
|
+
|
|
524
|
+
test('accepts ## phase headers (two hashes)', () => {
|
|
525
|
+
fs.writeFileSync(
|
|
526
|
+
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
|
527
|
+
`# Roadmap v1.0
|
|
528
|
+
|
|
529
|
+
## Phase 1: Foundation
|
|
530
|
+
**Goal:** Set up project infrastructure
|
|
531
|
+
**Plans:** 2 plans
|
|
532
|
+
|
|
533
|
+
## Phase 2: API
|
|
534
|
+
**Goal:** Build REST API
|
|
535
|
+
`
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const result = runGsdTools('roadmap get-phase 1', tmpDir);
|
|
539
|
+
assert.ok(result.success, `Command failed: ${result.error}`);
|
|
540
|
+
|
|
541
|
+
const output = JSON.parse(result.output);
|
|
542
|
+
assert.strictEqual(output.found, true, 'phase with ## header should be found');
|
|
543
|
+
assert.strictEqual(output.phase_name, 'Foundation', 'phase name extracted');
|
|
544
|
+
assert.strictEqual(output.goal, 'Set up project infrastructure', 'goal extracted');
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test('detects malformed ROADMAP with summary list but no detail sections', () => {
|
|
548
|
+
fs.writeFileSync(
|
|
549
|
+
path.join(tmpDir, '.planning', 'ROADMAP.md'),
|
|
550
|
+
`# Roadmap v1.0
|
|
551
|
+
|
|
552
|
+
## Phases
|
|
553
|
+
|
|
554
|
+
- [ ] **Phase 1: Foundation** - Set up project
|
|
555
|
+
- [ ] **Phase 2: API** - Build REST API
|
|
556
|
+
`
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
const result = runGsdTools('roadmap get-phase 1', tmpDir);
|
|
560
|
+
assert.ok(result.success, `Command failed: ${result.error}`);
|
|
561
|
+
|
|
562
|
+
const output = JSON.parse(result.output);
|
|
563
|
+
assert.strictEqual(output.found, false, 'phase should not be found');
|
|
564
|
+
assert.strictEqual(output.error, 'malformed_roadmap', 'should identify malformed roadmap');
|
|
565
|
+
assert.ok(output.message.includes('missing'), 'should explain the issue');
|
|
566
|
+
});
|
|
523
567
|
});
|
|
524
568
|
|
|
525
569
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -20,13 +20,15 @@ Look up the agent in the table for the resolved profile. Pass the model paramete
|
|
|
20
20
|
Task(
|
|
21
21
|
prompt="...",
|
|
22
22
|
subagent_type="gsd-planner",
|
|
23
|
-
model="{resolved_model}" #
|
|
23
|
+
model="{resolved_model}" # "inherit", "sonnet", or "haiku"
|
|
24
24
|
)
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
**Note:** Opus-tier agents resolve to `"inherit"` (not `"opus"`). This causes the agent to use the parent session's model, avoiding conflicts with organization policies that may block specific opus versions.
|
|
28
|
+
|
|
27
29
|
## Usage
|
|
28
30
|
|
|
29
31
|
1. Resolve once at orchestration start
|
|
30
32
|
2. Store the profile value
|
|
31
33
|
3. Look up each agent's model from the table when spawning
|
|
32
|
-
4. Pass model parameter to each Task call
|
|
34
|
+
4. Pass model parameter to each Task call (values: `"inherit"`, `"sonnet"`, `"haiku"`)
|
|
@@ -71,3 +71,6 @@ Verification requires goal-backward reasoning - checking if code *delivers* what
|
|
|
71
71
|
|
|
72
72
|
**Why Haiku for gsd-codebase-mapper?**
|
|
73
73
|
Read-only exploration and pattern extraction. No reasoning required, just structured output from file contents.
|
|
74
|
+
|
|
75
|
+
**Why `inherit` instead of passing `opus` directly?**
|
|
76
|
+
Claude Code's `"opus"` alias maps to a specific model version. Organizations may block older opus versions while allowing newer ones. GSD returns `"inherit"` for opus-tier agents, causing them to use whatever opus version the user has configured in their session. This avoids version conflicts and silent fallbacks to Sonnet.
|
|
@@ -79,6 +79,7 @@ Use AskUserQuestion to help users think by presenting concrete options to react
|
|
|
79
79
|
- Generic categories ("Technical", "Business", "Other")
|
|
80
80
|
- Leading options that presume an answer
|
|
81
81
|
- Too many options (2-4 is ideal)
|
|
82
|
+
- Headers longer than 12 characters (hard limit — validation will reject them)
|
|
82
83
|
|
|
83
84
|
**Example — vague answer:**
|
|
84
85
|
User says "it should be fast"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Phase Context Template
|
|
2
2
|
|
|
3
|
-
Template for `.planning/phases/XX-name/{
|
|
3
|
+
Template for `.planning/phases/XX-name/{phase_num}-CONTEXT.md` - captures implementation decisions for a phase.
|
|
4
4
|
|
|
5
5
|
**Purpose:** Document decisions that downstream agents need. Researcher uses this to know WHAT to investigate. Planner uses this to know WHAT choices are locked vs flexible.
|
|
6
6
|
|
|
@@ -276,7 +276,7 @@ The output should answer: "What does the researcher need to investigate? What ch
|
|
|
276
276
|
- "Easy to use"
|
|
277
277
|
|
|
278
278
|
**After creation:**
|
|
279
|
-
- File lives in phase directory: `.planning/phases/XX-name/{
|
|
279
|
+
- File lives in phase directory: `.planning/phases/XX-name/{phase_num}-CONTEXT.md`
|
|
280
280
|
- `gsd-phase-researcher` uses decisions to focus investigation
|
|
281
281
|
- `gsd-planner` uses decisions + research to create executable tasks
|
|
282
282
|
- Downstream agents should NOT need to ask the user again about captured decisions
|
|
@@ -22,14 +22,14 @@ Template for spawning gsd-planner agent. The agent contains all planning experti
|
|
|
22
22
|
@.planning/REQUIREMENTS.md
|
|
23
23
|
|
|
24
24
|
**Phase Context (if exists):**
|
|
25
|
-
@.planning/phases/{phase_dir}/{
|
|
25
|
+
@.planning/phases/{phase_dir}/{phase_num}-CONTEXT.md
|
|
26
26
|
|
|
27
27
|
**Research (if exists):**
|
|
28
|
-
@.planning/phases/{phase_dir}/{
|
|
28
|
+
@.planning/phases/{phase_dir}/{phase_num}-RESEARCH.md
|
|
29
29
|
|
|
30
30
|
**Gap Closure (if --gaps mode):**
|
|
31
|
-
@.planning/phases/{phase_dir}/{
|
|
32
|
-
@.planning/phases/{phase_dir}/{
|
|
31
|
+
@.planning/phases/{phase_dir}/{phase_num}-VERIFICATION.md
|
|
32
|
+
@.planning/phases/{phase_dir}/{phase_num}-UAT.md
|
|
33
33
|
|
|
34
34
|
</planning_context>
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Research Template
|
|
2
2
|
|
|
3
|
-
Template for `.planning/phases/XX-name/{
|
|
3
|
+
Template for `.planning/phases/XX-name/{phase_num}-RESEARCH.md` - comprehensive ecosystem research before planning.
|
|
4
4
|
|
|
5
5
|
**Purpose:** Document what Claude needs to know to implement a phase well - not just "which library" but "how do experts build this."
|
|
6
6
|
|
|
@@ -547,6 +547,6 @@ function useVehicleControls(rigidBodyRef) {
|
|
|
547
547
|
- Code examples can be referenced in task actions
|
|
548
548
|
|
|
549
549
|
**After creation:**
|
|
550
|
-
- File lives in phase directory: `.planning/phases/XX-name/{
|
|
550
|
+
- File lives in phase directory: `.planning/phases/XX-name/{phase_num}-RESEARCH.md`
|
|
551
551
|
- Referenced during planning workflow
|
|
552
552
|
- plan-phase loads it automatically when present
|
|
@@ -158,7 +158,7 @@ Update status in frontmatter to "diagnosed".
|
|
|
158
158
|
|
|
159
159
|
Commit the updated UAT.md:
|
|
160
160
|
```bash
|
|
161
|
-
node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs({
|
|
161
|
+
node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs({phase_num}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
|
|
162
162
|
```
|
|
163
163
|
</step>
|
|
164
164
|
|
|
@@ -216,7 +216,7 @@ After creating DISCOVERY.md, check confidence level.
|
|
|
216
216
|
If confidence is LOW:
|
|
217
217
|
Use AskUserQuestion:
|
|
218
218
|
|
|
219
|
-
- header: "Low
|
|
219
|
+
- header: "Low Conf."
|
|
220
220
|
- question: "Discovery confidence is LOW: [reason]. How would you like to proceed?"
|
|
221
221
|
- options:
|
|
222
222
|
- "Dig deeper" - Do more research before planning
|
|
@@ -136,7 +136,7 @@ ls ${phase_dir}/*-CONTEXT.md 2>/dev/null
|
|
|
136
136
|
|
|
137
137
|
**If exists:**
|
|
138
138
|
Use AskUserQuestion:
|
|
139
|
-
- header: "
|
|
139
|
+
- header: "Context"
|
|
140
140
|
- question: "Phase [X] already has context. What do you want to do?"
|
|
141
141
|
- options:
|
|
142
142
|
- "Update it" — Review and revise existing context
|
|
@@ -240,13 +240,13 @@ Ask 4 questions per area before offering to continue or move on. Each answer oft
|
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
2. **Ask 4 questions using AskUserQuestion:**
|
|
243
|
-
- header: "[Area]"
|
|
243
|
+
- header: "[Area]" (max 12 chars — abbreviate if needed)
|
|
244
244
|
- question: Specific decision for this area
|
|
245
245
|
- options: 2-3 concrete choices (AskUserQuestion adds "Other" automatically)
|
|
246
246
|
- Include "You decide" as an option when reasonable — captures Claude discretion
|
|
247
247
|
|
|
248
248
|
3. **After 4 questions, check:**
|
|
249
|
-
- header: "[Area]"
|
|
249
|
+
- header: "[Area]" (max 12 chars)
|
|
250
250
|
- question: "More questions about [area], or move to next?"
|
|
251
251
|
- options: "More questions" / "Next area"
|
|
252
252
|
|
|
@@ -226,6 +226,55 @@ After all waves:
|
|
|
226
226
|
```
|
|
227
227
|
</step>
|
|
228
228
|
|
|
229
|
+
<step name="close_parent_artifacts">
|
|
230
|
+
**For decimal/polish phases only (X.Y pattern):** Close the feedback loop by resolving parent UAT and debug artifacts.
|
|
231
|
+
|
|
232
|
+
**Skip if** phase number has no decimal (e.g., `3`, `04`) — only applies to gap-closure phases like `4.1`, `03.1`.
|
|
233
|
+
|
|
234
|
+
**1. Detect decimal phase and derive parent:**
|
|
235
|
+
```bash
|
|
236
|
+
# Check if phase_number contains a decimal
|
|
237
|
+
if [[ "$PHASE_NUMBER" == *.* ]]; then
|
|
238
|
+
PARENT_PHASE="${PHASE_NUMBER%%.*}"
|
|
239
|
+
fi
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**2. Find parent UAT file:**
|
|
243
|
+
```bash
|
|
244
|
+
find .planning/phases -path "*${PARENT_PHASE}*/*-UAT.md" -type f 2>/dev/null
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**If no parent UAT found:** Skip this step (gap-closure may have been triggered by VERIFICATION.md instead).
|
|
248
|
+
|
|
249
|
+
**3. Update UAT gap statuses:**
|
|
250
|
+
|
|
251
|
+
Read the parent UAT file's `## Gaps` section. For each gap entry with `status: failed`:
|
|
252
|
+
- Update to `status: resolved`
|
|
253
|
+
|
|
254
|
+
**4. Update UAT frontmatter:**
|
|
255
|
+
|
|
256
|
+
If all gaps now have `status: resolved`:
|
|
257
|
+
- Update frontmatter `status: diagnosed` → `status: resolved`
|
|
258
|
+
- Update frontmatter `updated:` timestamp
|
|
259
|
+
|
|
260
|
+
**5. Resolve referenced debug sessions:**
|
|
261
|
+
|
|
262
|
+
For each gap that has a `debug_session:` field:
|
|
263
|
+
- Read the debug session file
|
|
264
|
+
- Update frontmatter `status:` → `resolved`
|
|
265
|
+
- Update frontmatter `updated:` timestamp
|
|
266
|
+
- Move to resolved directory:
|
|
267
|
+
```bash
|
|
268
|
+
mkdir -p .planning/debug/resolved
|
|
269
|
+
mv .planning/debug/{slug}.md .planning/debug/resolved/
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**6. Commit updated artifacts:**
|
|
273
|
+
```bash
|
|
274
|
+
node ~/.claude/get-shit-done/bin/gsd-tools.js commit "docs(phase-${PARENT_PHASE}): resolve UAT gaps and debug sessions after ${PHASE_NUMBER} gap closure" --files .planning/phases/*${PARENT_PHASE}*/*-UAT.md .planning/debug/resolved/*.md
|
|
275
|
+
```
|
|
276
|
+
</step>
|
|
277
|
+
|
|
229
278
|
<step name="verify_phase_goal">
|
|
230
279
|
Verify phase achieved its GOAL, not just completed tasks.
|
|
231
280
|
|
|
@@ -267,7 +316,7 @@ All automated checks passed. {N} items need human testing:
|
|
|
267
316
|
## ⚠ Phase {X}: {Name} — Gaps Found
|
|
268
317
|
|
|
269
318
|
**Score:** {N}/{M} must-haves verified
|
|
270
|
-
**Report:** {phase_dir}/{
|
|
319
|
+
**Report:** {phase_dir}/{phase_num}-VERIFICATION.md
|
|
271
320
|
|
|
272
321
|
### What's Missing
|
|
273
322
|
{Gap summaries from VERIFICATION.md}
|
|
@@ -279,7 +328,7 @@ All automated checks passed. {N} items need human testing:
|
|
|
279
328
|
|
|
280
329
|
<sub>`/clear` first → fresh context window</sub>
|
|
281
330
|
|
|
282
|
-
Also: `cat {phase_dir}/{
|
|
331
|
+
Also: `cat {phase_dir}/{phase_num}-VERIFICATION.md` — full report
|
|
283
332
|
Also: `/gsd:verify-work {X}` — manual testing first
|
|
284
333
|
```
|
|
285
334
|
|
|
@@ -200,7 +200,7 @@ Present features by category:
|
|
|
200
200
|
|
|
201
201
|
**If no research:** Gather requirements through conversation. Ask: "What are the main things users need to do with [new features]?" Clarify, probe for related capabilities, group into categories.
|
|
202
202
|
|
|
203
|
-
**Scope each category** via AskUserQuestion (multiSelect: true):
|
|
203
|
+
**Scope each category** via AskUserQuestion (multiSelect: true, header max 12 chars):
|
|
204
204
|
- "[Feature 1]" — [brief description]
|
|
205
205
|
- "[Feature 2]" — [brief description]
|
|
206
206
|
- "None for this milestone" — Defer entire category
|
|
@@ -59,7 +59,7 @@ git init
|
|
|
59
59
|
**If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
|
|
60
60
|
|
|
61
61
|
Use AskUserQuestion:
|
|
62
|
-
- header: "
|
|
62
|
+
- header: "Codebase"
|
|
63
63
|
- question: "I detected existing code in this directory. Would you like to map the codebase first?"
|
|
64
64
|
- options:
|
|
65
65
|
- "Map codebase first" — Run /gsd:map-codebase to understand existing architecture (Recommended)
|
|
@@ -303,7 +303,7 @@ questions: [
|
|
|
303
303
|
]
|
|
304
304
|
},
|
|
305
305
|
{
|
|
306
|
-
header: "
|
|
306
|
+
header: "AI Models",
|
|
307
307
|
question: "Which AI models for planning agents?",
|
|
308
308
|
multiSelect: false,
|
|
309
309
|
options: [
|
|
@@ -663,7 +663,7 @@ For each capability mentioned:
|
|
|
663
663
|
|
|
664
664
|
For each category, use AskUserQuestion:
|
|
665
665
|
|
|
666
|
-
- header: "[Category
|
|
666
|
+
- header: "[Category]" (max 12 chars)
|
|
667
667
|
- question: "Which [category] features are in v1?"
|
|
668
668
|
- multiSelect: true
|
|
669
669
|
- options:
|
|
@@ -194,7 +194,7 @@ Read its `<objective>` section.
|
|
|
194
194
|
|
|
195
195
|
**Route B: Phase needs planning**
|
|
196
196
|
|
|
197
|
-
Check if `{
|
|
197
|
+
Check if `{phase_num}-CONTEXT.md` exists in phase directory.
|
|
198
198
|
|
|
199
199
|
**If CONTEXT.md exists:**
|
|
200
200
|
|
|
@@ -246,7 +246,7 @@ UAT.md exists with gaps (diagnosed issues). User needs to plan fixes.
|
|
|
246
246
|
|
|
247
247
|
## ⚠ UAT Gaps Found
|
|
248
248
|
|
|
249
|
-
**{
|
|
249
|
+
**{phase_num}-UAT.md** has {N} gaps requiring fixes.
|
|
250
250
|
|
|
251
251
|
`/gsd:plan-phase {phase} --gaps`
|
|
252
252
|
|
|
@@ -13,8 +13,9 @@ Detect whether GSD is installed locally or globally by checking both locations:
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
# Check local first (takes priority)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# Paths templated at install time for runtime compatibility
|
|
17
|
+
if [ -f ./.claude/get-shit-done/VERSION ]; then
|
|
18
|
+
cat ./.claude/get-shit-done/VERSION
|
|
18
19
|
echo "LOCAL"
|
|
19
20
|
elif [ -f ~/.claude/get-shit-done/VERSION ]; then
|
|
20
21
|
cat ~/.claude/get-shit-done/VERSION
|
|
@@ -167,6 +168,7 @@ rm -f ./.claude/cache/gsd-update-check.json
|
|
|
167
168
|
```bash
|
|
168
169
|
rm -f ~/.claude/cache/gsd-update-check.json
|
|
169
170
|
```
|
|
171
|
+
(Paths are templated at install time for runtime compatibility)
|
|
170
172
|
</step>
|
|
171
173
|
|
|
172
174
|
<step name="display_result">
|
|
@@ -164,7 +164,7 @@ skipped: 0
|
|
|
164
164
|
[none yet]
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
Write to `.planning/phases/XX-name/{
|
|
167
|
+
Write to `.planning/phases/XX-name/{phase_num}-UAT.md`
|
|
168
168
|
|
|
169
169
|
Proceed to `present_test`.
|
|
170
170
|
</step>
|
|
@@ -292,7 +292,7 @@ Clear Current Test section:
|
|
|
292
292
|
|
|
293
293
|
Commit the UAT file:
|
|
294
294
|
```bash
|
|
295
|
-
node ~/.claude/get-shit-done/bin/gsd-tools.js commit "test({
|
|
295
|
+
node ~/.claude/get-shit-done/bin/gsd-tools.js commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
|
|
296
296
|
```
|
|
297
297
|
|
|
298
298
|
Present summary:
|
|
@@ -366,7 +366,7 @@ Task(
|
|
|
366
366
|
**Mode:** gap_closure
|
|
367
367
|
|
|
368
368
|
**UAT with diagnoses:**
|
|
369
|
-
@.planning/phases/{phase_dir}/{
|
|
369
|
+
@.planning/phases/{phase_dir}/{phase_num}-UAT.md
|
|
370
370
|
|
|
371
371
|
**Project State:**
|
|
372
372
|
@.planning/STATE.md
|
package/package.json
CHANGED