gsd-antigravity-kit 1.27.2 → 1.27.3

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.
Files changed (70) hide show
  1. package/.agent/skills/gsd/SKILL.md +16 -2
  2. package/.agent/skills/gsd/assets/templates/config.json +3 -1
  3. package/.agent/skills/gsd/bin/gsd-tools.cjs +220 -105
  4. package/.agent/skills/gsd/bin/help-manifest.json +22 -1
  5. package/.agent/skills/gsd/bin/hooks/gsd-check-update.js +3 -2
  6. package/.agent/skills/gsd/bin/hooks/gsd-context-monitor.js +1 -1
  7. package/.agent/skills/gsd/bin/hooks/gsd-prompt-guard.js +1 -1
  8. package/.agent/skills/gsd/bin/hooks/gsd-statusline.js +1 -1
  9. package/.agent/skills/gsd/bin/hooks/gsd-workflow-guard.js +1 -1
  10. package/.agent/skills/gsd/bin/lib/commands.cjs +44 -7
  11. package/.agent/skills/gsd/bin/lib/config.cjs +19 -11
  12. package/.agent/skills/gsd/bin/lib/core.cjs +188 -33
  13. package/.agent/skills/gsd/bin/lib/init.cjs +480 -55
  14. package/.agent/skills/gsd/bin/lib/phase.cjs +142 -229
  15. package/.agent/skills/gsd/bin/lib/profile-pipeline.cjs +3 -1
  16. package/.agent/skills/gsd/bin/lib/state.cjs +16 -5
  17. package/.agent/skills/gsd/bin/lib/uat.cjs +5 -5
  18. package/.agent/skills/gsd/bin/lib/verify.cjs +11 -10
  19. package/.agent/skills/gsd/bin/lib/workstream.cjs +491 -0
  20. package/.agent/skills/gsd/references/agents/gsd-assumptions-analyzer.md +105 -0
  21. package/.agent/skills/gsd/references/agents/gsd-debugger.md +34 -0
  22. package/.agent/skills/gsd/references/agents/gsd-executor.md +2 -0
  23. package/.agent/skills/gsd/references/agents/gsd-phase-researcher.md +79 -0
  24. package/.agent/skills/gsd/references/agents/gsd-plan-checker.md +45 -0
  25. package/.agent/skills/gsd/references/agents/gsd-planner.md +45 -0
  26. package/.agent/skills/gsd/references/agents/gsd-roadmapper.md +29 -0
  27. package/.agent/skills/gsd/references/agents/gsd-verifier.md +116 -1
  28. package/.agent/skills/gsd/references/commands/discuss-phase.md +9 -36
  29. package/.agent/skills/gsd/references/commands/execute-phase.md +19 -2
  30. package/.agent/skills/gsd/references/commands/forensics.md +56 -0
  31. package/.agent/skills/gsd/references/commands/list-workspaces.md +19 -0
  32. package/.agent/skills/gsd/references/commands/manager.md +39 -0
  33. package/.agent/skills/gsd/references/commands/milestone-summary.md +51 -0
  34. package/.agent/skills/gsd/references/commands/new-workspace.md +44 -0
  35. package/.agent/skills/gsd/references/commands/plan-phase.md +3 -1
  36. package/.agent/skills/gsd/references/commands/remove-workspace.md +26 -0
  37. package/.agent/skills/gsd/references/commands/workstreams.md +63 -0
  38. package/.agent/skills/gsd/references/docs/decimal-phase-calculation.md +2 -3
  39. package/.agent/skills/gsd/references/docs/model-profiles.md +22 -2
  40. package/.agent/skills/gsd/references/docs/phase-argument-parsing.md +2 -2
  41. package/.agent/skills/gsd/references/docs/workstream-flag.md +58 -0
  42. package/.agent/skills/gsd/references/workflows/audit-milestone.md +2 -2
  43. package/.agent/skills/gsd/references/workflows/autonomous.md +73 -1
  44. package/.agent/skills/gsd/references/workflows/complete-milestone.md +1 -1
  45. package/.agent/skills/gsd/references/workflows/diagnose-issues.md +1 -0
  46. package/.agent/skills/gsd/references/workflows/discuss-phase-assumptions.md +645 -0
  47. package/.agent/skills/gsd/references/workflows/discuss-phase.md +11 -11
  48. package/.agent/skills/gsd/references/workflows/execute-phase.md +61 -13
  49. package/.agent/skills/gsd/references/workflows/execute-plan.md +1 -1
  50. package/.agent/skills/gsd/references/workflows/forensics.md +265 -0
  51. package/.agent/skills/gsd/references/workflows/help.md +3 -1
  52. package/.agent/skills/gsd/references/workflows/list-workspaces.md +56 -0
  53. package/.agent/skills/gsd/references/workflows/manager.md +360 -0
  54. package/.agent/skills/gsd/references/workflows/map-codebase.md +4 -4
  55. package/.agent/skills/gsd/references/workflows/milestone-summary.md +223 -0
  56. package/.agent/skills/gsd/references/workflows/new-milestone.md +36 -4
  57. package/.agent/skills/gsd/references/workflows/new-project.md +32 -1
  58. package/.agent/skills/gsd/references/workflows/new-workspace.md +237 -0
  59. package/.agent/skills/gsd/references/workflows/plan-milestone-gaps.md +1 -2
  60. package/.agent/skills/gsd/references/workflows/plan-phase.md +95 -25
  61. package/.agent/skills/gsd/references/workflows/progress.md +85 -16
  62. package/.agent/skills/gsd/references/workflows/quick.md +3 -2
  63. package/.agent/skills/gsd/references/workflows/remove-workspace.md +90 -0
  64. package/.agent/skills/gsd/references/workflows/resume-project.md +7 -7
  65. package/.agent/skills/gsd/references/workflows/settings.md +19 -8
  66. package/.agent/skills/gsd/references/workflows/transition.md +98 -11
  67. package/.agent/skills/gsd/references/workflows/validate-phase.md +4 -4
  68. package/.agent/skills/gsd-converter/SKILL.md +1 -0
  69. package/README.md +6 -6
  70. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gsd
3
- version: 1.27.0
3
+ version: 1.28.0
4
4
  description: "Antigravity GSD (Get Stuff Done) - A spec-driven hierarchical planning and execution system. Triggers on project planning, phase management, and GSD slash commands."
5
5
  ---
6
6
 
@@ -36,15 +36,20 @@ This skill should be used when:
36
36
  - `gsd:do`
37
37
  - `gsd:execute-phase`
38
38
  - `gsd:fast`
39
+ - `gsd:forensics`
39
40
  - `gsd:gsd-tools`
40
41
  - `gsd:health`
41
42
  - `gsd:help`
42
43
  - `gsd:insert-phase`
43
44
  - `gsd:join-discord`
44
45
  - `gsd:list-phase-assumptions`
46
+ - `gsd:list-workspaces`
47
+ - `gsd:manager`
45
48
  - `gsd:map-codebase`
49
+ - `gsd:milestone-summary`
46
50
  - `gsd:new-milestone`
47
51
  - `gsd:new-project`
52
+ - `gsd:new-workspace`
48
53
  - `gsd:next`
49
54
  - `gsd:note`
50
55
  - `gsd:pause-work`
@@ -57,6 +62,7 @@ This skill should be used when:
57
62
  - `gsd:quick`
58
63
  - `gsd:reapply-patches`
59
64
  - `gsd:remove-phase`
65
+ - `gsd:remove-workspace`
60
66
  - `gsd:research-phase`
61
67
  - `gsd:resume-work`
62
68
  - `gsd:review-backlog`
@@ -72,6 +78,7 @@ This skill should be used when:
72
78
  - `gsd:update`
73
79
  - `gsd:validate-phase`
74
80
  - `gsd:verify-work`
81
+ - `gsd:workstreams`
75
82
 
76
83
  ## System Overview
77
84
 
@@ -99,15 +106,20 @@ The following slash commands are available in this skill. Use them to drive the
99
106
  - **[`gsd:do`](references/commands/do.md)**: Route freeform text to the right GSD command automatically
100
107
  - **[`gsd:execute-phase`](references/commands/execute-phase.md)**: Execute all plans in a phase with wave-based parallelization
101
108
  - **[`gsd:fast`](references/commands/fast.md)**: Execute a trivial task inline — no subagents, no planning overhead
109
+ - **[`gsd:forensics`](references/commands/forensics.md)**: Post-mortem investigation for failed GSD workflows — analyzes git history, artifacts, and state to diagnose what went wrong
102
110
  - **[`gsd:gsd-tools`](references/commands/gsd-tools.md)**: Direct access to GSD internal CLI tools for atomic operations (state, roadmap, phase, config, etc.)
103
111
  - **[`gsd:health`](references/commands/health.md)**: Diagnose planning directory health and optionally repair issues
104
112
  - **[`gsd:help`](references/commands/help.md)**: Show available GSD commands and usage guide
105
113
  - **[`gsd:insert-phase`](references/commands/insert-phase.md)**: Insert urgent work as decimal phase (e.g., 72.1) between existing phases
106
114
  - **[`gsd:join-discord`](references/commands/join-discord.md)**: Join the GSD Discord community
107
115
  - **[`gsd:list-phase-assumptions`](references/commands/list-phase-assumptions.md)**: Surface Antigravity's assumptions about a phase approach before planning
116
+ - **[`gsd:list-workspaces`](references/commands/list-workspaces.md)**: List active GSD workspaces and their status
117
+ - **[`gsd:manager`](references/commands/manager.md)**: Interactive command center for managing multiple phases from one terminal
108
118
  - **[`gsd:map-codebase`](references/commands/map-codebase.md)**: Analyze codebase with parallel mapper agents to produce .planning/codebase/ documents
119
+ - **[`gsd:milestone-summary`](references/commands/milestone-summary.md)**: Generate a comprehensive project summary from milestone artifacts for team onboarding and review
109
120
  - **[`gsd:new-milestone`](references/commands/new-milestone.md)**: Start a new milestone cycle — update PROJECT.md and route to requirements
110
121
  - **[`gsd:new-project`](references/commands/new-project.md)**: Initialize a new project with deep context gathering and PROJECT.md
122
+ - **[`gsd:new-workspace`](references/commands/new-workspace.md)**: Create an isolated workspace with repo copies and independent .planning/
111
123
  - **[`gsd:next`](references/commands/next.md)**: Automatically advance to the next logical step in the GSD workflow
112
124
  - **[`gsd:note`](references/commands/note.md)**: Zero-friction idea capture. Append, list, or promote notes to todos.
113
125
  - **[`gsd:pause-work`](references/commands/pause-work.md)**: Create context handoff when pausing work mid-phase
@@ -120,6 +132,7 @@ The following slash commands are available in this skill. Use them to drive the
120
132
  - **[`gsd:quick`](references/commands/quick.md)**: Execute a quick task with GSD guarantees (atomic commits, state tracking) but skip optional agents
121
133
  - **[`gsd:reapply-patches`](references/commands/reapply-patches.md)**: Reapply local modifications after a GSD update
122
134
  - **[`gsd:remove-phase`](references/commands/remove-phase.md)**: Remove a future phase from roadmap and renumber subsequent phases
135
+ - **[`gsd:remove-workspace`](references/commands/remove-workspace.md)**: Remove a GSD workspace and clean up worktrees
123
136
  - **[`gsd:research-phase`](references/commands/research-phase.md)**: Research how to implement a phase (standalone - usually use /gsd:plan-phase instead)
124
137
  - **[`gsd:resume-work`](references/commands/resume-work.md)**: Resume work from previous session with full context restoration
125
138
  - **[`gsd:review-backlog`](references/commands/review-backlog.md)**: Review and promote backlog items to active milestone
@@ -135,6 +148,7 @@ The following slash commands are available in this skill. Use them to drive the
135
148
  - **[`gsd:update`](references/commands/update.md)**: Update GSD to latest version with changelog display
136
149
  - **[`gsd:validate-phase`](references/commands/validate-phase.md)**: Retroactively audit and fill Nyquist validation gaps for a completed phase
137
150
  - **[`gsd:verify-work`](references/commands/verify-work.md)**: Validate built features through conversational UAT
151
+ - **[`gsd:workstreams`](references/commands/workstreams.md)**: Manage parallel workstreams — list, create, switch, status, progress, complete, and resume
138
152
 
139
153
  ### 3. Directory Structure
140
154
  The skill uses a standardized directory structure for portability and organization:
@@ -169,4 +183,4 @@ General documentation on the GSD philosophy, usage patterns, and configuration.
169
183
  5. **CLI Invocation**: `gsd-tools` is **NOT** a global command. Always invoke it with the full node path: `node .agent/skills/gsd/bin/gsd-tools.cjs <command> [args]`. Never run `gsd-tools` bare.
170
184
 
171
185
  ---
172
- *Generated by gsd-converter on 2026-03-21*
186
+ *Generated by gsd-converter on 2026-03-22*
@@ -6,7 +6,9 @@
6
6
  "plan_check": true,
7
7
  "verifier": true,
8
8
  "auto_advance": false,
9
- "nyquist_validation": true
9
+ "nyquist_validation": true,
10
+ "discuss_mode": "discuss",
11
+ "research_before_questions": false
10
12
  },
11
13
  "planning": {
12
14
  "commit_docs": true,
@@ -33,8 +33,9 @@ function showHelp(cmd, sub) {
33
33
 
34
34
  const fs = require('fs');
35
35
  const path = require('path');
36
+ const core = require('./lib/core.cjs');
36
37
  const { parseIncludeFlag } = require('./lib/core.cjs');
37
- const { error, findProjectRoot } = require('./lib/core.cjs');
38
+ const { error, findProjectRoot, getActiveWorkstream } = core;
38
39
  const state = require('./lib/state.cjs');
39
40
  const phase = require('./lib/phase.cjs');
40
41
  const roadmap = require('./lib/roadmap.cjs');
@@ -47,6 +48,47 @@ const init = require('./lib/init.cjs');
47
48
  const frontmatter = require('./lib/frontmatter.cjs');
48
49
  const profilePipeline = require('./lib/profile-pipeline.cjs');
49
50
  const profileOutput = require('./lib/profile-output.cjs');
51
+ const workstream = require('./lib/workstream.cjs');
52
+
53
+ // ─── Arg parsing helpers ──────────────────────────────────────────────────────
54
+
55
+ /**
56
+ * Extract named --flag <value> pairs from an args array.
57
+ * Returns an object mapping flag names to their values (null if absent).
58
+ * Flags listed in `booleanFlags` are treated as boolean (no value consumed).
59
+ *
60
+ * parseNamedArgs(args, 'phase', 'plan') → { phase: '3', plan: '1' }
61
+ * parseNamedArgs(args, [], ['amend', 'force']) → { amend: true, force: false }
62
+ */
63
+ function parseNamedArgs(args, valueFlags = [], booleanFlags = []) {
64
+ const result = {};
65
+ for (const flag of valueFlags) {
66
+ const idx = args.indexOf(`--${flag}`);
67
+ result[flag] = idx !== -1 && args[idx + 1] !== undefined && !args[idx + 1].startsWith('--')
68
+ ? args[idx + 1]
69
+ : null;
70
+ }
71
+ for (const flag of booleanFlags) {
72
+ result[flag] = args.includes(`--${flag}`);
73
+ }
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * Collect all tokens after --flag until the next --flag or end of args.
79
+ * Handles multi-word values like --name Foo Bar Version 1.
80
+ * Returns null if the flag is absent.
81
+ */
82
+ function parseMultiwordArg(args, flag) {
83
+ const idx = args.indexOf(`--${flag}`);
84
+ if (idx === -1) return null;
85
+ const tokens = [];
86
+ for (let i = idx + 1; i < args.length; i++) {
87
+ if (args[i].startsWith('--')) break;
88
+ tokens.push(args[i]);
89
+ }
90
+ return tokens.length > 0 ? tokens.join(' ') : null;
91
+ }
50
92
 
51
93
  // ─── CLI Router ───────────────────────────────────────────────────────────────
52
94
 
@@ -73,11 +115,42 @@ async function main() {
73
115
  error(`Invalid --cwd: ${cwd}`);
74
116
  }
75
117
 
76
- // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree
118
+ // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
119
+ // However, in monorepo worktrees where the subdirectory itself owns .planning/,
120
+ // skip worktree resolution — the CWD is already the correct project root.
77
121
  const { resolveWorktreeRoot } = require('./lib/core.cjs');
122
+ if (!fs.existsSync(path.join(cwd, '.planning'))) {
78
123
  const worktreeRoot = resolveWorktreeRoot(cwd);
79
124
  if (worktreeRoot !== cwd) {
80
- cwd = worktreeRoot;
125
+ cwd = worktreeRoot;
126
+ }
127
+ }
128
+
129
+ // Optional workstream override for parallel milestone work.
130
+ // Priority: --ws flag > GSD_WORKSTREAM env var > active-workstream file > null (flat mode)
131
+ const wsEqArg = args.find(arg => arg.startsWith('--ws='));
132
+ const wsIdx = args.indexOf('--ws');
133
+ let ws = null;
134
+ if (wsEqArg) {
135
+ ws = wsEqArg.slice('--ws='.length).trim();
136
+ if (!ws) error('Missing value for --ws');
137
+ args.splice(args.indexOf(wsEqArg), 1);
138
+ } else if (wsIdx !== -1) {
139
+ ws = args[wsIdx + 1];
140
+ if (!ws || ws.startsWith('--')) error('Missing value for --ws');
141
+ args.splice(wsIdx, 2);
142
+ } else if (process.env.GSD_WORKSTREAM) {
143
+ ws = process.env.GSD_WORKSTREAM.trim();
144
+ } else {
145
+ ws = getActiveWorkstream(cwd);
146
+ }
147
+ // Validate workstream name to prevent path traversal attacks.
148
+ if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
149
+ error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
150
+ }
151
+ // Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
152
+ if (ws) {
153
+ process.env.GSD_WORKSTREAM = ws;
81
154
  }
82
155
 
83
156
  const rawIndex = args.indexOf('--raw');
@@ -96,10 +169,21 @@ async function main() {
96
169
  }
97
170
  if (rawIndex !== -1) args.splice(rawIndex, 1);
98
171
 
172
+ // --pick <name>: extract a single field from JSON output (replaces jq dependency).
173
+ // Supports dot-notation (e.g., --pick workflow.research) and bracket notation
174
+ // for arrays (e.g., --pick directories[-1]).
175
+ const pickIdx = args.indexOf('--pick');
176
+ let pickField = null;
177
+ if (pickIdx !== -1) {
178
+ pickField = args[pickIdx + 1];
179
+ if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
180
+ args.splice(pickIdx, 2);
181
+ }
182
+
99
183
  const command = args[0];
100
184
 
101
185
  if (!command) {
102
- error('Usage: node .agent/skills/gsd/bin/gsd-tools.cjs <command> [args] [--raw] [--cwd <path>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init');
186
+ error('Usage: node .agent/skills/gsd/bin/gsd-tools.cjs <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init, workstream');
103
187
  }
104
188
 
105
189
  // Multi-repo guard: resolve project root for commands that read/write .planning/.
@@ -113,6 +197,67 @@ async function main() {
113
197
  cwd = findProjectRoot(cwd);
114
198
  }
115
199
 
200
+ // When --pick is active, intercept stdout to extract the requested field.
201
+ if (pickField) {
202
+ const origWriteSync = fs.writeSync;
203
+ const chunks = [];
204
+ fs.writeSync = function (fd, data, ...rest) {
205
+ if (fd === 1) { chunks.push(String(data)); return; }
206
+ return origWriteSync.call(fs, fd, data, ...rest);
207
+ };
208
+ const cleanup = () => {
209
+ fs.writeSync = origWriteSync;
210
+ const captured = chunks.join('');
211
+ let jsonStr = captured;
212
+ if (jsonStr.startsWith('@file:')) {
213
+ jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
214
+ }
215
+ try {
216
+ const obj = JSON.parse(jsonStr);
217
+ const value = extractField(obj, pickField);
218
+ const result = value === null || value === undefined ? '' : String(value);
219
+ origWriteSync.call(fs, 1, result);
220
+ } catch {
221
+ origWriteSync.call(fs, 1, captured);
222
+ }
223
+ };
224
+ try {
225
+ await runCommand(command, args, cwd, raw);
226
+ cleanup();
227
+ } catch (e) {
228
+ fs.writeSync = origWriteSync;
229
+ throw e;
230
+ }
231
+ return;
232
+ }
233
+
234
+ await runCommand(command, args, cwd, raw);
235
+ }
236
+
237
+ /**
238
+ * Extract a field from an object using dot-notation and bracket syntax.
239
+ * Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
240
+ */
241
+ function extractField(obj, fieldPath) {
242
+ const parts = fieldPath.split('.');
243
+ let current = obj;
244
+ for (const part of parts) {
245
+ if (current === null || current === undefined) return undefined;
246
+ const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
247
+ if (bracketMatch) {
248
+ const key = bracketMatch[1];
249
+ const index = parseInt(bracketMatch[2], 10);
250
+ current = current[key];
251
+ if (!Array.isArray(current)) return undefined;
252
+ current = index < 0 ? current[current.length + index] : current[index];
253
+ } else {
254
+ current = current[part];
255
+ }
256
+ }
257
+ return current;
258
+ }
259
+
260
+ async function runCommand(command, args, cwd, raw) {
116
261
  switch (command) {
117
262
  case 'state': {
118
263
  const subcommand = args[1];
@@ -135,74 +280,27 @@ async function main() {
135
280
  } else if (subcommand === 'advance-plan') {
136
281
  state.cmdStateAdvancePlan(cwd, raw);
137
282
  } else if (subcommand === 'record-metric') {
138
- const phaseIdx = args.indexOf('--phase');
139
- const planIdx = args.indexOf('--plan');
140
- const durationIdx = args.indexOf('--duration');
141
- const tasksIdx = args.indexOf('--tasks');
142
- const filesIdx = args.indexOf('--files');
143
- state.cmdStateRecordMetric(cwd, {
144
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
145
- plan: planIdx !== -1 ? args[planIdx + 1] : null,
146
- duration: durationIdx !== -1 ? args[durationIdx + 1] : null,
147
- tasks: tasksIdx !== -1 ? args[tasksIdx + 1] : null,
148
- files: filesIdx !== -1 ? args[filesIdx + 1] : null,
149
- }, raw);
283
+ const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
284
+ state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
150
285
  } else if (subcommand === 'update-progress') {
151
286
  state.cmdStateUpdateProgress(cwd, raw);
152
287
  } else if (subcommand === 'add-decision') {
153
- const phaseIdx = args.indexOf('--phase');
154
- const summaryIdx = args.indexOf('--summary');
155
- const summaryFileIdx = args.indexOf('--summary-file');
156
- const rationaleIdx = args.indexOf('--rationale');
157
- const rationaleFileIdx = args.indexOf('--rationale-file');
158
- state.cmdStateAddDecision(cwd, {
159
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
160
- summary: summaryIdx !== -1 ? args[summaryIdx + 1] : null,
161
- summary_file: summaryFileIdx !== -1 ? args[summaryFileIdx + 1] : null,
162
- rationale: rationaleIdx !== -1 ? args[rationaleIdx + 1] : '',
163
- rationale_file: rationaleFileIdx !== -1 ? args[rationaleFileIdx + 1] : null,
164
- }, raw);
288
+ const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
289
+ state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
165
290
  } else if (subcommand === 'add-blocker') {
166
- const textIdx = args.indexOf('--text');
167
- const textFileIdx = args.indexOf('--text-file');
168
- state.cmdStateAddBlocker(cwd, {
169
- text: textIdx !== -1 ? args[textIdx + 1] : null,
170
- text_file: textFileIdx !== -1 ? args[textFileIdx + 1] : null,
171
- }, raw);
291
+ const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
292
+ state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
172
293
  } else if (subcommand === 'resolve-blocker') {
173
- const textIdx = args.indexOf('--text');
174
- state.cmdStateResolveBlocker(cwd, textIdx !== -1 ? args[textIdx + 1] : null, raw);
294
+ state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
175
295
  } else if (subcommand === 'record-session') {
176
- const stoppedIdx = args.indexOf('--stopped-at');
177
- const resumeIdx = args.indexOf('--resume-file');
178
- state.cmdStateRecordSession(cwd, {
179
- stopped_at: stoppedIdx !== -1 ? args[stoppedIdx + 1] : null,
180
- resume_file: resumeIdx !== -1 ? args[resumeIdx + 1] : 'None',
181
- }, raw);
296
+ const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
297
+ state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
182
298
  } else if (subcommand === 'begin-phase') {
183
- const phaseIdx = args.indexOf('--phase');
184
- const nameIdx = args.indexOf('--name');
185
- const plansIdx = args.indexOf('--plans');
186
- state.cmdStateBeginPhase(
187
- cwd,
188
- phaseIdx !== -1 ? args[phaseIdx + 1] : null,
189
- nameIdx !== -1 ? args[nameIdx + 1] : null,
190
- plansIdx !== -1 ? parseInt(args[plansIdx + 1], 10) : null,
191
- raw
192
- );
299
+ const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
300
+ state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
193
301
  } else if (subcommand === 'signal-waiting') {
194
- const typeIdx = args.indexOf('--type');
195
- const qIdx = args.indexOf('--question');
196
- const optIdx = args.indexOf('--options');
197
- const phaseIdx = args.indexOf('--phase');
198
- state.cmdSignalWaiting(
199
- cwd,
200
- typeIdx !== -1 ? args[typeIdx + 1] : null,
201
- qIdx !== -1 ? args[qIdx + 1] : null,
202
- optIdx !== -1 ? args[optIdx + 1] : null,
203
- phaseIdx !== -1 ? args[phaseIdx + 1] : null,
204
- raw
205
- );
302
+ const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
303
+ state.cmdSignalWaiting(cwd, type, question, options, p, raw);
206
304
  } else if (subcommand === 'signal-resume') {
207
305
  state.cmdSignalResume(cwd, raw);
208
306
  } else {
@@ -258,24 +356,18 @@ async function main() {
258
356
  template.cmdTemplateSelect(cwd, args[2], raw);
259
357
  } else if (subcommand === 'fill') {
260
358
  const templateType = args[2];
261
- const phaseIdx = args.indexOf('--phase');
262
- const planIdx = args.indexOf('--plan');
263
- const nameIdx = args.indexOf('--name');
264
- const typeIdx = args.indexOf('--type');
265
- const waveIdx = args.indexOf('--wave');
266
- const fieldsIdx = args.indexOf('--fields');
267
- template.cmdTemplateFill(cwd, templateType, {
268
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
269
- plan: planIdx !== -1 ? args[planIdx + 1] : null,
270
- name: nameIdx !== -1 ? args[nameIdx + 1] : null,
271
- type: typeIdx !== -1 ? args[typeIdx + 1] : 'execute',
272
- wave: waveIdx !== -1 ? args[waveIdx + 1] : '1',
273
- fields: fieldsIdx !== -1 ? (() => {
359
+ const { phase, plan, name, type, wave, fields: fieldsRaw } = parseNamedArgs(args, ['phase', 'plan', 'name', 'type', 'wave', 'fields']);
360
+ let fields = {};
361
+ if (fieldsRaw) {
274
362
  const { safeJsonParse } = require('./lib/security.cjs');
275
- const result = safeJsonParse(args[fieldsIdx + 1], { label: '--fields' });
363
+ const result = safeJsonParse(fieldsRaw, { label: '--fields' });
276
364
  if (!result.ok) error(result.error);
277
- return result.value;
278
- })() : {},
365
+ fields = result.value;
366
+ }
367
+ template.cmdTemplateFill(cwd, templateType, {
368
+ phase, plan, name, fields,
369
+ type: type || 'execute',
370
+ wave: wave || '1',
279
371
  }, raw);
280
372
  } else {
281
373
  error('Unknown template subcommand. Available: select, fill');
@@ -287,18 +379,14 @@ async function main() {
287
379
  const subcommand = args[1];
288
380
  const file = args[2];
289
381
  if (subcommand === 'get') {
290
- const fieldIdx = args.indexOf('--field');
291
- frontmatter.cmdFrontmatterGet(cwd, file, fieldIdx !== -1 ? args[fieldIdx + 1] : null, raw);
382
+ frontmatter.cmdFrontmatterGet(cwd, file, parseNamedArgs(args, ['field']).field, raw);
292
383
  } else if (subcommand === 'set') {
293
- const fieldIdx = args.indexOf('--field');
294
- const valueIdx = args.indexOf('--value');
295
- frontmatter.cmdFrontmatterSet(cwd, file, fieldIdx !== -1 ? args[fieldIdx + 1] : null, valueIdx !== -1 ? args[valueIdx + 1] : undefined, raw);
384
+ const { field, value } = parseNamedArgs(args, ['field', 'value']);
385
+ frontmatter.cmdFrontmatterSet(cwd, file, field, value !== null ? value : undefined, raw);
296
386
  } else if (subcommand === 'merge') {
297
- const dataIdx = args.indexOf('--data');
298
- frontmatter.cmdFrontmatterMerge(cwd, file, dataIdx !== -1 ? args[dataIdx + 1] : null, raw);
387
+ frontmatter.cmdFrontmatterMerge(cwd, file, parseNamedArgs(args, ['data']).data, raw);
299
388
  } else if (subcommand === 'validate') {
300
- const schemaIdx = args.indexOf('--schema');
301
- frontmatter.cmdFrontmatterValidate(cwd, file, schemaIdx !== -1 ? args[schemaIdx + 1] : null, raw);
389
+ frontmatter.cmdFrontmatterValidate(cwd, file, parseNamedArgs(args, ['schema']).schema, raw);
302
390
  } else {
303
391
  error('Unknown frontmatter subcommand. Available: get, set, merge, validate');
304
392
  }
@@ -449,18 +537,8 @@ async function main() {
449
537
  case 'milestone': {
450
538
  const subcommand = args[1];
451
539
  if (subcommand === 'complete') {
452
- const nameIndex = args.indexOf('--name');
540
+ const milestoneName = parseMultiwordArg(args, 'name');
453
541
  const archivePhases = args.includes('--archive-phases');
454
- // Collect --name value (everything after --name until next flag or end)
455
- let milestoneName = null;
456
- if (nameIndex !== -1) {
457
- const nameArgs = [];
458
- for (let i = nameIndex + 1; i < args.length; i++) {
459
- if (args[i].startsWith('--')) break;
460
- nameArgs.push(args[i]);
461
- }
462
- milestoneName = nameArgs.join(' ') || null;
463
- }
464
542
  milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases }, raw);
465
543
  } else {
466
544
  error('Unknown milestone subcommand. Available: complete');
@@ -513,11 +591,9 @@ async function main() {
513
591
 
514
592
  case 'scaffold': {
515
593
  const scaffoldType = args[1];
516
- const phaseIndex = args.indexOf('--phase');
517
- const nameIndex = args.indexOf('--name');
518
594
  const scaffoldOptions = {
519
- phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
520
- name: nameIndex !== -1 ? args.slice(nameIndex + 1).join(' ') : null,
595
+ phase: parseNamedArgs(args, ['phase']).phase,
596
+ name: parseMultiwordArg(args, 'name'),
521
597
  };
522
598
  commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
523
599
  break;
@@ -563,8 +639,20 @@ async function main() {
563
639
  case 'progress':
564
640
  init.cmdInitProgress(cwd, includes, raw);
565
641
  break;
642
+ case 'manager':
643
+ init.cmdInitManager(cwd, raw);
644
+ break;
645
+ case 'new-workspace':
646
+ init.cmdInitNewWorkspace(cwd, raw);
647
+ break;
648
+ case 'list-workspaces':
649
+ init.cmdInitListWorkspaces(cwd, raw);
650
+ break;
651
+ case 'remove-workspace':
652
+ init.cmdInitRemoveWorkspace(cwd, args[2], raw);
653
+ break;
566
654
  default:
567
- error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress`);
655
+ error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress, manager, new-workspace, list-workspaces, remove-workspace`);
568
656
  }
569
657
  break;
570
658
  }
@@ -686,6 +774,33 @@ async function main() {
686
774
  break;
687
775
  }
688
776
 
777
+ case 'workstream': {
778
+ const subcommand = args[1];
779
+ if (subcommand === 'create') {
780
+ const migrateNameIdx = args.indexOf('--migrate-name');
781
+ const noMigrate = args.includes('--no-migrate');
782
+ workstream.cmdWorkstreamCreate(cwd, args[2], {
783
+ migrate: !noMigrate,
784
+ migrateName: migrateNameIdx !== -1 ? args[migrateNameIdx + 1] : null,
785
+ }, raw);
786
+ } else if (subcommand === 'list') {
787
+ workstream.cmdWorkstreamList(cwd, raw);
788
+ } else if (subcommand === 'status') {
789
+ workstream.cmdWorkstreamStatus(cwd, args[2], raw);
790
+ } else if (subcommand === 'complete') {
791
+ workstream.cmdWorkstreamComplete(cwd, args[2], {}, raw);
792
+ } else if (subcommand === 'set') {
793
+ workstream.cmdWorkstreamSet(cwd, args[2], raw);
794
+ } else if (subcommand === 'get') {
795
+ workstream.cmdWorkstreamGet(cwd, raw);
796
+ } else if (subcommand === 'progress') {
797
+ workstream.cmdWorkstreamProgress(cwd, raw);
798
+ } else {
799
+ error('Unknown workstream subcommand. Available: create, list, status, complete, set, get, progress');
800
+ }
801
+ break;
802
+ }
803
+
689
804
  default:
690
805
  error(`Unknown command: ${command}`);
691
806
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.27.0",
2
+ "version": "1.28.0",
3
3
  "commands": {
4
4
  "add-backlog": {
5
5
  "description": "Add an idea to the backlog parking lot (999.x numbering)"
@@ -46,6 +46,9 @@
46
46
  "fast": {
47
47
  "description": "Execute a trivial task inline \u2014 no subagents, no planning overhead"
48
48
  },
49
+ "forensics": {
50
+ "description": "Post-mortem investigation for failed GSD workflows \u2014 analyzes git history, artifacts, and state to diagnose what went wrong"
51
+ },
49
52
  "health": {
50
53
  "description": "Diagnose planning directory health and optionally repair issues"
51
54
  },
@@ -61,15 +64,27 @@
61
64
  "list-phase-assumptions": {
62
65
  "description": "Surface Antigravity's assumptions about a phase approach before planning"
63
66
  },
67
+ "list-workspaces": {
68
+ "description": "List active GSD workspaces and their status"
69
+ },
70
+ "manager": {
71
+ "description": "Interactive command center for managing multiple phases from one terminal"
72
+ },
64
73
  "map-codebase": {
65
74
  "description": "Analyze codebase with parallel mapper agents to produce .planning/codebase/ documents"
66
75
  },
76
+ "milestone-summary": {
77
+ "description": "Generate a comprehensive project summary from milestone artifacts for team onboarding and review"
78
+ },
67
79
  "new-milestone": {
68
80
  "description": "Start a new milestone cycle \u2014 update PROJECT.md and route to requirements"
69
81
  },
70
82
  "new-project": {
71
83
  "description": "Initialize a new project with deep context gathering and PROJECT.md"
72
84
  },
85
+ "new-workspace": {
86
+ "description": "Create an isolated workspace with repo copies and independent .planning/"
87
+ },
73
88
  "next": {
74
89
  "description": "Automatically advance to the next logical step in the GSD workflow"
75
90
  },
@@ -110,6 +125,9 @@
110
125
  "remove-phase": {
111
126
  "description": "Remove a future phase from roadmap and renumber subsequent phases"
112
127
  },
128
+ "remove-workspace": {
129
+ "description": "Remove a GSD workspace and clean up worktrees"
130
+ },
113
131
  "research-phase": {
114
132
  "description": "Research how to implement a phase (standalone - usually use /gsd:plan-phase instead)"
115
133
  },
@@ -155,6 +173,9 @@
155
173
  "verify-work": {
156
174
  "description": "Validate built features through conversational UAT"
157
175
  },
176
+ "workstreams": {
177
+ "description": "Manage parallel workstreams \u2014 list, create, switch, status, progress, complete, and resume"
178
+ },
158
179
  "state": {
159
180
  "description": "Manage and query project state memory.",
160
181
  "subcommands": {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // gsd-hook-version: 1.27.0
2
+ // gsd-hook-version: 1.28.0
3
3
  // Check for GSD updates in background, write result to cache
4
4
  // Called by SessionStart hook - runs once per session
5
5
 
@@ -65,9 +65,10 @@ const child = spawn(process.execPath, ['-e', `
65
65
  } catch (e) {}
66
66
 
67
67
  // Check for stale hooks — compare hook version headers against installed VERSION
68
+ // Hooks live inside get-shit-done/hooks/, not configDir/hooks/
68
69
  let staleHooks = [];
69
70
  if (configDir) {
70
- const hooksDir = path.join(configDir, 'hooks');
71
+ const hooksDir = path.join(configDir, 'get-shit-done', 'hooks');
71
72
  try {
72
73
  if (fs.existsSync(hooksDir)) {
73
74
  const hookFiles = fs.readdirSync(hooksDir).filter(f => f.startsWith('gsd-') && f.endsWith('.js'));
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // gsd-hook-version: 1.27.0
2
+ // gsd-hook-version: 1.28.0
3
3
  // Context Monitor - PostToolUse/AfterTool hook (Gemini uses AfterTool)
4
4
  // Reads context metrics from the statusline bridge file and injects
5
5
  // warnings when context usage is high. This makes the AGENT aware of
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // gsd-hook-version: 1.27.0
2
+ // gsd-hook-version: 1.28.0
3
3
  // GSD Prompt Injection Guard — PreToolUse hook
4
4
  // Scans file content being written to .planning/ for prompt injection patterns.
5
5
  // Defense-in-depth: catches injected instructions before they enter agent context.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // gsd-hook-version: 1.27.0
2
+ // gsd-hook-version: 1.28.0
3
3
  // Antigravity Statusline - GSD Edition
4
4
  // Shows: model | current task | directory | context usage
5
5
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // gsd-hook-version: 1.27.0
2
+ // gsd-hook-version: 1.28.0
3
3
  // GSD Workflow Guard — PreToolUse hook
4
4
  // Detects when Antigravity attempts file edits outside a GSD workflow context
5
5
  // (no active /gsd: command or Task subagent) and injects an advisory warning.