gsd-opencode 1.22.1 → 1.30.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 (156) 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 +118 -2
  5. package/agents/gsd-executor.md +24 -4
  6. package/agents/gsd-integration-checker.md +0 -2
  7. package/agents/gsd-nyquist-auditor.md +0 -2
  8. package/agents/gsd-phase-researcher.md +150 -5
  9. package/agents/gsd-plan-checker.md +70 -5
  10. package/agents/gsd-planner.md +49 -4
  11. package/agents/gsd-project-researcher.md +28 -3
  12. package/agents/gsd-research-synthesizer.md +0 -2
  13. package/agents/gsd-roadmapper.md +29 -2
  14. package/agents/gsd-ui-auditor.md +445 -0
  15. package/agents/gsd-ui-checker.md +305 -0
  16. package/agents/gsd-ui-researcher.md +368 -0
  17. package/agents/gsd-user-profiler.md +173 -0
  18. package/agents/gsd-verifier.md +123 -4
  19. package/commands/gsd/gsd-add-backlog.md +76 -0
  20. package/commands/gsd/gsd-audit-uat.md +24 -0
  21. package/commands/gsd/gsd-autonomous.md +41 -0
  22. package/commands/gsd/gsd-debug.md +5 -0
  23. package/commands/gsd/gsd-discuss-phase.md +10 -36
  24. package/commands/gsd/gsd-do.md +30 -0
  25. package/commands/gsd/gsd-execute-phase.md +20 -2
  26. package/commands/gsd/gsd-fast.md +30 -0
  27. package/commands/gsd/gsd-forensics.md +56 -0
  28. package/commands/gsd/gsd-list-workspaces.md +19 -0
  29. package/commands/gsd/gsd-manager.md +39 -0
  30. package/commands/gsd/gsd-milestone-summary.md +51 -0
  31. package/commands/gsd/gsd-new-workspace.md +44 -0
  32. package/commands/gsd/gsd-next.md +24 -0
  33. package/commands/gsd/gsd-note.md +34 -0
  34. package/commands/gsd/gsd-plan-phase.md +3 -1
  35. package/commands/gsd/gsd-plant-seed.md +28 -0
  36. package/commands/gsd/gsd-pr-branch.md +25 -0
  37. package/commands/gsd/gsd-profile-user.md +46 -0
  38. package/commands/gsd/gsd-quick.md +4 -2
  39. package/commands/gsd/gsd-reapply-patches.md +9 -8
  40. package/commands/gsd/gsd-remove-workspace.md +26 -0
  41. package/commands/gsd/gsd-research-phase.md +5 -0
  42. package/commands/gsd/gsd-review-backlog.md +61 -0
  43. package/commands/gsd/gsd-review.md +37 -0
  44. package/commands/gsd/gsd-session-report.md +19 -0
  45. package/commands/gsd/gsd-set-profile.md +24 -23
  46. package/commands/gsd/gsd-ship.md +23 -0
  47. package/commands/gsd/gsd-stats.md +18 -0
  48. package/commands/gsd/gsd-thread.md +127 -0
  49. package/commands/gsd/gsd-ui-phase.md +34 -0
  50. package/commands/gsd/gsd-ui-review.md +32 -0
  51. package/commands/gsd/gsd-workstreams.md +66 -0
  52. package/get-shit-done/bin/gsd-tools.cjs +410 -84
  53. package/get-shit-done/bin/lib/commands.cjs +429 -18
  54. package/get-shit-done/bin/lib/config.cjs +318 -45
  55. package/get-shit-done/bin/lib/core.cjs +822 -84
  56. package/get-shit-done/bin/lib/frontmatter.cjs +78 -41
  57. package/get-shit-done/bin/lib/init.cjs +836 -104
  58. package/get-shit-done/bin/lib/milestone.cjs +44 -33
  59. package/get-shit-done/bin/lib/model-profiles.cjs +68 -0
  60. package/get-shit-done/bin/lib/phase.cjs +293 -306
  61. package/get-shit-done/bin/lib/profile-output.cjs +952 -0
  62. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  63. package/get-shit-done/bin/lib/roadmap.cjs +55 -24
  64. package/get-shit-done/bin/lib/security.cjs +382 -0
  65. package/get-shit-done/bin/lib/state.cjs +363 -53
  66. package/get-shit-done/bin/lib/template.cjs +2 -2
  67. package/get-shit-done/bin/lib/uat.cjs +282 -0
  68. package/get-shit-done/bin/lib/verify.cjs +104 -36
  69. package/get-shit-done/bin/lib/workstream.cjs +491 -0
  70. package/get-shit-done/references/checkpoints.md +12 -10
  71. package/get-shit-done/references/decimal-phase-calculation.md +2 -3
  72. package/get-shit-done/references/git-integration.md +47 -0
  73. package/get-shit-done/references/model-profile-resolution.md +2 -0
  74. package/get-shit-done/references/model-profiles.md +62 -16
  75. package/get-shit-done/references/phase-argument-parsing.md +2 -2
  76. package/get-shit-done/references/planning-config.md +3 -1
  77. package/get-shit-done/references/user-profiling.md +681 -0
  78. package/get-shit-done/references/workstream-flag.md +58 -0
  79. package/get-shit-done/templates/UAT.md +21 -3
  80. package/get-shit-done/templates/UI-SPEC.md +100 -0
  81. package/get-shit-done/templates/claude-md.md +122 -0
  82. package/get-shit-done/templates/config.json +10 -3
  83. package/get-shit-done/templates/context.md +61 -6
  84. package/get-shit-done/templates/dev-preferences.md +21 -0
  85. package/get-shit-done/templates/discussion-log.md +63 -0
  86. package/get-shit-done/templates/phase-prompt.md +46 -5
  87. package/get-shit-done/templates/project.md +2 -0
  88. package/get-shit-done/templates/state.md +2 -2
  89. package/get-shit-done/templates/user-profile.md +146 -0
  90. package/get-shit-done/workflows/add-phase.md +2 -2
  91. package/get-shit-done/workflows/add-tests.md +4 -4
  92. package/get-shit-done/workflows/add-todo.md +3 -3
  93. package/get-shit-done/workflows/audit-milestone.md +13 -5
  94. package/get-shit-done/workflows/audit-uat.md +109 -0
  95. package/get-shit-done/workflows/autonomous.md +891 -0
  96. package/get-shit-done/workflows/check-todos.md +2 -2
  97. package/get-shit-done/workflows/cleanup.md +4 -4
  98. package/get-shit-done/workflows/complete-milestone.md +9 -6
  99. package/get-shit-done/workflows/diagnose-issues.md +15 -3
  100. package/get-shit-done/workflows/discovery-phase.md +3 -3
  101. package/get-shit-done/workflows/discuss-phase-assumptions.md +653 -0
  102. package/get-shit-done/workflows/discuss-phase.md +411 -38
  103. package/get-shit-done/workflows/do.md +104 -0
  104. package/get-shit-done/workflows/execute-phase.md +405 -18
  105. package/get-shit-done/workflows/execute-plan.md +77 -12
  106. package/get-shit-done/workflows/fast.md +105 -0
  107. package/get-shit-done/workflows/forensics.md +265 -0
  108. package/get-shit-done/workflows/health.md +28 -6
  109. package/get-shit-done/workflows/help.md +124 -7
  110. package/get-shit-done/workflows/insert-phase.md +2 -2
  111. package/get-shit-done/workflows/list-phase-assumptions.md +2 -2
  112. package/get-shit-done/workflows/list-workspaces.md +56 -0
  113. package/get-shit-done/workflows/manager.md +362 -0
  114. package/get-shit-done/workflows/map-codebase.md +74 -13
  115. package/get-shit-done/workflows/milestone-summary.md +223 -0
  116. package/get-shit-done/workflows/new-milestone.md +120 -18
  117. package/get-shit-done/workflows/new-project.md +178 -39
  118. package/get-shit-done/workflows/new-workspace.md +237 -0
  119. package/get-shit-done/workflows/next.md +97 -0
  120. package/get-shit-done/workflows/node-repair.md +92 -0
  121. package/get-shit-done/workflows/note.md +156 -0
  122. package/get-shit-done/workflows/pause-work.md +62 -8
  123. package/get-shit-done/workflows/plan-milestone-gaps.md +4 -5
  124. package/get-shit-done/workflows/plan-phase.md +332 -33
  125. package/get-shit-done/workflows/plant-seed.md +169 -0
  126. package/get-shit-done/workflows/pr-branch.md +129 -0
  127. package/get-shit-done/workflows/profile-user.md +450 -0
  128. package/get-shit-done/workflows/progress.md +145 -20
  129. package/get-shit-done/workflows/quick.md +205 -49
  130. package/get-shit-done/workflows/remove-phase.md +2 -2
  131. package/get-shit-done/workflows/remove-workspace.md +90 -0
  132. package/get-shit-done/workflows/research-phase.md +11 -3
  133. package/get-shit-done/workflows/resume-project.md +35 -16
  134. package/get-shit-done/workflows/review.md +228 -0
  135. package/get-shit-done/workflows/session-report.md +146 -0
  136. package/get-shit-done/workflows/set-profile.md +2 -2
  137. package/get-shit-done/workflows/settings.md +79 -10
  138. package/get-shit-done/workflows/ship.md +228 -0
  139. package/get-shit-done/workflows/stats.md +60 -0
  140. package/get-shit-done/workflows/transition.md +147 -20
  141. package/get-shit-done/workflows/ui-phase.md +302 -0
  142. package/get-shit-done/workflows/ui-review.md +165 -0
  143. package/get-shit-done/workflows/update.md +108 -25
  144. package/get-shit-done/workflows/validate-phase.md +15 -8
  145. package/get-shit-done/workflows/verify-phase.md +16 -5
  146. package/get-shit-done/workflows/verify-work.md +72 -18
  147. package/package.json +1 -1
  148. package/skills/gsd-audit-milestone/SKILL.md +29 -0
  149. package/skills/gsd-cleanup/SKILL.md +19 -0
  150. package/skills/gsd-complete-milestone/SKILL.md +131 -0
  151. package/skills/gsd-discuss-phase/SKILL.md +54 -0
  152. package/skills/gsd-execute-phase/SKILL.md +49 -0
  153. package/skills/gsd-plan-phase/SKILL.md +37 -0
  154. package/skills/gsd-ui-phase/SKILL.md +24 -0
  155. package/skills/gsd-ui-review/SKILL.md +24 -0
  156. package/skills/gsd-verify-work/SKILL.md +30 -0
@@ -4,17 +4,21 @@
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
- const { safeReadFile, output, error } = require('./core.cjs');
7
+ const { safeReadFile, normalizeMd, output, error } = require('./core.cjs');
8
8
 
9
9
  // ─── Parsing engine ───────────────────────────────────────────────────────────
10
10
 
11
11
  function extractFrontmatter(content) {
12
12
  const frontmatter = {};
13
- const match = content.match(/^---\n([\s\S]+?)\n---/);
13
+ // Find ALL frontmatter blocks at the start of the file.
14
+ // If multiple blocks exist (corruption from CRLF mismatch), use the LAST one
15
+ // since it represents the most recent state sync.
16
+ const allBlocks = [...content.matchAll(/(?:^|\n)\s*---\r?\n([\s\S]+?)\r?\n---/g)];
17
+ const match = allBlocks.length > 0 ? allBlocks[allBlocks.length - 1] : null;
14
18
  if (!match) return frontmatter;
15
19
 
16
20
  const yaml = match[1];
17
- const lines = yaml.split('\n');
21
+ const lines = yaml.split(/\r?\n/);
18
22
 
19
23
  // Stack to track nested objects: [{obj, key, indent}]
20
24
  // obj = object to write to, key = current key collecting array items, indent = indentation level
@@ -149,7 +153,7 @@ function reconstructFrontmatter(obj) {
149
153
 
150
154
  function spliceFrontmatter(content, newObj) {
151
155
  const yamlStr = reconstructFrontmatter(newObj);
152
- const match = content.match(/^---\n[\s\S]+?\n---/);
156
+ const match = content.match(/^---\r?\n[\s\S]+?\r?\n---/);
153
157
  if (match) {
154
158
  return `---\n${yamlStr}\n---` + content.slice(match[0].length);
155
159
  }
@@ -159,61 +163,90 @@ function spliceFrontmatter(content, newObj) {
159
163
  function parseMustHavesBlock(content, blockName) {
160
164
  // Extract a specific block from must_haves in raw frontmatter YAML
161
165
  // Handles 3-level nesting: must_haves > artifacts/key_links > [{path, provides, ...}]
162
- const fmMatch = content.match(/^---\n([\s\S]+?)\n---/);
166
+ const fmMatch = content.match(/^---\r?\n([\s\S]+?)\r?\n---/);
163
167
  if (!fmMatch) return [];
164
168
 
165
169
  const yaml = fmMatch[1];
166
- // Find the block (e.g., "truths:", "artifacts:", "key_links:")
167
- const blockPattern = new RegExp(`^\\s{4}${blockName}:\\s*$`, 'm');
168
- const blockStart = yaml.search(blockPattern);
170
+
171
+ // Find must_haves: first to detect its indentation level
172
+ const mustHavesMatch = yaml.match(/^(\s*)must_haves:\s*$/m);
173
+ if (!mustHavesMatch) return [];
174
+ const mustHavesIndent = mustHavesMatch[1].length;
175
+
176
+ // Find the block (e.g., "truths:", "artifacts:", "key_links:") under must_haves
177
+ // It must be indented more than must_haves but we detect the actual indent dynamically
178
+ const blockPattern = new RegExp(`^(\\s+)${blockName}:\\s*$`, 'm');
179
+ const blockMatch = yaml.match(blockPattern);
180
+ if (!blockMatch) return [];
181
+
182
+ const blockIndent = blockMatch[1].length;
183
+ // The block must be nested under must_haves (more indented)
184
+ if (blockIndent <= mustHavesIndent) return [];
185
+
186
+ // Find where the block starts in the yaml string
187
+ const blockStart = yaml.indexOf(blockMatch[0]);
169
188
  if (blockStart === -1) return [];
170
189
 
171
190
  const afterBlock = yaml.slice(blockStart);
172
- const blockLines = afterBlock.split('\n').slice(1); // skip the header line
191
+ const blockLines = afterBlock.split(/\r?\n/).slice(1); // skip the header line
173
192
 
193
+ // List items are indented one level deeper than blockIndent
194
+ // Continuation KVs are indented one level deeper than list items
174
195
  const items = [];
175
196
  let current = null;
197
+ let listItemIndent = -1; // detected from first "- " line
176
198
 
177
199
  for (const line of blockLines) {
178
- // Stop at same or lower indent level (non-continuation)
200
+ // Skip empty lines
179
201
  if (line.trim() === '') continue;
180
202
  const indent = line.match(/^(\s*)/)[1].length;
181
- if (indent <= 4 && line.trim() !== '') break; // back to must_haves level or higher
182
-
183
- if (line.match(/^\s{6}-\s+/)) {
184
- // New list item at 6-space indent
185
- if (current) items.push(current);
186
- current = {};
187
- // Check if it's a simple string item
188
- const simpleMatch = line.match(/^\s{6}-\s+"?([^"]+)"?\s*$/);
189
- if (simpleMatch && !line.includes(':')) {
190
- current = simpleMatch[1];
191
- } else {
192
- // Key-value on same line as dash: "- path: value"
193
- const kvMatch = line.match(/^\s{6}-\s+(\w+):\s*"?([^"]*)"?\s*$/);
194
- if (kvMatch) {
195
- current = {};
196
- current[kvMatch[1]] = kvMatch[2];
203
+ // Stop at same or lower indent level than the block header
204
+ if (indent <= blockIndent && line.trim() !== '') break;
205
+
206
+ const trimmed = line.trim();
207
+
208
+ if (trimmed.startsWith('- ')) {
209
+ // Detect list item indent from the first occurrence
210
+ if (listItemIndent === -1) listItemIndent = indent;
211
+
212
+ // Only treat as a top-level list item if at the expected indent
213
+ if (indent === listItemIndent) {
214
+ if (current) items.push(current);
215
+ current = {};
216
+ const afterDash = trimmed.slice(2);
217
+ // Check if it's a simple string item (no colon means not a key-value)
218
+ if (!afterDash.includes(':')) {
219
+ current = afterDash.replace(/^["']|["']$/g, '');
220
+ } else {
221
+ // Key-value on same line as dash: "- path: value"
222
+ const kvMatch = afterDash.match(/^(\w+):\s*"?([^"]*)"?\s*$/);
223
+ if (kvMatch) {
224
+ current = {};
225
+ current[kvMatch[1]] = kvMatch[2];
226
+ }
197
227
  }
228
+ continue;
198
229
  }
199
- } else if (current && typeof current === 'object') {
200
- // Continuation key-value at 8+ space indent
201
- const kvMatch = line.match(/^\s{8,}(\w+):\s*"?([^"]*)"?\s*$/);
202
- if (kvMatch) {
203
- const val = kvMatch[2];
204
- // Try to parse as number
205
- current[kvMatch[1]] = /^\d+$/.test(val) ? parseInt(val, 10) : val;
206
- }
207
- // Array items under a key
208
- const arrMatch = line.match(/^\s{10,}-\s+"?([^"]+)"?\s*$/);
209
- if (arrMatch) {
210
- // Find the last key added and convert to array
230
+ }
231
+
232
+ if (current && typeof current === 'object' && indent > listItemIndent) {
233
+ // Continuation key-value or nested array item
234
+ if (trimmed.startsWith('- ')) {
235
+ // Array item under a key
236
+ const arrVal = trimmed.slice(2).replace(/^["']|["']$/g, '');
211
237
  const keys = Object.keys(current);
212
238
  const lastKey = keys[keys.length - 1];
213
239
  if (lastKey && !Array.isArray(current[lastKey])) {
214
240
  current[lastKey] = current[lastKey] ? [current[lastKey]] : [];
215
241
  }
216
- if (lastKey) current[lastKey].push(arrMatch[1]);
242
+ if (lastKey) current[lastKey].push(arrVal);
243
+ } else {
244
+ const kvMatch = trimmed.match(/^(\w+):\s*"?([^"]*)"?\s*$/);
245
+ if (kvMatch) {
246
+ const val = kvMatch[2];
247
+ // Try to parse as number
248
+ current[kvMatch[1]] = /^\d+$/.test(val) ? parseInt(val, 10) : val;
249
+ }
217
250
  }
218
251
  }
219
252
  }
@@ -232,6 +265,8 @@ const FRONTMATTER_SCHEMAS = {
232
265
 
233
266
  function cmdFrontmatterGet(cwd, filePath, field, raw) {
234
267
  if (!filePath) { error('file path required'); }
268
+ // Path traversal guard: reject null bytes
269
+ if (filePath.includes('\0')) { error('file path contains null bytes'); }
235
270
  const fullPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
236
271
  const content = safeReadFile(fullPath);
237
272
  if (!content) { output({ error: 'File not found', path: filePath }, raw); return; }
@@ -247,6 +282,8 @@ function cmdFrontmatterGet(cwd, filePath, field, raw) {
247
282
 
248
283
  function cmdFrontmatterSet(cwd, filePath, field, value, raw) {
249
284
  if (!filePath || !field || value === undefined) { error('file, field, and value required'); }
285
+ // Path traversal guard: reject null bytes
286
+ if (filePath.includes('\0')) { error('file path contains null bytes'); }
250
287
  const fullPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
251
288
  if (!fs.existsSync(fullPath)) { output({ error: 'File not found', path: filePath }, raw); return; }
252
289
  const content = fs.readFileSync(fullPath, 'utf-8');
@@ -255,7 +292,7 @@ function cmdFrontmatterSet(cwd, filePath, field, value, raw) {
255
292
  try { parsedValue = JSON.parse(value); } catch { parsedValue = value; }
256
293
  fm[field] = parsedValue;
257
294
  const newContent = spliceFrontmatter(content, fm);
258
- fs.writeFileSync(fullPath, newContent, 'utf-8');
295
+ fs.writeFileSync(fullPath, normalizeMd(newContent), 'utf-8');
259
296
  output({ updated: true, field, value: parsedValue }, raw, 'true');
260
297
  }
261
298
 
@@ -269,7 +306,7 @@ function cmdFrontmatterMerge(cwd, filePath, data, raw) {
269
306
  try { mergeData = JSON.parse(data); } catch { error('Invalid JSON for --data'); return; }
270
307
  Object.assign(fm, mergeData);
271
308
  const newContent = spliceFrontmatter(content, fm);
272
- fs.writeFileSync(fullPath, newContent, 'utf-8');
309
+ fs.writeFileSync(fullPath, normalizeMd(newContent), 'utf-8');
273
310
  output({ merged: true, fields: Object.keys(mergeData) }, raw, 'true');
274
311
  }
275
312