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
@@ -4,9 +4,9 @@
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
- const { escapeRegex, getMilestonePhaseFilter, output, error } = require('./core.cjs');
7
+ const { escapeRegex, getMilestonePhaseFilter, extractOneLinerFromBody, normalizeMd, planningPaths, output, error } = require('./core.cjs');
8
8
  const { extractFrontmatter } = require('./frontmatter.cjs');
9
- const { writeStateMd } = require('./state.cjs');
9
+ const { writeStateMd, stateReplaceFieldWithFallback } = require('./state.cjs');
10
10
 
11
11
  function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
12
12
  if (!reqIdsRaw || reqIdsRaw.length === 0) {
@@ -25,7 +25,7 @@ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
25
25
  error('no valid requirement IDs found');
26
26
  }
27
27
 
28
- const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
28
+ const reqPath = planningPaths(cwd).requirements;
29
29
  if (!fs.existsSync(reqPath)) {
30
30
  output({ updated: false, reason: 'REQUIREMENTS.md not found', ids: reqIds }, raw, 'no requirements file');
31
31
  return;
@@ -33,6 +33,7 @@ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
33
33
 
34
34
  let reqContent = fs.readFileSync(reqPath, 'utf-8');
35
35
  const updated = [];
36
+ const alreadyComplete = [];
36
37
  const notFound = [];
37
38
 
38
39
  for (const reqId of reqIds) {
@@ -60,7 +61,14 @@ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
60
61
  if (found) {
61
62
  updated.push(reqId);
62
63
  } else {
63
- notFound.push(reqId);
64
+ // Check if already complete before declaring not_found
65
+ const doneCheckbox = new RegExp(`-\\s*\\[x\\]\\s*\\*\\*${reqEscaped}\\*\\*`, 'gi');
66
+ const doneTable = new RegExp(`\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|\\s*Complete\\s*\\|`, 'gi');
67
+ if (doneCheckbox.test(reqContent) || doneTable.test(reqContent)) {
68
+ alreadyComplete.push(reqId);
69
+ } else {
70
+ notFound.push(reqId);
71
+ }
64
72
  }
65
73
  }
66
74
 
@@ -71,6 +79,7 @@ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
71
79
  output({
72
80
  updated: updated.length > 0,
73
81
  marked_complete: updated,
82
+ already_complete: alreadyComplete,
74
83
  not_found: notFound,
75
84
  total: reqIds.length,
76
85
  }, raw, `${updated.length}/${reqIds.length} requirements marked complete`);
@@ -81,12 +90,12 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
81
90
  error('version required for milestone complete (e.g., v1.0)');
82
91
  }
83
92
 
84
- const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
85
- const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
86
- const statePath = path.join(cwd, '.planning', 'STATE.md');
93
+ const roadmapPath = planningPaths(cwd).roadmap;
94
+ const reqPath = planningPaths(cwd).requirements;
95
+ const statePath = planningPaths(cwd).state;
87
96
  const milestonesPath = path.join(cwd, '.planning', 'MILESTONES.md');
88
97
  const archiveDir = path.join(cwd, '.planning', 'milestones');
89
- const phasesDir = path.join(cwd, '.planning', 'phases');
98
+ const phasesDir = planningPaths(cwd).phases;
90
99
  const today = new Date().toISOString().split('T')[0];
91
100
  const milestoneName = options.name || version;
92
101
 
@@ -122,16 +131,24 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
122
131
  try {
123
132
  const content = fs.readFileSync(path.join(phasesDir, dir, s), 'utf-8');
124
133
  const fm = extractFrontmatter(content);
125
- if (fm['one-liner']) {
126
- accomplishments.push(fm['one-liner']);
134
+ const oneLiner = fm['one-liner'] || extractOneLinerFromBody(content);
135
+ if (oneLiner) {
136
+ accomplishments.push(oneLiner);
127
137
  }
128
- // Count tasks
129
- const taskMatches = content.match(/##\s*task\s*\d+/gi) || [];
130
- totalTasks += taskMatches.length;
131
- } catch {}
138
+ // Count tasks: prefer **Tasks:** N from Performance section,
139
+ // then <task XML tags, then ## task N markdown headers
140
+ const tasksFieldMatch = content.match(/\*\*Tasks:\*\*\s*(\d+)/);
141
+ if (tasksFieldMatch) {
142
+ totalTasks += parseInt(tasksFieldMatch[1], 10);
143
+ } else {
144
+ const xmlTaskMatches = content.match(/<task[\s>]/gi) || [];
145
+ const mdTaskMatches = content.match(/##\s*task\s*\d+/gi) || [];
146
+ totalTasks += xmlTaskMatches.length || mdTaskMatches.length;
147
+ }
148
+ } catch { /* intentionally empty */ }
132
149
  }
133
150
  }
134
- } catch {}
151
+ } catch { /* intentionally empty */ }
135
152
 
136
153
  // Archive ROADMAP.md
137
154
  if (fs.existsSync(roadmapPath)) {
@@ -160,38 +177,32 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
160
177
  const existing = fs.readFileSync(milestonesPath, 'utf-8');
161
178
  if (!existing.trim()) {
162
179
  // Empty file — treat like new
163
- fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
180
+ fs.writeFileSync(milestonesPath, normalizeMd(`# Milestones\n\n${milestoneEntry}`), 'utf-8');
164
181
  } else {
165
182
  // Insert after the header line(s) for reverse chronological order (newest first)
166
183
  const headerMatch = existing.match(/^(#{1,3}\s+[^\n]*\n\n?)/);
167
184
  if (headerMatch) {
168
185
  const header = headerMatch[1];
169
186
  const rest = existing.slice(header.length);
170
- fs.writeFileSync(milestonesPath, header + milestoneEntry + rest, 'utf-8');
187
+ fs.writeFileSync(milestonesPath, normalizeMd(header + milestoneEntry + rest), 'utf-8');
171
188
  } else {
172
189
  // No recognizable header — prepend the entry
173
- fs.writeFileSync(milestonesPath, milestoneEntry + existing, 'utf-8');
190
+ fs.writeFileSync(milestonesPath, normalizeMd(milestoneEntry + existing), 'utf-8');
174
191
  }
175
192
  }
176
193
  } else {
177
- fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
194
+ fs.writeFileSync(milestonesPath, normalizeMd(`# Milestones\n\n${milestoneEntry}`), 'utf-8');
178
195
  }
179
196
 
180
- // Update STATE.md
197
+ // Update STATE.md — use shared helpers that handle both **bold:** and plain Field: formats
181
198
  if (fs.existsSync(statePath)) {
182
199
  let stateContent = fs.readFileSync(statePath, 'utf-8');
183
- stateContent = stateContent.replace(
184
- /(\*\*Status:\*\*\s*).*/,
185
- `$1${version} milestone complete`
186
- );
187
- stateContent = stateContent.replace(
188
- /(\*\*Last Activity:\*\*\s*).*/,
189
- `$1${today}`
190
- );
191
- stateContent = stateContent.replace(
192
- /(\*\*Last Activity Description:\*\*\s*).*/,
193
- `$1${version} milestone completed and archived`
194
- );
200
+
201
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Status', null, `${version} milestone complete`);
202
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity', 'Last activity', today);
203
+ stateContent = stateReplaceFieldWithFallback(stateContent, 'Last Activity Description', null,
204
+ `${version} milestone completed and archived`);
205
+
195
206
  writeStateMd(statePath, stateContent, cwd);
196
207
  }
197
208
 
@@ -211,7 +222,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
211
222
  archivedCount++;
212
223
  }
213
224
  phasesArchived = archivedCount > 0;
214
- } catch {}
225
+ } catch { /* intentionally empty */ }
215
226
  }
216
227
 
217
228
  const result = {
@@ -235,7 +246,28 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
235
246
  output(result, raw);
236
247
  }
237
248
 
249
+ function cmdPhasesClear(cwd, raw) {
250
+ const phasesDir = planningPaths(cwd).phases;
251
+ let cleared = 0;
252
+
253
+ if (fs.existsSync(phasesDir)) {
254
+ try {
255
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
256
+ for (const entry of entries) {
257
+ if (!entry.isDirectory()) continue;
258
+ fs.rmSync(path.join(phasesDir, entry.name), { recursive: true, force: true });
259
+ cleared++;
260
+ }
261
+ } catch (e) {
262
+ error('Failed to clear phases directory: ' + e.message);
263
+ }
264
+ }
265
+
266
+ output({ cleared }, raw, `${cleared} phase director${cleared === 1 ? 'y' : 'ies'} cleared`);
267
+ }
268
+
238
269
  module.exports = {
239
270
  cmdRequirementsMarkComplete,
240
271
  cmdMilestoneComplete,
272
+ cmdPhasesClear,
241
273
  };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Mapping of GSD agent to model for each profile.
3
+ *
4
+ * Should be in sync with the profiles table in `get-shit-done/references/model-profiles.md`. But
5
+ * possibly worth making this the single source of truth at some point, and removing the markdown
6
+ * reference table in favor of programmatically determining the model to use for an agent (which
7
+ * would be faster, use fewer tokens, and be less error-prone).
8
+ */
9
+ const MODEL_PROFILES = {
10
+ 'gsd-planner': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
11
+ 'gsd-roadmapper': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
12
+ 'gsd-executor': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
13
+ 'gsd-phase-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
14
+ 'gsd-project-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
15
+ 'gsd-research-synthesizer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
16
+ 'gsd-debugger': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
17
+ 'gsd-codebase-mapper': { quality: 'sonnet', balanced: 'haiku', budget: 'haiku' },
18
+ 'gsd-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
19
+ 'gsd-plan-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
20
+ 'gsd-integration-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
21
+ 'gsd-nyquist-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
22
+ 'gsd-ui-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
23
+ 'gsd-ui-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
24
+ 'gsd-ui-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
25
+ 'gsd-doc-writer': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
26
+ 'gsd-doc-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
27
+ };
28
+ const VALID_PROFILES = Object.keys(MODEL_PROFILES['gsd-planner']);
29
+
30
+ /**
31
+ * Formats the agent-to-model mapping as a human-readable table (in string format).
32
+ *
33
+ * @param {Object<string, string>} agentToModelMap - A mapping from agent to model
34
+ * @returns {string} A formatted table string
35
+ */
36
+ function formatAgentToModelMapAsTable(agentToModelMap) {
37
+ const agentWidth = Math.max('Agent'.length, ...Object.keys(agentToModelMap).map((a) => a.length));
38
+ const modelWidth = Math.max(
39
+ 'Model'.length,
40
+ ...Object.values(agentToModelMap).map((m) => m.length)
41
+ );
42
+ const sep = '─'.repeat(agentWidth + 2) + '┼' + '─'.repeat(modelWidth + 2);
43
+ const header = ' ' + 'Agent'.padEnd(agentWidth) + ' │ ' + 'Model'.padEnd(modelWidth);
44
+ let agentToModelTable = header + '\n' + sep + '\n';
45
+ for (const [agent, model] of Object.entries(agentToModelMap)) {
46
+ agentToModelTable += ' ' + agent.padEnd(agentWidth) + ' │ ' + model.padEnd(modelWidth) + '\n';
47
+ }
48
+ return agentToModelTable;
49
+ }
50
+
51
+ /**
52
+ * Returns a mapping from agent to model for the given model profile.
53
+ *
54
+ * @param {string} normalizedProfile - The normalized (lowercase and trimmed) profile name
55
+ * @returns {Object<string, string>} A mapping from agent to model for the given profile
56
+ */
57
+ function getAgentToModelMapForProfile(normalizedProfile) {
58
+ const agentToModelMap = {};
59
+ for (const [agent, profileToModelMap] of Object.entries(MODEL_PROFILES)) {
60
+ agentToModelMap[agent] = profileToModelMap[normalizedProfile];
61
+ }
62
+ return agentToModelMap;
63
+ }
64
+
65
+ module.exports = {
66
+ MODEL_PROFILES,
67
+ VALID_PROFILES,
68
+ formatAgentToModelMapAsTable,
69
+ getAgentToModelMapForProfile,
70
+ };