gsd-opencode 1.22.1 → 1.33.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.
Files changed (188) hide show
  1. package/agents/gsd-advisor-researcher.md +112 -0
  2. package/agents/gsd-assumptions-analyzer.md +110 -0
  3. package/agents/gsd-codebase-mapper.md +0 -2
  4. package/agents/gsd-debugger.md +117 -2
  5. package/agents/gsd-doc-verifier.md +207 -0
  6. package/agents/gsd-doc-writer.md +608 -0
  7. package/agents/gsd-executor.md +45 -4
  8. package/agents/gsd-integration-checker.md +0 -2
  9. package/agents/gsd-nyquist-auditor.md +0 -2
  10. package/agents/gsd-phase-researcher.md +191 -5
  11. package/agents/gsd-plan-checker.md +152 -5
  12. package/agents/gsd-planner.md +131 -157
  13. package/agents/gsd-project-researcher.md +28 -3
  14. package/agents/gsd-research-synthesizer.md +0 -2
  15. package/agents/gsd-roadmapper.md +29 -2
  16. package/agents/gsd-security-auditor.md +129 -0
  17. package/agents/gsd-ui-auditor.md +485 -0
  18. package/agents/gsd-ui-checker.md +305 -0
  19. package/agents/gsd-ui-researcher.md +368 -0
  20. package/agents/gsd-user-profiler.md +173 -0
  21. package/agents/gsd-verifier.md +207 -22
  22. package/commands/gsd/gsd-add-backlog.md +76 -0
  23. package/commands/gsd/gsd-analyze-dependencies.md +34 -0
  24. package/commands/gsd/gsd-audit-uat.md +24 -0
  25. package/commands/gsd/gsd-autonomous.md +45 -0
  26. package/commands/gsd/gsd-cleanup.md +5 -0
  27. package/commands/gsd/gsd-debug.md +29 -21
  28. package/commands/gsd/gsd-discuss-phase.md +15 -36
  29. package/commands/gsd/gsd-do.md +30 -0
  30. package/commands/gsd/gsd-docs-update.md +48 -0
  31. package/commands/gsd/gsd-execute-phase.md +24 -2
  32. package/commands/gsd/gsd-fast.md +30 -0
  33. package/commands/gsd/gsd-forensics.md +56 -0
  34. package/commands/gsd/gsd-help.md +2 -0
  35. package/commands/gsd/gsd-join-discord.md +2 -1
  36. package/commands/gsd/gsd-list-workspaces.md +19 -0
  37. package/commands/gsd/gsd-manager.md +40 -0
  38. package/commands/gsd/gsd-milestone-summary.md +51 -0
  39. package/commands/gsd/gsd-new-project.md +4 -0
  40. package/commands/gsd/gsd-new-workspace.md +44 -0
  41. package/commands/gsd/gsd-next.md +24 -0
  42. package/commands/gsd/gsd-note.md +34 -0
  43. package/commands/gsd/gsd-plan-phase.md +8 -1
  44. package/commands/gsd/gsd-plant-seed.md +28 -0
  45. package/commands/gsd/gsd-pr-branch.md +25 -0
  46. package/commands/gsd/gsd-profile-user.md +46 -0
  47. package/commands/gsd/gsd-quick.md +7 -3
  48. package/commands/gsd/gsd-reapply-patches.md +178 -45
  49. package/commands/gsd/gsd-remove-workspace.md +26 -0
  50. package/commands/gsd/gsd-research-phase.md +7 -12
  51. package/commands/gsd/gsd-review-backlog.md +62 -0
  52. package/commands/gsd/gsd-review.md +38 -0
  53. package/commands/gsd/gsd-secure-phase.md +35 -0
  54. package/commands/gsd/gsd-session-report.md +19 -0
  55. package/commands/gsd/gsd-set-profile.md +24 -23
  56. package/commands/gsd/gsd-ship.md +23 -0
  57. package/commands/gsd/gsd-stats.md +18 -0
  58. package/commands/gsd/gsd-thread.md +127 -0
  59. package/commands/gsd/gsd-ui-phase.md +34 -0
  60. package/commands/gsd/gsd-ui-review.md +32 -0
  61. package/commands/gsd/gsd-workstreams.md +71 -0
  62. package/get-shit-done/bin/gsd-tools.cjs +450 -90
  63. package/get-shit-done/bin/lib/commands.cjs +489 -24
  64. package/get-shit-done/bin/lib/config.cjs +329 -48
  65. package/get-shit-done/bin/lib/core.cjs +1143 -102
  66. package/get-shit-done/bin/lib/docs.cjs +267 -0
  67. package/get-shit-done/bin/lib/frontmatter.cjs +125 -43
  68. package/get-shit-done/bin/lib/init.cjs +918 -106
  69. package/get-shit-done/bin/lib/milestone.cjs +65 -33
  70. package/get-shit-done/bin/lib/model-profiles.cjs +70 -0
  71. package/get-shit-done/bin/lib/phase.cjs +434 -404
  72. package/get-shit-done/bin/lib/profile-output.cjs +1048 -0
  73. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  74. package/get-shit-done/bin/lib/roadmap.cjs +156 -101
  75. package/get-shit-done/bin/lib/schema-detect.cjs +238 -0
  76. package/get-shit-done/bin/lib/security.cjs +384 -0
  77. package/get-shit-done/bin/lib/state.cjs +711 -79
  78. package/get-shit-done/bin/lib/template.cjs +2 -2
  79. package/get-shit-done/bin/lib/uat.cjs +282 -0
  80. package/get-shit-done/bin/lib/verify.cjs +254 -42
  81. package/get-shit-done/bin/lib/workstream.cjs +495 -0
  82. package/get-shit-done/references/agent-contracts.md +79 -0
  83. package/get-shit-done/references/artifact-types.md +113 -0
  84. package/get-shit-done/references/checkpoints.md +12 -10
  85. package/get-shit-done/references/context-budget.md +49 -0
  86. package/get-shit-done/references/continuation-format.md +15 -15
  87. package/get-shit-done/references/decimal-phase-calculation.md +2 -3
  88. package/get-shit-done/references/domain-probes.md +125 -0
  89. package/get-shit-done/references/gate-prompts.md +100 -0
  90. package/get-shit-done/references/git-integration.md +47 -0
  91. package/get-shit-done/references/model-profile-resolution.md +2 -0
  92. package/get-shit-done/references/model-profiles.md +62 -16
  93. package/get-shit-done/references/phase-argument-parsing.md +2 -2
  94. package/get-shit-done/references/planner-gap-closure.md +62 -0
  95. package/get-shit-done/references/planner-reviews.md +39 -0
  96. package/get-shit-done/references/planner-revision.md +87 -0
  97. package/get-shit-done/references/planning-config.md +18 -1
  98. package/get-shit-done/references/revision-loop.md +97 -0
  99. package/get-shit-done/references/ui-brand.md +2 -2
  100. package/get-shit-done/references/universal-anti-patterns.md +58 -0
  101. package/get-shit-done/references/user-profiling.md +681 -0
  102. package/get-shit-done/references/workstream-flag.md +111 -0
  103. package/get-shit-done/templates/SECURITY.md +61 -0
  104. package/get-shit-done/templates/UAT.md +21 -3
  105. package/get-shit-done/templates/UI-SPEC.md +100 -0
  106. package/get-shit-done/templates/VALIDATION.md +3 -3
  107. package/get-shit-done/templates/claude-md.md +145 -0
  108. package/get-shit-done/templates/config.json +14 -3
  109. package/get-shit-done/templates/context.md +61 -6
  110. package/get-shit-done/templates/debug-subagent-prompt.md +2 -6
  111. package/get-shit-done/templates/dev-preferences.md +21 -0
  112. package/get-shit-done/templates/discussion-log.md +63 -0
  113. package/get-shit-done/templates/phase-prompt.md +46 -5
  114. package/get-shit-done/templates/planner-subagent-prompt.md +2 -10
  115. package/get-shit-done/templates/project.md +2 -0
  116. package/get-shit-done/templates/state.md +2 -2
  117. package/get-shit-done/templates/user-profile.md +146 -0
  118. package/get-shit-done/workflows/add-phase.md +4 -4
  119. package/get-shit-done/workflows/add-tests.md +4 -4
  120. package/get-shit-done/workflows/add-todo.md +4 -4
  121. package/get-shit-done/workflows/analyze-dependencies.md +96 -0
  122. package/get-shit-done/workflows/audit-milestone.md +20 -16
  123. package/get-shit-done/workflows/audit-uat.md +109 -0
  124. package/get-shit-done/workflows/autonomous.md +1036 -0
  125. package/get-shit-done/workflows/check-todos.md +4 -4
  126. package/get-shit-done/workflows/cleanup.md +4 -4
  127. package/get-shit-done/workflows/complete-milestone.md +22 -10
  128. package/get-shit-done/workflows/diagnose-issues.md +21 -7
  129. package/get-shit-done/workflows/discovery-phase.md +2 -2
  130. package/get-shit-done/workflows/discuss-phase-assumptions.md +671 -0
  131. package/get-shit-done/workflows/discuss-phase-power.md +291 -0
  132. package/get-shit-done/workflows/discuss-phase.md +558 -47
  133. package/get-shit-done/workflows/do.md +104 -0
  134. package/get-shit-done/workflows/docs-update.md +1093 -0
  135. package/get-shit-done/workflows/execute-phase.md +741 -58
  136. package/get-shit-done/workflows/execute-plan.md +77 -12
  137. package/get-shit-done/workflows/fast.md +105 -0
  138. package/get-shit-done/workflows/forensics.md +265 -0
  139. package/get-shit-done/workflows/health.md +28 -6
  140. package/get-shit-done/workflows/help.md +127 -7
  141. package/get-shit-done/workflows/insert-phase.md +4 -4
  142. package/get-shit-done/workflows/list-phase-assumptions.md +2 -2
  143. package/get-shit-done/workflows/list-workspaces.md +56 -0
  144. package/get-shit-done/workflows/manager.md +363 -0
  145. package/get-shit-done/workflows/map-codebase.md +83 -44
  146. package/get-shit-done/workflows/milestone-summary.md +223 -0
  147. package/get-shit-done/workflows/new-milestone.md +133 -25
  148. package/get-shit-done/workflows/new-project.md +216 -54
  149. package/get-shit-done/workflows/new-workspace.md +237 -0
  150. package/get-shit-done/workflows/next.md +97 -0
  151. package/get-shit-done/workflows/node-repair.md +92 -0
  152. package/get-shit-done/workflows/note.md +156 -0
  153. package/get-shit-done/workflows/pause-work.md +132 -15
  154. package/get-shit-done/workflows/plan-milestone-gaps.md +6 -7
  155. package/get-shit-done/workflows/plan-phase.md +513 -62
  156. package/get-shit-done/workflows/plant-seed.md +169 -0
  157. package/get-shit-done/workflows/pr-branch.md +129 -0
  158. package/get-shit-done/workflows/profile-user.md +450 -0
  159. package/get-shit-done/workflows/progress.md +154 -29
  160. package/get-shit-done/workflows/quick.md +285 -111
  161. package/get-shit-done/workflows/remove-phase.md +2 -2
  162. package/get-shit-done/workflows/remove-workspace.md +90 -0
  163. package/get-shit-done/workflows/research-phase.md +13 -9
  164. package/get-shit-done/workflows/resume-project.md +37 -18
  165. package/get-shit-done/workflows/review.md +281 -0
  166. package/get-shit-done/workflows/secure-phase.md +154 -0
  167. package/get-shit-done/workflows/session-report.md +146 -0
  168. package/get-shit-done/workflows/set-profile.md +2 -2
  169. package/get-shit-done/workflows/settings.md +91 -11
  170. package/get-shit-done/workflows/ship.md +237 -0
  171. package/get-shit-done/workflows/stats.md +60 -0
  172. package/get-shit-done/workflows/transition.md +150 -23
  173. package/get-shit-done/workflows/ui-phase.md +292 -0
  174. package/get-shit-done/workflows/ui-review.md +183 -0
  175. package/get-shit-done/workflows/update.md +262 -30
  176. package/get-shit-done/workflows/validate-phase.md +14 -17
  177. package/get-shit-done/workflows/verify-phase.md +143 -11
  178. package/get-shit-done/workflows/verify-work.md +141 -39
  179. package/package.json +1 -1
  180. package/skills/gsd-audit-milestone/SKILL.md +29 -0
  181. package/skills/gsd-cleanup/SKILL.md +19 -0
  182. package/skills/gsd-complete-milestone/SKILL.md +131 -0
  183. package/skills/gsd-discuss-phase/SKILL.md +54 -0
  184. package/skills/gsd-execute-phase/SKILL.md +49 -0
  185. package/skills/gsd-plan-phase/SKILL.md +37 -0
  186. package/skills/gsd-ui-phase/SKILL.md +24 -0
  187. package/skills/gsd-ui-review/SKILL.md +24 -0
  188. package/skills/gsd-verify-work/SKILL.md +30 -0
@@ -6,7 +6,7 @@
6
6
  * Replaces repetitive inline bash patterns across ~50 GSD command/workflow/agent files.
7
7
  * Centralizes: config parsing, model resolution, phase lookup, git commits, summary verification.
8
8
  *
9
- * Usage: node gsd-tools.cjs <command> [args] [--raw]
9
+ * Usage: node gsd-tools.cjs <command> [args] [--raw] [--pick <field>]
10
10
  *
11
11
  * Atomic Commands:
12
12
  * state load Load project config + state
@@ -14,9 +14,13 @@
14
14
  * state update <field> <value> Update a STATE.md field
15
15
  * state get [section] Get STATE.md content or section
16
16
  * state patch --field val ... Batch update STATE.md fields
17
+ * state begin-phase --phase N --name S --plans C Update STATE.md for new phase start
18
+ * state signal-waiting --type T --question Q --options "A|B" --phase P write WAITING.json signal
19
+ * state signal-resume Remove WAITING.json signal
17
20
  * resolve-model <agent-type> Get model for agent based on profile
18
21
  * find-phase <phase> Find phase directory by number
19
- * commit <message> [--files f1 f2] Commit planning docs
22
+ * commit <message> [--files f1 f2] [--no-verify] Commit planning docs
23
+ * commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
20
24
  * verify-summary <path> Verify a SUMMARY.md file
21
25
  * generate-slug <text> Convert text to URL-safe slug
22
26
  * current-timestamp [format] Get timestamp (full|date|filename)
@@ -32,7 +36,7 @@
32
36
  *
33
37
  * Phase Operations:
34
38
  * phase next-decimal <phase> Calculate next decimal phase number
35
- * phase add <description> Append new phase to roadmap + create dir
39
+ * phase add <description> [--id ID] Append new phase to roadmap + create dir
36
40
  * phase insert <after> <description> Insert decimal phase after existing
37
41
  * phase remove <phase> [--force] Remove phase, renumber all subsequent
38
42
  * phase complete <phase> Mark phase done, update state + roadmap
@@ -54,6 +58,7 @@
54
58
  * Validation:
55
59
  * validate consistency Check phase numbering, disk/roadmap sync
56
60
  * validate health [--repair] Check .planning/ integrity, optionally repair
61
+ * validate agents Check GSD agent installation status
57
62
  *
58
63
  * Progress:
59
64
  * progress [json|table|bar] Render progress in various formats
@@ -61,6 +66,10 @@
61
66
  * Todos:
62
67
  * todo complete <filename> Move todo from pending to completed
63
68
  *
69
+ * UAT Audit:
70
+ * audit-uat Scan all phases for unresolved UAT/verification items
71
+ * uat render-checkpoint --file <path> Render the current UAT checkpoint block
72
+ *
64
73
  * Scaffolding:
65
74
  * scaffold context --phase <N> Create CONTEXT.md template
66
75
  * scaffold uat --phase <N> Create UAT.md template
@@ -84,6 +93,7 @@
84
93
  * verify commits <h1> [h2] ... Batch verify commit hashes
85
94
  * verify artifacts <plan-file> Check must_haves.artifacts
86
95
  * verify key-links <plan-file> Check must_haves.key_links
96
+ * verify schema-drift <phase> [--skip] Detect schema file changes without push
87
97
  *
88
98
  * Template Fill:
89
99
  * template fill summary --phase N Create pre-filled SUMMARY.md
@@ -124,11 +134,15 @@
124
134
  * init milestone-op All context for milestone operations
125
135
  * init map-codebase All context for map-codebase workflow
126
136
  * init progress All context for progress workflow
137
+ *
138
+ * Documentation:
139
+ * docs-init Project context for docs-update workflow
127
140
  */
128
141
 
129
142
  const fs = require('fs');
130
143
  const path = require('path');
131
- const { error } = require('./lib/core.cjs');
144
+ const core = require('./lib/core.cjs');
145
+ const { error, findProjectRoot, getActiveWorkstream } = core;
132
146
  const state = require('./lib/state.cjs');
133
147
  const phase = require('./lib/phase.cjs');
134
148
  const roadmap = require('./lib/roadmap.cjs');
@@ -139,6 +153,50 @@ const milestone = require('./lib/milestone.cjs');
139
153
  const commands = require('./lib/commands.cjs');
140
154
  const init = require('./lib/init.cjs');
141
155
  const frontmatter = require('./lib/frontmatter.cjs');
156
+ const profilePipeline = require('./lib/profile-pipeline.cjs');
157
+ const profileOutput = require('./lib/profile-output.cjs');
158
+ const workstream = require('./lib/workstream.cjs');
159
+ const docs = require('./lib/docs.cjs');
160
+
161
+ // ─── Arg parsing helpers ──────────────────────────────────────────────────────
162
+
163
+ /**
164
+ * Extract named --flag <value> pairs from an args array.
165
+ * Returns an object mapping flag names to their values (null if absent).
166
+ * Flags listed in `booleanFlags` are treated as boolean (no value consumed).
167
+ *
168
+ * parseNamedArgs(args, 'phase', 'plan') → { phase: '3', plan: '1' }
169
+ * parseNamedArgs(args, [], ['amend', 'force']) → { amend: true, force: false }
170
+ */
171
+ function parseNamedArgs(args, valueFlags = [], booleanFlags = []) {
172
+ const result = {};
173
+ for (const flag of valueFlags) {
174
+ const idx = args.indexOf(`--${flag}`);
175
+ result[flag] = idx !== -1 && args[idx + 1] !== undefined && !args[idx + 1].startsWith('--')
176
+ ? args[idx + 1]
177
+ : null;
178
+ }
179
+ for (const flag of booleanFlags) {
180
+ result[flag] = args.includes(`--${flag}`);
181
+ }
182
+ return result;
183
+ }
184
+
185
+ /**
186
+ * Collect all tokens after --flag until the next --flag or end of args.
187
+ * Handles multi-word values like --name Foo Bar Version 1.
188
+ * Returns null if the flag is absent.
189
+ */
190
+ function parseMultiwordArg(args, flag) {
191
+ const idx = args.indexOf(`--${flag}`);
192
+ if (idx === -1) return null;
193
+ const tokens = [];
194
+ for (let i = idx + 1; i < args.length; i++) {
195
+ if (args[i].startsWith('--')) break;
196
+ tokens.push(args[i]);
197
+ }
198
+ return tokens.length > 0 ? tokens.join(' ') : null;
199
+ }
142
200
 
143
201
  // ─── CLI Router ───────────────────────────────────────────────────────────────
144
202
 
@@ -165,16 +223,137 @@ async function main() {
165
223
  error(`Invalid --cwd: ${cwd}`);
166
224
  }
167
225
 
226
+ // Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
227
+ // However, in monorepo worktrees where the subdirectory itself owns .planning/,
228
+ // skip worktree resolution — the CWD is already the correct project root.
229
+ const { resolveWorktreeRoot } = require('./lib/core.cjs');
230
+ if (!fs.existsSync(path.join(cwd, '.planning'))) {
231
+ const worktreeRoot = resolveWorktreeRoot(cwd);
232
+ if (worktreeRoot !== cwd) {
233
+ cwd = worktreeRoot;
234
+ }
235
+ }
236
+
237
+ // Optional workstream override for parallel milestone work.
238
+ // Priority: --ws flag > GSD_WORKSTREAM env var > session-scoped pointer > shared legacy pointer > null
239
+ const wsEqArg = args.find(arg => arg.startsWith('--ws='));
240
+ const wsIdx = args.indexOf('--ws');
241
+ let ws = null;
242
+ if (wsEqArg) {
243
+ ws = wsEqArg.slice('--ws='.length).trim();
244
+ if (!ws) error('Missing value for --ws');
245
+ args.splice(args.indexOf(wsEqArg), 1);
246
+ } else if (wsIdx !== -1) {
247
+ ws = args[wsIdx + 1];
248
+ if (!ws || ws.startsWith('--')) error('Missing value for --ws');
249
+ args.splice(wsIdx, 2);
250
+ } else if (process.env.GSD_WORKSTREAM) {
251
+ ws = process.env.GSD_WORKSTREAM.trim();
252
+ } else {
253
+ ws = getActiveWorkstream(cwd);
254
+ }
255
+ // Validate workstream name to prevent path traversal attacks.
256
+ if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
257
+ error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
258
+ }
259
+ // Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
260
+ if (ws) {
261
+ process.env.GSD_WORKSTREAM = ws;
262
+ }
263
+
168
264
  const rawIndex = args.indexOf('--raw');
169
265
  const raw = rawIndex !== -1;
170
266
  if (rawIndex !== -1) args.splice(rawIndex, 1);
171
267
 
268
+ // --pick <name>: extract a single field from JSON output (replaces jq dependency).
269
+ // Supports dot-notation (e.g., --pick workflow.research) and bracket notation
270
+ // for arrays (e.g., --pick directories[-1]).
271
+ const pickIdx = args.indexOf('--pick');
272
+ let pickField = null;
273
+ if (pickIdx !== -1) {
274
+ pickField = args[pickIdx + 1];
275
+ if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
276
+ args.splice(pickIdx, 2);
277
+ }
278
+
172
279
  const command = args[0];
173
280
 
174
281
  if (!command) {
175
- error('Usage: gsd-tools <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, init');
282
+ error('Usage: gsd-tools <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, docs-init');
283
+ }
284
+
285
+ // Multi-repo guard: resolve project root for commands that read/write .planning/.
286
+ // Skip for pure-utility commands that don't touch .planning/ to avoid unnecessary
287
+ // filesystem traversal on every invocation.
288
+ const SKIP_ROOT_RESOLUTION = new Set([
289
+ 'generate-slug', 'current-timestamp', 'verify-path-exists',
290
+ 'verify-summary', 'template', 'frontmatter',
291
+ ]);
292
+ if (!SKIP_ROOT_RESOLUTION.has(command)) {
293
+ cwd = findProjectRoot(cwd);
294
+ }
295
+
296
+ // When --pick is active, intercept stdout to extract the requested field.
297
+ if (pickField) {
298
+ const origWriteSync = fs.writeSync;
299
+ const chunks = [];
300
+ fs.writeSync = function (fd, data, ...rest) {
301
+ if (fd === 1) { chunks.push(String(data)); return; }
302
+ return origWriteSync.call(fs, fd, data, ...rest);
303
+ };
304
+ const cleanup = () => {
305
+ fs.writeSync = origWriteSync;
306
+ const captured = chunks.join('');
307
+ let jsonStr = captured;
308
+ if (jsonStr.startsWith('@file:')) {
309
+ jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
310
+ }
311
+ try {
312
+ const obj = JSON.parse(jsonStr);
313
+ const value = extractField(obj, pickField);
314
+ const result = value === null || value === undefined ? '' : String(value);
315
+ origWriteSync.call(fs, 1, result);
316
+ } catch {
317
+ origWriteSync.call(fs, 1, captured);
318
+ }
319
+ };
320
+ try {
321
+ await runCommand(command, args, cwd, raw);
322
+ cleanup();
323
+ } catch (e) {
324
+ fs.writeSync = origWriteSync;
325
+ throw e;
326
+ }
327
+ return;
328
+ }
329
+
330
+ await runCommand(command, args, cwd, raw);
331
+ }
332
+
333
+ /**
334
+ * Extract a field from an object using dot-notation and bracket syntax.
335
+ * Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
336
+ */
337
+ function extractField(obj, fieldPath) {
338
+ const parts = fieldPath.split('.');
339
+ let current = obj;
340
+ for (const part of parts) {
341
+ if (current === null || current === undefined) return undefined;
342
+ const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
343
+ if (bracketMatch) {
344
+ const key = bracketMatch[1];
345
+ const index = parseInt(bracketMatch[2], 10);
346
+ current = current[key];
347
+ if (!Array.isArray(current)) return undefined;
348
+ current = index < 0 ? current[current.length + index] : current[index];
349
+ } else {
350
+ current = current[part];
351
+ }
176
352
  }
353
+ return current;
354
+ }
177
355
 
356
+ async function runCommand(command, args, cwd, raw) {
178
357
  switch (command) {
179
358
  case 'state': {
180
359
  const subcommand = args[1];
@@ -197,50 +376,37 @@ async function main() {
197
376
  } else if (subcommand === 'advance-plan') {
198
377
  state.cmdStateAdvancePlan(cwd, raw);
199
378
  } else if (subcommand === 'record-metric') {
200
- const phaseIdx = args.indexOf('--phase');
201
- const planIdx = args.indexOf('--plan');
202
- const durationIdx = args.indexOf('--duration');
203
- const tasksIdx = args.indexOf('--tasks');
204
- const filesIdx = args.indexOf('--files');
205
- state.cmdStateRecordMetric(cwd, {
206
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
207
- plan: planIdx !== -1 ? args[planIdx + 1] : null,
208
- duration: durationIdx !== -1 ? args[durationIdx + 1] : null,
209
- tasks: tasksIdx !== -1 ? args[tasksIdx + 1] : null,
210
- files: filesIdx !== -1 ? args[filesIdx + 1] : null,
211
- }, raw);
379
+ const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
380
+ state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
212
381
  } else if (subcommand === 'update-progress') {
213
382
  state.cmdStateUpdateProgress(cwd, raw);
214
383
  } else if (subcommand === 'add-decision') {
215
- const phaseIdx = args.indexOf('--phase');
216
- const summaryIdx = args.indexOf('--summary');
217
- const summaryFileIdx = args.indexOf('--summary-file');
218
- const rationaleIdx = args.indexOf('--rationale');
219
- const rationaleFileIdx = args.indexOf('--rationale-file');
220
- state.cmdStateAddDecision(cwd, {
221
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
222
- summary: summaryIdx !== -1 ? args[summaryIdx + 1] : null,
223
- summary_file: summaryFileIdx !== -1 ? args[summaryFileIdx + 1] : null,
224
- rationale: rationaleIdx !== -1 ? args[rationaleIdx + 1] : '',
225
- rationale_file: rationaleFileIdx !== -1 ? args[rationaleFileIdx + 1] : null,
226
- }, raw);
384
+ const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
385
+ state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
227
386
  } else if (subcommand === 'add-blocker') {
228
- const textIdx = args.indexOf('--text');
229
- const textFileIdx = args.indexOf('--text-file');
230
- state.cmdStateAddBlocker(cwd, {
231
- text: textIdx !== -1 ? args[textIdx + 1] : null,
232
- text_file: textFileIdx !== -1 ? args[textFileIdx + 1] : null,
233
- }, raw);
387
+ const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
388
+ state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
234
389
  } else if (subcommand === 'resolve-blocker') {
235
- const textIdx = args.indexOf('--text');
236
- state.cmdStateResolveBlocker(cwd, textIdx !== -1 ? args[textIdx + 1] : null, raw);
390
+ state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
237
391
  } else if (subcommand === 'record-session') {
238
- const stoppedIdx = args.indexOf('--stopped-at');
239
- const resumeIdx = args.indexOf('--resume-file');
240
- state.cmdStateRecordSession(cwd, {
241
- stopped_at: stoppedIdx !== -1 ? args[stoppedIdx + 1] : null,
242
- resume_file: resumeIdx !== -1 ? args[resumeIdx + 1] : 'None',
243
- }, raw);
392
+ const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
393
+ state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
394
+ } else if (subcommand === 'begin-phase') {
395
+ const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
396
+ state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
397
+ } else if (subcommand === 'signal-waiting') {
398
+ const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
399
+ state.cmdSignalWaiting(cwd, type, question, options, p, raw);
400
+ } else if (subcommand === 'signal-resume') {
401
+ state.cmdSignalResume(cwd, raw);
402
+ } else if (subcommand === 'planned-phase') {
403
+ const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
404
+ state.cmdStatePlannedPhase(cwd, p, plans !== null ? parseInt(plans, 10) : null, raw);
405
+ } else if (subcommand === 'validate') {
406
+ state.cmdStateValidate(cwd, raw);
407
+ } else if (subcommand === 'sync') {
408
+ const { verify } = parseNamedArgs(args, [], ['verify']);
409
+ state.cmdStateSync(cwd, { verify }, raw);
244
410
  } else {
245
411
  state.cmdStateLoad(cwd, raw);
246
412
  }
@@ -259,6 +425,7 @@ async function main() {
259
425
 
260
426
  case 'commit': {
261
427
  const amend = args.includes('--amend');
428
+ const noVerify = args.includes('--no-verify');
262
429
  const filesIndex = args.indexOf('--files');
263
430
  // Collect all positional args between command name and first flag,
264
431
  // then join them — handles both quoted ("multi word msg") and
@@ -267,7 +434,20 @@ async function main() {
267
434
  const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
268
435
  const message = messageArgs.join(' ') || undefined;
269
436
  const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
270
- commands.cmdCommit(cwd, message, files, raw, amend);
437
+ commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
438
+ break;
439
+ }
440
+
441
+ case 'check-commit': {
442
+ commands.cmdCheckCommit(cwd, raw);
443
+ break;
444
+ }
445
+
446
+ case 'commit-to-subrepo': {
447
+ const message = args[1];
448
+ const filesIndex = args.indexOf('--files');
449
+ const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
450
+ commands.cmdCommitToSubrepo(cwd, message, files, raw);
271
451
  break;
272
452
  }
273
453
 
@@ -285,19 +465,18 @@ async function main() {
285
465
  template.cmdTemplateSelect(cwd, args[2], raw);
286
466
  } else if (subcommand === 'fill') {
287
467
  const templateType = args[2];
288
- const phaseIdx = args.indexOf('--phase');
289
- const planIdx = args.indexOf('--plan');
290
- const nameIdx = args.indexOf('--name');
291
- const typeIdx = args.indexOf('--type');
292
- const waveIdx = args.indexOf('--wave');
293
- const fieldsIdx = args.indexOf('--fields');
468
+ const { phase, plan, name, type, wave, fields: fieldsRaw } = parseNamedArgs(args, ['phase', 'plan', 'name', 'type', 'wave', 'fields']);
469
+ let fields = {};
470
+ if (fieldsRaw) {
471
+ const { safeJsonParse } = require('./lib/security.cjs');
472
+ const result = safeJsonParse(fieldsRaw, { label: '--fields' });
473
+ if (!result.ok) error(result.error);
474
+ fields = result.value;
475
+ }
294
476
  template.cmdTemplateFill(cwd, templateType, {
295
- phase: phaseIdx !== -1 ? args[phaseIdx + 1] : null,
296
- plan: planIdx !== -1 ? args[planIdx + 1] : null,
297
- name: nameIdx !== -1 ? args[nameIdx + 1] : null,
298
- type: typeIdx !== -1 ? args[typeIdx + 1] : 'execute',
299
- wave: waveIdx !== -1 ? args[waveIdx + 1] : '1',
300
- fields: fieldsIdx !== -1 ? JSON.parse(args[fieldsIdx + 1]) : {},
477
+ phase, plan, name, fields,
478
+ type: type || 'execute',
479
+ wave: wave || '1',
301
480
  }, raw);
302
481
  } else {
303
482
  error('Unknown template subcommand. Available: select, fill');
@@ -309,18 +488,14 @@ async function main() {
309
488
  const subcommand = args[1];
310
489
  const file = args[2];
311
490
  if (subcommand === 'get') {
312
- const fieldIdx = args.indexOf('--field');
313
- frontmatter.cmdFrontmatterGet(cwd, file, fieldIdx !== -1 ? args[fieldIdx + 1] : null, raw);
491
+ frontmatter.cmdFrontmatterGet(cwd, file, parseNamedArgs(args, ['field']).field, raw);
314
492
  } else if (subcommand === 'set') {
315
- const fieldIdx = args.indexOf('--field');
316
- const valueIdx = args.indexOf('--value');
317
- frontmatter.cmdFrontmatterSet(cwd, file, fieldIdx !== -1 ? args[fieldIdx + 1] : null, valueIdx !== -1 ? args[valueIdx + 1] : undefined, raw);
493
+ const { field, value } = parseNamedArgs(args, ['field', 'value']);
494
+ frontmatter.cmdFrontmatterSet(cwd, file, field, value !== null ? value : undefined, raw);
318
495
  } else if (subcommand === 'merge') {
319
- const dataIdx = args.indexOf('--data');
320
- frontmatter.cmdFrontmatterMerge(cwd, file, dataIdx !== -1 ? args[dataIdx + 1] : null, raw);
496
+ frontmatter.cmdFrontmatterMerge(cwd, file, parseNamedArgs(args, ['data']).data, raw);
321
497
  } else if (subcommand === 'validate') {
322
- const schemaIdx = args.indexOf('--schema');
323
- frontmatter.cmdFrontmatterValidate(cwd, file, schemaIdx !== -1 ? args[schemaIdx + 1] : null, raw);
498
+ frontmatter.cmdFrontmatterValidate(cwd, file, parseNamedArgs(args, ['schema']).schema, raw);
324
499
  } else {
325
500
  error('Unknown frontmatter subcommand. Available: get, set, merge, validate');
326
501
  }
@@ -341,8 +516,11 @@ async function main() {
341
516
  verify.cmdVerifyArtifacts(cwd, args[2], raw);
342
517
  } else if (subcommand === 'key-links') {
343
518
  verify.cmdVerifyKeyLinks(cwd, args[2], raw);
519
+ } else if (subcommand === 'schema-drift') {
520
+ const skipFlag = args.includes('--skip');
521
+ verify.cmdVerifySchemaDrift(cwd, args[2], skipFlag, raw);
344
522
  } else {
345
- error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links');
523
+ error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links, schema-drift');
346
524
  }
347
525
  break;
348
526
  }
@@ -377,11 +555,26 @@ async function main() {
377
555
  break;
378
556
  }
379
557
 
558
+ case "config-set-model-profile": {
559
+ config.cmdConfigSetModelProfile(cwd, args[1], raw);
560
+ break;
561
+ }
562
+
380
563
  case 'config-get': {
381
564
  config.cmdConfigGet(cwd, args[1], raw);
382
565
  break;
383
566
  }
384
567
 
568
+ case 'config-new-project': {
569
+ config.cmdConfigNewProject(cwd, args[1], raw);
570
+ break;
571
+ }
572
+
573
+ case 'agent-skills': {
574
+ init.cmdAgentSkills(cwd, args[1], raw);
575
+ break;
576
+ }
577
+
385
578
  case 'history-digest': {
386
579
  commands.cmdHistoryDigest(cwd, raw);
387
580
  break;
@@ -398,8 +591,10 @@ async function main() {
398
591
  includeArchived: args.includes('--include-archived'),
399
592
  };
400
593
  phase.cmdPhasesList(cwd, options, raw);
594
+ } else if (subcommand === 'clear') {
595
+ milestone.cmdPhasesClear(cwd, raw);
401
596
  } else {
402
- error('Unknown phases subcommand. Available: list');
597
+ error('Unknown phases subcommand. Available: list, clear');
403
598
  }
404
599
  break;
405
600
  }
@@ -433,7 +628,18 @@ async function main() {
433
628
  if (subcommand === 'next-decimal') {
434
629
  phase.cmdPhaseNextDecimal(cwd, args[2], raw);
435
630
  } else if (subcommand === 'add') {
436
- phase.cmdPhaseAdd(cwd, args.slice(2).join(' '), raw);
631
+ const idIdx = args.indexOf('--id');
632
+ let customId = null;
633
+ const descArgs = [];
634
+ for (let i = 2; i < args.length; i++) {
635
+ if (args[i] === '--id' && i + 1 < args.length) {
636
+ customId = args[i + 1];
637
+ i++; // skip value
638
+ } else {
639
+ descArgs.push(args[i]);
640
+ }
641
+ }
642
+ phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
437
643
  } else if (subcommand === 'insert') {
438
644
  phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
439
645
  } else if (subcommand === 'remove') {
@@ -450,18 +656,8 @@ async function main() {
450
656
  case 'milestone': {
451
657
  const subcommand = args[1];
452
658
  if (subcommand === 'complete') {
453
- const nameIndex = args.indexOf('--name');
659
+ const milestoneName = parseMultiwordArg(args, 'name');
454
660
  const archivePhases = args.includes('--archive-phases');
455
- // Collect --name value (everything after --name until next flag or end)
456
- let milestoneName = null;
457
- if (nameIndex !== -1) {
458
- const nameArgs = [];
459
- for (let i = nameIndex + 1; i < args.length; i++) {
460
- if (args[i].startsWith('--')) break;
461
- nameArgs.push(args[i]);
462
- }
463
- milestoneName = nameArgs.join(' ') || null;
464
- }
465
661
  milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases }, raw);
466
662
  } else {
467
663
  error('Unknown milestone subcommand. Available: complete');
@@ -476,8 +672,10 @@ async function main() {
476
672
  } else if (subcommand === 'health') {
477
673
  const repairFlag = args.includes('--repair');
478
674
  verify.cmdValidateHealth(cwd, { repair: repairFlag }, raw);
675
+ } else if (subcommand === 'agents') {
676
+ verify.cmdValidateAgents(cwd, raw);
479
677
  } else {
480
- error('Unknown validate subcommand. Available: consistency, health');
678
+ error('Unknown validate subcommand. Available: consistency, health, agents');
481
679
  }
482
680
  break;
483
681
  }
@@ -488,23 +686,47 @@ async function main() {
488
686
  break;
489
687
  }
490
688
 
689
+ case 'audit-uat': {
690
+ const uat = require('./lib/uat.cjs');
691
+ uat.cmdAuditUat(cwd, raw);
692
+ break;
693
+ }
694
+
695
+ case 'uat': {
696
+ const subcommand = args[1];
697
+ const uat = require('./lib/uat.cjs');
698
+ if (subcommand === 'render-checkpoint') {
699
+ const options = parseNamedArgs(args, ['file']);
700
+ uat.cmdRenderCheckpoint(cwd, options, raw);
701
+ } else {
702
+ error('Unknown uat subcommand. Available: render-checkpoint');
703
+ }
704
+ break;
705
+ }
706
+
707
+ case 'stats': {
708
+ const subcommand = args[1] || 'json';
709
+ commands.cmdStats(cwd, subcommand, raw);
710
+ break;
711
+ }
712
+
491
713
  case 'todo': {
492
714
  const subcommand = args[1];
493
715
  if (subcommand === 'complete') {
494
716
  commands.cmdTodoComplete(cwd, args[2], raw);
717
+ } else if (subcommand === 'match-phase') {
718
+ commands.cmdTodoMatchPhase(cwd, args[2], raw);
495
719
  } else {
496
- error('Unknown todo subcommand. Available: complete');
720
+ error('Unknown todo subcommand. Available: complete, match-phase');
497
721
  }
498
722
  break;
499
723
  }
500
724
 
501
725
  case 'scaffold': {
502
726
  const scaffoldType = args[1];
503
- const phaseIndex = args.indexOf('--phase');
504
- const nameIndex = args.indexOf('--name');
505
727
  const scaffoldOptions = {
506
- phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
507
- name: nameIndex !== -1 ? args.slice(nameIndex + 1).join(' ') : null,
728
+ phase: parseNamedArgs(args, ['phase']).phase,
729
+ name: parseMultiwordArg(args, 'name'),
508
730
  };
509
731
  commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
510
732
  break;
@@ -513,12 +735,16 @@ async function main() {
513
735
  case 'init': {
514
736
  const workflow = args[1];
515
737
  switch (workflow) {
516
- case 'execute-phase':
517
- init.cmdInitExecutePhase(cwd, args[2], raw);
738
+ case 'execute-phase': {
739
+ const { validate: epValidate } = parseNamedArgs(args, [], ['validate']);
740
+ init.cmdInitExecutePhase(cwd, args[2], raw, { validate: epValidate });
518
741
  break;
519
- case 'plan-phase':
520
- init.cmdInitPlanPhase(cwd, args[2], raw);
742
+ }
743
+ case 'plan-phase': {
744
+ const { validate: ppValidate } = parseNamedArgs(args, [], ['validate']);
745
+ init.cmdInitPlanPhase(cwd, args[2], raw, { validate: ppValidate });
521
746
  break;
747
+ }
522
748
  case 'new-project':
523
749
  init.cmdInitNewProject(cwd, raw);
524
750
  break;
@@ -549,8 +775,20 @@ async function main() {
549
775
  case 'progress':
550
776
  init.cmdInitProgress(cwd, raw);
551
777
  break;
778
+ case 'manager':
779
+ init.cmdInitManager(cwd, raw);
780
+ break;
781
+ case 'new-workspace':
782
+ init.cmdInitNewWorkspace(cwd, raw);
783
+ break;
784
+ case 'list-workspaces':
785
+ init.cmdInitListWorkspaces(cwd, raw);
786
+ break;
787
+ case 'remove-workspace':
788
+ init.cmdInitRemoveWorkspace(cwd, args[2], raw);
789
+ break;
552
790
  default:
553
- 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`);
791
+ 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`);
554
792
  }
555
793
  break;
556
794
  }
@@ -584,6 +822,128 @@ async function main() {
584
822
  break;
585
823
  }
586
824
 
825
+ // ─── Profiling Pipeline ────────────────────────────────────────────────
826
+
827
+ case 'scan-sessions': {
828
+ const pathIdx = args.indexOf('--path');
829
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
830
+ const verboseFlag = args.includes('--verbose');
831
+ const jsonFlag = args.includes('--json');
832
+ await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
833
+ break;
834
+ }
835
+
836
+ case 'extract-messages': {
837
+ const sessionIdx = args.indexOf('--session');
838
+ const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
839
+ const limitIdx = args.indexOf('--limit');
840
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
841
+ const pathIdx = args.indexOf('--path');
842
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
843
+ const projectArg = args[1];
844
+ if (!projectArg || projectArg.startsWith('--')) {
845
+ error('Usage: gsd-tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.');
846
+ }
847
+ await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
848
+ break;
849
+ }
850
+
851
+ case 'profile-sample': {
852
+ const pathIdx = args.indexOf('--path');
853
+ const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
854
+ const limitIdx = args.indexOf('--limit');
855
+ const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
856
+ const maxPerIdx = args.indexOf('--max-per-project');
857
+ const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
858
+ const maxCharsIdx = args.indexOf('--max-chars');
859
+ const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
860
+ await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
861
+ break;
862
+ }
863
+
864
+ // ─── Profile Output ──────────────────────────────────────────────────
865
+
866
+ case 'write-profile': {
867
+ const inputIdx = args.indexOf('--input');
868
+ const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
869
+ if (!inputPath) error('--input <analysis-json-path> is required');
870
+ const outputIdx = args.indexOf('--output');
871
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
872
+ profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
873
+ break;
874
+ }
875
+
876
+ case 'profile-questionnaire': {
877
+ const answersIdx = args.indexOf('--answers');
878
+ const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
879
+ profileOutput.cmdProfileQuestionnaire({ answers }, raw);
880
+ break;
881
+ }
882
+
883
+ case 'generate-dev-preferences': {
884
+ const analysisIdx = args.indexOf('--analysis');
885
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
886
+ const outputIdx = args.indexOf('--output');
887
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
888
+ const stackIdx = args.indexOf('--stack');
889
+ const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
890
+ profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
891
+ break;
892
+ }
893
+
894
+ case 'generate-OpenCode-profile': {
895
+ const analysisIdx = args.indexOf('--analysis');
896
+ const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
897
+ const outputIdx = args.indexOf('--output');
898
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
899
+ const globalFlag = args.includes('--global');
900
+ profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
901
+ break;
902
+ }
903
+
904
+ case 'generate-OpenCode-md': {
905
+ const outputIdx = args.indexOf('--output');
906
+ const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
907
+ const autoFlag = args.includes('--auto');
908
+ const forceFlag = args.includes('--force');
909
+ profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
910
+ break;
911
+ }
912
+
913
+ case 'workstream': {
914
+ const subcommand = args[1];
915
+ if (subcommand === 'create') {
916
+ const migrateNameIdx = args.indexOf('--migrate-name');
917
+ const noMigrate = args.includes('--no-migrate');
918
+ workstream.cmdWorkstreamCreate(cwd, args[2], {
919
+ migrate: !noMigrate,
920
+ migrateName: migrateNameIdx !== -1 ? args[migrateNameIdx + 1] : null,
921
+ }, raw);
922
+ } else if (subcommand === 'list') {
923
+ workstream.cmdWorkstreamList(cwd, raw);
924
+ } else if (subcommand === 'status') {
925
+ workstream.cmdWorkstreamStatus(cwd, args[2], raw);
926
+ } else if (subcommand === 'complete') {
927
+ workstream.cmdWorkstreamComplete(cwd, args[2], {}, raw);
928
+ } else if (subcommand === 'set') {
929
+ workstream.cmdWorkstreamSet(cwd, args[2], raw);
930
+ } else if (subcommand === 'get') {
931
+ workstream.cmdWorkstreamGet(cwd, raw);
932
+ } else if (subcommand === 'progress') {
933
+ workstream.cmdWorkstreamProgress(cwd, raw);
934
+ } else {
935
+ error('Unknown workstream subcommand. Available: create, list, status, complete, set, get, progress');
936
+ }
937
+ break;
938
+ }
939
+
940
+ // ─── Documentation ────────────────────────────────────────────────────
941
+
942
+ case 'docs-init': {
943
+ docs.cmdDocsInit(cwd, raw);
944
+ break;
945
+ }
946
+
587
947
  default:
588
948
  error(`Unknown command: ${command}`);
589
949
  }