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