gsd-opencode 1.22.0 → 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.
- package/agents/gsd-advisor-researcher.md +112 -0
- package/agents/gsd-assumptions-analyzer.md +110 -0
- package/agents/gsd-codebase-mapper.md +1 -2
- package/agents/gsd-debugger.md +119 -2
- package/agents/gsd-executor.md +25 -4
- package/agents/gsd-integration-checker.md +1 -2
- package/agents/gsd-nyquist-auditor.md +1 -2
- package/agents/gsd-phase-researcher.md +151 -5
- package/agents/gsd-plan-checker.md +71 -5
- package/agents/gsd-planner.md +50 -4
- package/agents/gsd-project-researcher.md +29 -3
- package/agents/gsd-research-synthesizer.md +1 -2
- package/agents/gsd-roadmapper.md +30 -2
- package/agents/gsd-ui-auditor.md +445 -0
- package/agents/gsd-ui-checker.md +305 -0
- package/agents/gsd-ui-researcher.md +368 -0
- package/agents/gsd-user-profiler.md +173 -0
- package/agents/gsd-verifier.md +124 -4
- package/commands/gsd/gsd-add-backlog.md +76 -0
- package/commands/gsd/gsd-audit-uat.md +24 -0
- package/commands/gsd/gsd-autonomous.md +41 -0
- package/commands/gsd/gsd-debug.md +5 -0
- package/commands/gsd/gsd-discuss-phase.md +10 -36
- package/commands/gsd/gsd-do.md +30 -0
- package/commands/gsd/gsd-execute-phase.md +20 -2
- package/commands/gsd/gsd-fast.md +30 -0
- package/commands/gsd/gsd-forensics.md +56 -0
- package/commands/gsd/gsd-list-workspaces.md +19 -0
- package/commands/gsd/gsd-manager.md +39 -0
- package/commands/gsd/gsd-milestone-summary.md +51 -0
- package/commands/gsd/gsd-new-workspace.md +44 -0
- package/commands/gsd/gsd-next.md +24 -0
- package/commands/gsd/gsd-note.md +34 -0
- package/commands/gsd/gsd-plan-phase.md +3 -1
- package/commands/gsd/gsd-plant-seed.md +28 -0
- package/commands/gsd/gsd-pr-branch.md +25 -0
- package/commands/gsd/gsd-profile-user.md +46 -0
- package/commands/gsd/gsd-quick.md +4 -2
- package/commands/gsd/gsd-reapply-patches.md +10 -6
- package/commands/gsd/gsd-remove-workspace.md +26 -0
- package/commands/gsd/gsd-research-phase.md +5 -0
- package/commands/gsd/gsd-resume-work.md +1 -1
- package/commands/gsd/gsd-review-backlog.md +61 -0
- package/commands/gsd/gsd-review.md +37 -0
- package/commands/gsd/gsd-session-report.md +19 -0
- package/commands/gsd/gsd-set-profile.md +24 -23
- package/commands/gsd/gsd-ship.md +23 -0
- package/commands/gsd/gsd-stats.md +18 -0
- package/commands/gsd/gsd-thread.md +127 -0
- package/commands/gsd/gsd-ui-phase.md +34 -0
- package/commands/gsd/gsd-ui-review.md +32 -0
- package/commands/gsd/gsd-workstreams.md +66 -0
- package/get-shit-done/bin/gsd-tools.cjs +410 -84
- package/get-shit-done/bin/lib/commands.cjs +429 -18
- package/get-shit-done/bin/lib/config.cjs +318 -45
- package/get-shit-done/bin/lib/core.cjs +822 -84
- package/get-shit-done/bin/lib/frontmatter.cjs +78 -41
- package/get-shit-done/bin/lib/init.cjs +836 -104
- package/get-shit-done/bin/lib/milestone.cjs +44 -33
- package/get-shit-done/bin/lib/model-profiles.cjs +68 -0
- package/get-shit-done/bin/lib/phase.cjs +293 -306
- package/get-shit-done/bin/lib/profile-output.cjs +952 -0
- package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
- package/get-shit-done/bin/lib/roadmap.cjs +55 -24
- package/get-shit-done/bin/lib/security.cjs +382 -0
- package/get-shit-done/bin/lib/state.cjs +363 -53
- package/get-shit-done/bin/lib/template.cjs +2 -2
- package/get-shit-done/bin/lib/uat.cjs +282 -0
- package/get-shit-done/bin/lib/verify.cjs +104 -36
- package/get-shit-done/bin/lib/workstream.cjs +491 -0
- package/get-shit-done/references/checkpoints.md +12 -10
- package/get-shit-done/references/decimal-phase-calculation.md +2 -3
- package/get-shit-done/references/git-integration.md +47 -0
- package/get-shit-done/references/model-profile-resolution.md +2 -0
- package/get-shit-done/references/model-profiles.md +62 -16
- package/get-shit-done/references/phase-argument-parsing.md +2 -2
- package/get-shit-done/references/planning-config.md +3 -1
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/workstream-flag.md +58 -0
- package/get-shit-done/templates/UAT.md +21 -3
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/claude-md.md +122 -0
- package/get-shit-done/templates/config.json +10 -3
- package/get-shit-done/templates/context.md +61 -6
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/phase-prompt.md +46 -5
- package/get-shit-done/templates/project.md +2 -0
- package/get-shit-done/templates/state.md +2 -2
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/workflows/add-phase.md +2 -2
- package/get-shit-done/workflows/add-tests.md +4 -4
- package/get-shit-done/workflows/add-todo.md +3 -3
- package/get-shit-done/workflows/audit-milestone.md +13 -5
- package/get-shit-done/workflows/audit-uat.md +109 -0
- package/get-shit-done/workflows/autonomous.md +891 -0
- package/get-shit-done/workflows/check-todos.md +2 -2
- package/get-shit-done/workflows/cleanup.md +4 -4
- package/get-shit-done/workflows/complete-milestone.md +9 -6
- package/get-shit-done/workflows/diagnose-issues.md +15 -3
- package/get-shit-done/workflows/discovery-phase.md +2 -2
- package/get-shit-done/workflows/discuss-phase-assumptions.md +653 -0
- package/get-shit-done/workflows/discuss-phase.md +411 -38
- package/get-shit-done/workflows/do.md +104 -0
- package/get-shit-done/workflows/execute-phase.md +405 -18
- package/get-shit-done/workflows/execute-plan.md +77 -12
- package/get-shit-done/workflows/fast.md +105 -0
- package/get-shit-done/workflows/forensics.md +265 -0
- package/get-shit-done/workflows/health.md +28 -6
- package/get-shit-done/workflows/help.md +124 -7
- package/get-shit-done/workflows/insert-phase.md +2 -2
- package/get-shit-done/workflows/list-phase-assumptions.md +2 -2
- package/get-shit-done/workflows/list-workspaces.md +56 -0
- package/get-shit-done/workflows/manager.md +362 -0
- package/get-shit-done/workflows/map-codebase.md +74 -13
- package/get-shit-done/workflows/milestone-summary.md +223 -0
- package/get-shit-done/workflows/new-milestone.md +120 -18
- package/get-shit-done/workflows/new-project.md +178 -39
- package/get-shit-done/workflows/new-workspace.md +237 -0
- package/get-shit-done/workflows/next.md +97 -0
- package/get-shit-done/workflows/node-repair.md +92 -0
- package/get-shit-done/workflows/note.md +156 -0
- package/get-shit-done/workflows/pause-work.md +62 -8
- package/get-shit-done/workflows/plan-milestone-gaps.md +4 -5
- package/get-shit-done/workflows/plan-phase.md +332 -33
- package/get-shit-done/workflows/plant-seed.md +169 -0
- package/get-shit-done/workflows/pr-branch.md +129 -0
- package/get-shit-done/workflows/profile-user.md +450 -0
- package/get-shit-done/workflows/progress.md +145 -20
- package/get-shit-done/workflows/quick.md +205 -49
- package/get-shit-done/workflows/remove-phase.md +2 -2
- package/get-shit-done/workflows/remove-workspace.md +90 -0
- package/get-shit-done/workflows/research-phase.md +11 -3
- package/get-shit-done/workflows/resume-project.md +35 -16
- package/get-shit-done/workflows/review.md +228 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/set-profile.md +2 -2
- package/get-shit-done/workflows/settings.md +80 -11
- package/get-shit-done/workflows/ship.md +228 -0
- package/get-shit-done/workflows/stats.md +60 -0
- package/get-shit-done/workflows/transition.md +147 -20
- package/get-shit-done/workflows/ui-phase.md +302 -0
- package/get-shit-done/workflows/ui-review.md +165 -0
- package/get-shit-done/workflows/update.md +108 -25
- package/get-shit-done/workflows/validate-phase.md +15 -8
- package/get-shit-done/workflows/verify-phase.md +16 -5
- package/get-shit-done/workflows/verify-work.md +72 -18
- package/package.json +1 -1
- package/skills/gsd-audit-milestone/SKILL.md +29 -0
- package/skills/gsd-cleanup/SKILL.md +19 -0
- package/skills/gsd-complete-milestone/SKILL.md +131 -0
- package/skills/gsd-discuss-phase/SKILL.md +54 -0
- package/skills/gsd-execute-phase/SKILL.md +49 -0
- package/skills/gsd-plan-phase/SKILL.md +37 -0
- package/skills/gsd-ui-phase/SKILL.md +24 -0
- package/skills/gsd-ui-review/SKILL.md +24 -0
- 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
|
-
|
|
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(
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
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(
|
|
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
|
-
//
|
|
200
|
+
// Skip empty lines
|
|
179
201
|
if (line.trim() === '') continue;
|
|
180
202
|
const indent = line.match(/^(\s*)/)[1].length;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
//
|
|
205
|
-
|
|
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(
|
|
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
|
|