@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 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.18.0...HEAD
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/{phase}-RESEARCH.md`
180
+ **Location:** `.planning/phases/XX-name/{phase_num}-RESEARCH.md`
181
181
 
182
182
  ```markdown
183
183
  # Phase [X]: [Name] - Research
@@ -286,12 +286,46 @@ After roadmap creation, REQUIREMENTS.md gets updated with phase mappings:
286
286
 
287
287
  ## ROADMAP.md Structure
288
288
 
289
- Use template from `~/.claude/get-shit-done/templates/roadmap.md`.
289
+ **CRITICAL: ROADMAP.md requires TWO phase representations. Both are mandatory.**
290
290
 
291
- Key sections:
292
- - Overview (2-3 sentences)
293
- - Phases with Goal, Dependencies, Requirements, Success Criteria
294
- - Progress table
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
 
@@ -317,7 +317,7 @@ gaps:
317
317
 
318
318
  ## Create VERIFICATION.md
319
319
 
320
- Create `.planning/phases/{phase_dir}/{phase}-VERIFICATION.md`:
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}/{phase}-VERIFICATION.md
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 claudeDirRegex = /~\/\.claude\//g;
650
+ const globalClaudeRegex = /~\/\.claude\//g;
651
+ const localClaudeRegex = /\.\/\.claude\//g;
630
652
  const opencodeDirRegex = /~\/\.opencode\//g;
631
- content = content.replace(claudeDirRegex, pathPrefix);
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
- // Always replace ~/.claude/ as it is the source of truth in the repo
691
+ // Replace ~/.claude/ and ./.claude/ with runtime-appropriate paths
669
692
  let content = fs.readFileSync(srcPath, 'utf8');
670
- const claudeDirRegex = /~\/\.claude\//g;
671
- content = content.replace(claudeDirRegex, pathPrefix);
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. Clean up settings.json (remove GSD hooks and statusline)
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
- fs.copyFileSync(srcFile, destFile);
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:** `{phase}-CONTEXT.md` — decisions clear enough that downstream agents can act without asking the user again
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="${HOME}/.claude/gsd-local-patches"
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="./.claude/gsd-local-patches"
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: {phase}-UAT.md tracking all test results. If issues found: diagnosed gaps, verified fix plans ready for /gsd:execute-phase
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 "### Phase X:" or "### Phase X.Y:" with optional name
838
+ // Match "## Phase X:" or "### Phase X:" with optional name
839
839
  const phasePattern = new RegExp(
840
- `###\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
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###\s+Phase\s+\d/i);
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 model = agentModels[profile] || agentModels['balanced'] || 'sonnet';
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 = /###\s*Phase\s+(\d+(?:\.\d+)?)\s*:\s*([^\n]+)/gi;
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###\s+Phase\s+\d/i);
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 = /###\s*Phase\s+(\d+)(?:\.\d+)?:/gi;
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(`###\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
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(`(###\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
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###\s+Phase\s+\d/i);
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?###\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n###\\s+Phase\\s+\\d|$)`,
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(`(###\\s*Phase\\s+)${oldStr}(\\s*:)`, 'gi'),
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
- `(###\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
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 = /###\s*Phase\s+(\d+(?:\.\d+)?)\s*:/gi;
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
- return agentModels[profile] || agentModels['balanced'] || 'sonnet';
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
- const phaseInfo = findPhaseInternal(cwd, phase);
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}" # e.g., "opus" for quality profile
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
  # UAT Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-UAT.md` — persistent UAT session tracking.
3
+ Template for `.planning/phases/XX-name/{phase_num}-UAT.md` — persistent UAT session tracking.
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Phase Context Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-CONTEXT.md` - captures implementation decisions for a phase.
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/{phase}-CONTEXT.md`
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}/{phase}-CONTEXT.md
25
+ @.planning/phases/{phase_dir}/{phase_num}-CONTEXT.md
26
26
 
27
27
  **Research (if exists):**
28
- @.planning/phases/{phase_dir}/{phase}-RESEARCH.md
28
+ @.planning/phases/{phase_dir}/{phase_num}-RESEARCH.md
29
29
 
30
30
  **Gap Closure (if --gaps mode):**
31
- @.planning/phases/{phase_dir}/{phase}-VERIFICATION.md
32
- @.planning/phases/{phase_dir}/{phase}-UAT.md
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/{phase}-RESEARCH.md` - comprehensive ecosystem research before planning.
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/{phase}-RESEARCH.md`
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
@@ -1,6 +1,6 @@
1
1
  # Verification Report Template
2
2
 
3
- Template for `.planning/phases/XX-name/{phase}-VERIFICATION.md` — phase goal verification results.
3
+ Template for `.planning/phases/XX-name/{phase_num}-VERIFICATION.md` — phase goal verification results.
4
4
 
5
5
  ---
6
6
 
@@ -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({phase}): add root causes from diagnosis" --files ".planning/phases/XX-name/{phase}-UAT.md"
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 Confidence"
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: "Existing context"
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}/{phase}-VERIFICATION.md
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}/{phase}-VERIFICATION.md` — full report
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: "Existing Code"
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: "Model Profile",
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 name]"
666
+ - header: "[Category]" (max 12 chars)
667
667
  - question: "Which [category] features are in v1?"
668
668
  - multiSelect: true
669
669
  - options:
@@ -104,7 +104,7 @@ IMPORTANT: If CONTEXT.md exists below, it contains user decisions from /gsd:disc
104
104
  </additional_context>
105
105
 
106
106
  <output>
107
- Write to: {phase_dir}/{phase}-RESEARCH.md
107
+ Write to: {phase_dir}/{phase_num}-RESEARCH.md
108
108
  </output>
109
109
  ```
110
110
 
@@ -194,7 +194,7 @@ Read its `<objective>` section.
194
194
 
195
195
  **Route B: Phase needs planning**
196
196
 
197
- Check if `{phase}-CONTEXT.md` exists in phase directory.
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
- **{phase}-UAT.md** has {N} gaps requiring fixes.
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
- if [ -f "./.claude/get-shit-done/VERSION" ]; then
17
- cat "./.claude/get-shit-done/VERSION"
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/{phase}-UAT.md`
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({phase}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase}-UAT.md"
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}/{phase}-UAT.md
369
+ @.planning/phases/{phase_dir}/{phase_num}-UAT.md
370
370
 
371
371
  **Project State:**
372
372
  @.planning/STATE.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahmed118glitch/get-shit-done-codex",
3
- "version": "1.18.4",
3
+ "version": "1.19.0",
4
4
  "description": "Codex-native package for the get-shit-done workflow with native subagent orchestration.",
5
5
  "bin": {
6
6
  "get-shit-done-codex": "bin/install-codex.js",