gsd-code-first 1.0.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/LICENSE +21 -0
- package/README.ja-JP.md +834 -0
- package/README.ko-KR.md +823 -0
- package/README.md +937 -0
- package/README.pt-BR.md +452 -0
- package/README.zh-CN.md +800 -0
- package/agents/gsd-advisor-researcher.md +104 -0
- package/agents/gsd-annotator.md +148 -0
- package/agents/gsd-arc-executor.md +537 -0
- package/agents/gsd-arc-planner.md +374 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-planner.md +155 -0
- package/agents/gsd-codebase-mapper.md +770 -0
- package/agents/gsd-debugger.md +1373 -0
- package/agents/gsd-executor.md +509 -0
- package/agents/gsd-integration-checker.md +443 -0
- package/agents/gsd-nyquist-auditor.md +176 -0
- package/agents/gsd-phase-researcher.md +698 -0
- package/agents/gsd-plan-checker.md +773 -0
- package/agents/gsd-planner.md +1354 -0
- package/agents/gsd-project-researcher.md +654 -0
- package/agents/gsd-prototyper.md +161 -0
- package/agents/gsd-research-synthesizer.md +247 -0
- package/agents/gsd-roadmapper.md +679 -0
- package/agents/gsd-ui-auditor.md +439 -0
- package/agents/gsd-ui-checker.md +300 -0
- package/agents/gsd-ui-researcher.md +357 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +700 -0
- package/bin/install.js +5009 -0
- package/commands/gsd/add-backlog.md +76 -0
- package/commands/gsd/add-phase.md +43 -0
- package/commands/gsd/add-tests.md +41 -0
- package/commands/gsd/add-todo.md +47 -0
- package/commands/gsd/annotate.md +54 -0
- package/commands/gsd/audit-milestone.md +36 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +41 -0
- package/commands/gsd/check-todos.md +45 -0
- package/commands/gsd/cleanup.md +18 -0
- package/commands/gsd/complete-milestone.md +136 -0
- package/commands/gsd/debug.md +173 -0
- package/commands/gsd/deep-plan.md +52 -0
- package/commands/gsd/discuss-phase.md +64 -0
- package/commands/gsd/do.md +30 -0
- package/commands/gsd/execute-phase.md +59 -0
- package/commands/gsd/extract-plan.md +35 -0
- package/commands/gsd/fast.md +30 -0
- package/commands/gsd/forensics.md +56 -0
- package/commands/gsd/health.md +22 -0
- package/commands/gsd/help.md +22 -0
- package/commands/gsd/insert-phase.md +32 -0
- package/commands/gsd/iterate.md +124 -0
- package/commands/gsd/join-discord.md +18 -0
- package/commands/gsd/list-phase-assumptions.md +46 -0
- package/commands/gsd/list-workspaces.md +19 -0
- package/commands/gsd/manager.md +39 -0
- package/commands/gsd/map-codebase.md +71 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/new-milestone.md +44 -0
- package/commands/gsd/new-project.md +42 -0
- package/commands/gsd/new-workspace.md +44 -0
- package/commands/gsd/next.md +24 -0
- package/commands/gsd/note.md +34 -0
- package/commands/gsd/pause-work.md +38 -0
- package/commands/gsd/plan-milestone-gaps.md +34 -0
- package/commands/gsd/plan-phase.md +47 -0
- package/commands/gsd/plant-seed.md +28 -0
- package/commands/gsd/pr-branch.md +25 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +24 -0
- package/commands/gsd/prototype.md +56 -0
- package/commands/gsd/quick.md +47 -0
- package/commands/gsd/reapply-patches.md +123 -0
- package/commands/gsd/remove-phase.md +31 -0
- package/commands/gsd/remove-workspace.md +26 -0
- package/commands/gsd/research-phase.md +195 -0
- package/commands/gsd/resume-work.md +40 -0
- package/commands/gsd/review-backlog.md +61 -0
- package/commands/gsd/review.md +37 -0
- package/commands/gsd/session-report.md +19 -0
- package/commands/gsd/set-mode.md +41 -0
- package/commands/gsd/set-profile.md +12 -0
- package/commands/gsd/settings.md +36 -0
- package/commands/gsd/ship.md +23 -0
- package/commands/gsd/stats.md +18 -0
- package/commands/gsd/thread.md +127 -0
- package/commands/gsd/ui-phase.md +34 -0
- package/commands/gsd/ui-review.md +32 -0
- package/commands/gsd/update.md +37 -0
- package/commands/gsd/validate-phase.md +35 -0
- package/commands/gsd/verify-work.md +38 -0
- package/commands/gsd/workstreams.md +63 -0
- package/get-shit-done/bin/gsd-tools.cjs +946 -0
- package/get-shit-done/bin/lib/arc-scanner.cjs +341 -0
- package/get-shit-done/bin/lib/commands.cjs +959 -0
- package/get-shit-done/bin/lib/config.cjs +466 -0
- package/get-shit-done/bin/lib/core.cjs +1230 -0
- package/get-shit-done/bin/lib/frontmatter.cjs +336 -0
- package/get-shit-done/bin/lib/init.cjs +1442 -0
- package/get-shit-done/bin/lib/milestone.cjs +252 -0
- package/get-shit-done/bin/lib/model-profiles.cjs +68 -0
- package/get-shit-done/bin/lib/phase.cjs +888 -0
- 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 +329 -0
- package/get-shit-done/bin/lib/security.cjs +382 -0
- package/get-shit-done/bin/lib/state.cjs +1031 -0
- package/get-shit-done/bin/lib/template.cjs +222 -0
- package/get-shit-done/bin/lib/uat.cjs +282 -0
- package/get-shit-done/bin/lib/verify.cjs +888 -0
- package/get-shit-done/bin/lib/workstream.cjs +491 -0
- package/get-shit-done/commands/gsd/workstreams.md +63 -0
- package/get-shit-done/references/arc-standard.md +315 -0
- package/get-shit-done/references/checkpoints.md +778 -0
- package/get-shit-done/references/continuation-format.md +249 -0
- package/get-shit-done/references/decimal-phase-calculation.md +64 -0
- package/get-shit-done/references/git-integration.md +295 -0
- package/get-shit-done/references/git-planning-commit.md +38 -0
- package/get-shit-done/references/model-profile-resolution.md +36 -0
- package/get-shit-done/references/model-profiles.md +139 -0
- package/get-shit-done/references/phase-argument-parsing.md +61 -0
- package/get-shit-done/references/planning-config.md +202 -0
- package/get-shit-done/references/questioning.md +162 -0
- package/get-shit-done/references/tdd.md +263 -0
- package/get-shit-done/references/ui-brand.md +160 -0
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/verification-patterns.md +612 -0
- package/get-shit-done/references/workstream-flag.md +58 -0
- package/get-shit-done/templates/DEBUG.md +164 -0
- package/get-shit-done/templates/UAT.md +265 -0
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/VALIDATION.md +76 -0
- package/get-shit-done/templates/claude-md.md +122 -0
- package/get-shit-done/templates/codebase/architecture.md +255 -0
- package/get-shit-done/templates/codebase/concerns.md +310 -0
- package/get-shit-done/templates/codebase/conventions.md +307 -0
- package/get-shit-done/templates/codebase/integrations.md +280 -0
- package/get-shit-done/templates/codebase/stack.md +186 -0
- package/get-shit-done/templates/codebase/structure.md +285 -0
- package/get-shit-done/templates/codebase/testing.md +480 -0
- package/get-shit-done/templates/config.json +44 -0
- package/get-shit-done/templates/context.md +352 -0
- package/get-shit-done/templates/continue-here.md +78 -0
- package/get-shit-done/templates/copilot-instructions.md +7 -0
- package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discovery.md +146 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/milestone-archive.md +123 -0
- package/get-shit-done/templates/milestone.md +115 -0
- package/get-shit-done/templates/phase-prompt.md +610 -0
- package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
- package/get-shit-done/templates/project.md +186 -0
- package/get-shit-done/templates/requirements.md +231 -0
- package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
- package/get-shit-done/templates/research-project/FEATURES.md +147 -0
- package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
- package/get-shit-done/templates/research-project/STACK.md +120 -0
- package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
- package/get-shit-done/templates/research.md +552 -0
- package/get-shit-done/templates/retrospective.md +54 -0
- package/get-shit-done/templates/roadmap.md +202 -0
- package/get-shit-done/templates/state.md +176 -0
- package/get-shit-done/templates/summary-complex.md +59 -0
- package/get-shit-done/templates/summary-minimal.md +41 -0
- package/get-shit-done/templates/summary-standard.md +48 -0
- package/get-shit-done/templates/summary.md +248 -0
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/templates/user-setup.md +311 -0
- package/get-shit-done/templates/verification-report.md +322 -0
- package/get-shit-done/workflows/add-phase.md +112 -0
- package/get-shit-done/workflows/add-tests.md +351 -0
- package/get-shit-done/workflows/add-todo.md +158 -0
- package/get-shit-done/workflows/audit-milestone.md +340 -0
- 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 +177 -0
- package/get-shit-done/workflows/cleanup.md +152 -0
- package/get-shit-done/workflows/complete-milestone.md +767 -0
- package/get-shit-done/workflows/diagnose-issues.md +231 -0
- package/get-shit-done/workflows/discovery-phase.md +289 -0
- package/get-shit-done/workflows/discuss-phase-assumptions.md +653 -0
- package/get-shit-done/workflows/discuss-phase.md +1049 -0
- package/get-shit-done/workflows/do.md +104 -0
- package/get-shit-done/workflows/execute-phase.md +846 -0
- package/get-shit-done/workflows/execute-plan.md +514 -0
- 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 +181 -0
- package/get-shit-done/workflows/help.md +634 -0
- package/get-shit-done/workflows/insert-phase.md +130 -0
- package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
- 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 +377 -0
- package/get-shit-done/workflows/milestone-summary.md +223 -0
- package/get-shit-done/workflows/new-milestone.md +486 -0
- package/get-shit-done/workflows/new-project.md +1250 -0
- 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 +176 -0
- package/get-shit-done/workflows/plan-milestone-gaps.md +273 -0
- package/get-shit-done/workflows/plan-phase.md +859 -0
- 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 +507 -0
- package/get-shit-done/workflows/quick.md +757 -0
- package/get-shit-done/workflows/remove-phase.md +155 -0
- package/get-shit-done/workflows/remove-workspace.md +90 -0
- package/get-shit-done/workflows/research-phase.md +82 -0
- package/get-shit-done/workflows/resume-project.md +326 -0
- package/get-shit-done/workflows/review.md +228 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/settings.md +283 -0
- 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 +671 -0
- 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 +323 -0
- package/get-shit-done/workflows/validate-phase.md +174 -0
- package/get-shit-done/workflows/verify-phase.md +254 -0
- package/get-shit-done/workflows/verify-work.md +637 -0
- package/hooks/dist/gsd-check-update.js +114 -0
- package/hooks/dist/gsd-context-monitor.js +156 -0
- package/hooks/dist/gsd-prompt-guard.js +96 -0
- package/hooks/dist/gsd-statusline.js +119 -0
- package/hooks/dist/gsd-workflow-guard.js +94 -0
- package/package.json +52 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +82 -0
- package/scripts/prompt-injection-scan.sh +198 -0
- package/scripts/run-tests.cjs +29 -0
- package/scripts/secret-scan.sh +227 -0
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSD Tools — CLI utility for GSD workflow operations
|
|
5
|
+
*
|
|
6
|
+
* Replaces repetitive inline bash patterns across ~50 GSD command/workflow/agent files.
|
|
7
|
+
* Centralizes: config parsing, model resolution, phase lookup, git commits, summary verification.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node gsd-tools.cjs <command> [args] [--raw] [--pick <field>]
|
|
10
|
+
*
|
|
11
|
+
* Atomic Commands:
|
|
12
|
+
* state load Load project config + state
|
|
13
|
+
* state json Output STATE.md frontmatter as JSON
|
|
14
|
+
* state update <field> <value> Update a STATE.md field
|
|
15
|
+
* state get [section] Get STATE.md content or section
|
|
16
|
+
* state patch --field val ... Batch update STATE.md fields
|
|
17
|
+
* state begin-phase --phase N --name S --plans C Update STATE.md for new phase start
|
|
18
|
+
* state signal-waiting --type T --question Q --options "A|B" --phase P Write WAITING.json signal
|
|
19
|
+
* state signal-resume Remove WAITING.json signal
|
|
20
|
+
* resolve-model <agent-type> Get model for agent based on profile
|
|
21
|
+
* find-phase <phase> Find phase directory by number
|
|
22
|
+
* commit <message> [--files f1 f2] [--no-verify] Commit planning docs
|
|
23
|
+
* commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
|
|
24
|
+
* verify-summary <path> Verify a SUMMARY.md file
|
|
25
|
+
* generate-slug <text> Convert text to URL-safe slug
|
|
26
|
+
* current-timestamp [format] Get timestamp (full|date|filename)
|
|
27
|
+
* list-todos [area] Count and enumerate pending todos
|
|
28
|
+
* verify-path-exists <path> Check file/directory existence
|
|
29
|
+
* config-ensure-section Initialize .planning/config.json
|
|
30
|
+
* history-digest Aggregate all SUMMARY.md data
|
|
31
|
+
* summary-extract <path> [--fields] Extract structured data from SUMMARY.md
|
|
32
|
+
* state-snapshot Structured parse of STATE.md
|
|
33
|
+
* phase-plan-index <phase> Index plans with waves and status
|
|
34
|
+
* websearch <query> Search web via Brave API (if configured)
|
|
35
|
+
* [--limit N] [--freshness day|week|month]
|
|
36
|
+
*
|
|
37
|
+
* Phase Operations:
|
|
38
|
+
* phase next-decimal <phase> Calculate next decimal phase number
|
|
39
|
+
* phase add <description> [--id ID] Append new phase to roadmap + create dir
|
|
40
|
+
* phase insert <after> <description> Insert decimal phase after existing
|
|
41
|
+
* phase remove <phase> [--force] Remove phase, renumber all subsequent
|
|
42
|
+
* phase complete <phase> Mark phase done, update state + roadmap
|
|
43
|
+
*
|
|
44
|
+
* Roadmap Operations:
|
|
45
|
+
* roadmap get-phase <phase> Extract phase section from ROADMAP.md
|
|
46
|
+
* roadmap analyze Full roadmap parse with disk status
|
|
47
|
+
* roadmap update-plan-progress <N> Update progress table row from disk (PLAN vs SUMMARY counts)
|
|
48
|
+
*
|
|
49
|
+
* Requirements Operations:
|
|
50
|
+
* requirements mark-complete <ids> Mark requirement IDs as complete in REQUIREMENTS.md
|
|
51
|
+
* Accepts: REQ-01,REQ-02 or REQ-01 REQ-02 or [REQ-01, REQ-02]
|
|
52
|
+
*
|
|
53
|
+
* Milestone Operations:
|
|
54
|
+
* milestone complete <version> Archive milestone, create MILESTONES.md
|
|
55
|
+
* [--name <name>]
|
|
56
|
+
* [--archive-phases] Move phase dirs to milestones/vX.Y-phases/
|
|
57
|
+
*
|
|
58
|
+
* Validation:
|
|
59
|
+
* validate consistency Check phase numbering, disk/roadmap sync
|
|
60
|
+
* validate health [--repair] Check .planning/ integrity, optionally repair
|
|
61
|
+
* validate agents Check GSD agent installation status
|
|
62
|
+
*
|
|
63
|
+
* Progress:
|
|
64
|
+
* progress [json|table|bar] Render progress in various formats
|
|
65
|
+
*
|
|
66
|
+
* Todos:
|
|
67
|
+
* todo complete <filename> Move todo from pending to completed
|
|
68
|
+
*
|
|
69
|
+
* UAT Audit:
|
|
70
|
+
* audit-uat Scan all phases for unresolved UAT/verification items
|
|
71
|
+
* uat render-checkpoint --file <path> Render the current UAT checkpoint block
|
|
72
|
+
*
|
|
73
|
+
* Scaffolding:
|
|
74
|
+
* scaffold context --phase <N> Create CONTEXT.md template
|
|
75
|
+
* scaffold uat --phase <N> Create UAT.md template
|
|
76
|
+
* scaffold verification --phase <N> Create VERIFICATION.md template
|
|
77
|
+
* scaffold phase-dir --phase <N> Create phase directory
|
|
78
|
+
* --name <name>
|
|
79
|
+
*
|
|
80
|
+
* Frontmatter CRUD:
|
|
81
|
+
* frontmatter get <file> [--field k] Extract frontmatter as JSON
|
|
82
|
+
* frontmatter set <file> --field k Update single frontmatter field
|
|
83
|
+
* --value jsonVal
|
|
84
|
+
* frontmatter merge <file> Merge JSON into frontmatter
|
|
85
|
+
* --data '{json}'
|
|
86
|
+
* frontmatter validate <file> Validate required fields
|
|
87
|
+
* --schema plan|summary|verification
|
|
88
|
+
*
|
|
89
|
+
* Verification Suite:
|
|
90
|
+
* verify plan-structure <file> Check PLAN.md structure + tasks
|
|
91
|
+
* verify phase-completeness <phase> Check all plans have summaries
|
|
92
|
+
* verify references <file> Check @-refs + paths resolve
|
|
93
|
+
* verify commits <h1> [h2] ... Batch verify commit hashes
|
|
94
|
+
* verify artifacts <plan-file> Check must_haves.artifacts
|
|
95
|
+
* verify key-links <plan-file> Check must_haves.key_links
|
|
96
|
+
*
|
|
97
|
+
* Template Fill:
|
|
98
|
+
* template fill summary --phase N Create pre-filled SUMMARY.md
|
|
99
|
+
* [--plan M] [--name "..."]
|
|
100
|
+
* [--fields '{json}']
|
|
101
|
+
* template fill plan --phase N Create pre-filled PLAN.md
|
|
102
|
+
* [--plan M] [--type execute|tdd]
|
|
103
|
+
* [--wave N] [--fields '{json}']
|
|
104
|
+
* template fill verification Create pre-filled VERIFICATION.md
|
|
105
|
+
* --phase N [--fields '{json}']
|
|
106
|
+
*
|
|
107
|
+
* State Progression:
|
|
108
|
+
* state advance-plan Increment plan counter
|
|
109
|
+
* state record-metric --phase N Record execution metrics
|
|
110
|
+
* --plan M --duration Xmin
|
|
111
|
+
* [--tasks N] [--files N]
|
|
112
|
+
* state update-progress Recalculate progress bar
|
|
113
|
+
* state add-decision --summary "..." Add decision to STATE.md
|
|
114
|
+
* [--phase N] [--rationale "..."]
|
|
115
|
+
* [--summary-file path] [--rationale-file path]
|
|
116
|
+
* state add-blocker --text "..." Add blocker
|
|
117
|
+
* [--text-file path]
|
|
118
|
+
* state resolve-blocker --text "..." Remove blocker
|
|
119
|
+
* state record-session Update session continuity
|
|
120
|
+
* --stopped-at "..."
|
|
121
|
+
* [--resume-file path]
|
|
122
|
+
*
|
|
123
|
+
* Compound Commands (workflow-specific initialization):
|
|
124
|
+
* init execute-phase <phase> All context for execute-phase workflow
|
|
125
|
+
* init plan-phase <phase> All context for plan-phase workflow
|
|
126
|
+
* init new-project All context for new-project workflow
|
|
127
|
+
* init new-milestone All context for new-milestone workflow
|
|
128
|
+
* init quick <description> All context for quick workflow
|
|
129
|
+
* init resume All context for resume-project workflow
|
|
130
|
+
* init verify-work <phase> All context for verify-work workflow
|
|
131
|
+
* init phase-op <phase> Generic phase operation context
|
|
132
|
+
* init todos [area] All context for todo workflows
|
|
133
|
+
* init milestone-op All context for milestone operations
|
|
134
|
+
* init map-codebase All context for map-codebase workflow
|
|
135
|
+
* init progress All context for progress workflow
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
const fs = require('fs');
|
|
139
|
+
const path = require('path');
|
|
140
|
+
const core = require('./lib/core.cjs');
|
|
141
|
+
const { error, findProjectRoot, getActiveWorkstream } = core;
|
|
142
|
+
const state = require('./lib/state.cjs');
|
|
143
|
+
const phase = require('./lib/phase.cjs');
|
|
144
|
+
const roadmap = require('./lib/roadmap.cjs');
|
|
145
|
+
const verify = require('./lib/verify.cjs');
|
|
146
|
+
const config = require('./lib/config.cjs');
|
|
147
|
+
const template = require('./lib/template.cjs');
|
|
148
|
+
const milestone = require('./lib/milestone.cjs');
|
|
149
|
+
const commands = require('./lib/commands.cjs');
|
|
150
|
+
const init = require('./lib/init.cjs');
|
|
151
|
+
const frontmatter = require('./lib/frontmatter.cjs');
|
|
152
|
+
const profilePipeline = require('./lib/profile-pipeline.cjs');
|
|
153
|
+
const profileOutput = require('./lib/profile-output.cjs');
|
|
154
|
+
const workstream = require('./lib/workstream.cjs');
|
|
155
|
+
const arcScanner = require('./lib/arc-scanner.cjs');
|
|
156
|
+
|
|
157
|
+
// ─── Arg parsing helpers ──────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Extract named --flag <value> pairs from an args array.
|
|
161
|
+
* Returns an object mapping flag names to their values (null if absent).
|
|
162
|
+
* Flags listed in `booleanFlags` are treated as boolean (no value consumed).
|
|
163
|
+
*
|
|
164
|
+
* parseNamedArgs(args, 'phase', 'plan') → { phase: '3', plan: '1' }
|
|
165
|
+
* parseNamedArgs(args, [], ['amend', 'force']) → { amend: true, force: false }
|
|
166
|
+
*/
|
|
167
|
+
function parseNamedArgs(args, valueFlags = [], booleanFlags = []) {
|
|
168
|
+
const result = {};
|
|
169
|
+
for (const flag of valueFlags) {
|
|
170
|
+
const idx = args.indexOf(`--${flag}`);
|
|
171
|
+
result[flag] = idx !== -1 && args[idx + 1] !== undefined && !args[idx + 1].startsWith('--')
|
|
172
|
+
? args[idx + 1]
|
|
173
|
+
: null;
|
|
174
|
+
}
|
|
175
|
+
for (const flag of booleanFlags) {
|
|
176
|
+
result[flag] = args.includes(`--${flag}`);
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Collect all tokens after --flag until the next --flag or end of args.
|
|
183
|
+
* Handles multi-word values like --name Foo Bar Version 1.
|
|
184
|
+
* Returns null if the flag is absent.
|
|
185
|
+
*/
|
|
186
|
+
function parseMultiwordArg(args, flag) {
|
|
187
|
+
const idx = args.indexOf(`--${flag}`);
|
|
188
|
+
if (idx === -1) return null;
|
|
189
|
+
const tokens = [];
|
|
190
|
+
for (let i = idx + 1; i < args.length; i++) {
|
|
191
|
+
if (args[i].startsWith('--')) break;
|
|
192
|
+
tokens.push(args[i]);
|
|
193
|
+
}
|
|
194
|
+
return tokens.length > 0 ? tokens.join(' ') : null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ─── CLI Router ───────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
async function main() {
|
|
200
|
+
const args = process.argv.slice(2);
|
|
201
|
+
|
|
202
|
+
// Optional cwd override for sandboxed subagents running outside project root.
|
|
203
|
+
let cwd = process.cwd();
|
|
204
|
+
const cwdEqArg = args.find(arg => arg.startsWith('--cwd='));
|
|
205
|
+
const cwdIdx = args.indexOf('--cwd');
|
|
206
|
+
if (cwdEqArg) {
|
|
207
|
+
const value = cwdEqArg.slice('--cwd='.length).trim();
|
|
208
|
+
if (!value) error('Missing value for --cwd');
|
|
209
|
+
args.splice(args.indexOf(cwdEqArg), 1);
|
|
210
|
+
cwd = path.resolve(value);
|
|
211
|
+
} else if (cwdIdx !== -1) {
|
|
212
|
+
const value = args[cwdIdx + 1];
|
|
213
|
+
if (!value || value.startsWith('--')) error('Missing value for --cwd');
|
|
214
|
+
args.splice(cwdIdx, 2);
|
|
215
|
+
cwd = path.resolve(value);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {
|
|
219
|
+
error(`Invalid --cwd: ${cwd}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Resolve worktree root: in a linked worktree, .planning/ lives in the main worktree.
|
|
223
|
+
// However, in monorepo worktrees where the subdirectory itself owns .planning/,
|
|
224
|
+
// skip worktree resolution — the CWD is already the correct project root.
|
|
225
|
+
const { resolveWorktreeRoot } = require('./lib/core.cjs');
|
|
226
|
+
if (!fs.existsSync(path.join(cwd, '.planning'))) {
|
|
227
|
+
const worktreeRoot = resolveWorktreeRoot(cwd);
|
|
228
|
+
if (worktreeRoot !== cwd) {
|
|
229
|
+
cwd = worktreeRoot;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Optional workstream override for parallel milestone work.
|
|
234
|
+
// Priority: --ws flag > GSD_WORKSTREAM env var > active-workstream file > null (flat mode)
|
|
235
|
+
const wsEqArg = args.find(arg => arg.startsWith('--ws='));
|
|
236
|
+
const wsIdx = args.indexOf('--ws');
|
|
237
|
+
let ws = null;
|
|
238
|
+
if (wsEqArg) {
|
|
239
|
+
ws = wsEqArg.slice('--ws='.length).trim();
|
|
240
|
+
if (!ws) error('Missing value for --ws');
|
|
241
|
+
args.splice(args.indexOf(wsEqArg), 1);
|
|
242
|
+
} else if (wsIdx !== -1) {
|
|
243
|
+
ws = args[wsIdx + 1];
|
|
244
|
+
if (!ws || ws.startsWith('--')) error('Missing value for --ws');
|
|
245
|
+
args.splice(wsIdx, 2);
|
|
246
|
+
} else if (process.env.GSD_WORKSTREAM) {
|
|
247
|
+
ws = process.env.GSD_WORKSTREAM.trim();
|
|
248
|
+
} else {
|
|
249
|
+
ws = getActiveWorkstream(cwd);
|
|
250
|
+
}
|
|
251
|
+
// Validate workstream name to prevent path traversal attacks.
|
|
252
|
+
if (ws && !/^[a-zA-Z0-9_-]+$/.test(ws)) {
|
|
253
|
+
error('Invalid workstream name: must be alphanumeric, hyphens, and underscores only');
|
|
254
|
+
}
|
|
255
|
+
// Set env var so all modules (planningDir, planningPaths) auto-resolve workstream paths
|
|
256
|
+
if (ws) {
|
|
257
|
+
process.env.GSD_WORKSTREAM = ws;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const rawIndex = args.indexOf('--raw');
|
|
261
|
+
const raw = rawIndex !== -1;
|
|
262
|
+
if (rawIndex !== -1) args.splice(rawIndex, 1);
|
|
263
|
+
|
|
264
|
+
// --pick <name>: extract a single field from JSON output (replaces jq dependency).
|
|
265
|
+
// Supports dot-notation (e.g., --pick workflow.research) and bracket notation
|
|
266
|
+
// for arrays (e.g., --pick directories[-1]).
|
|
267
|
+
const pickIdx = args.indexOf('--pick');
|
|
268
|
+
let pickField = null;
|
|
269
|
+
if (pickIdx !== -1) {
|
|
270
|
+
pickField = args[pickIdx + 1];
|
|
271
|
+
if (!pickField || pickField.startsWith('--')) error('Missing value for --pick');
|
|
272
|
+
args.splice(pickIdx, 2);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const command = args[0];
|
|
276
|
+
|
|
277
|
+
if (!command) {
|
|
278
|
+
error('Usage: gsd-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init, workstream');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Multi-repo guard: resolve project root for commands that read/write .planning/.
|
|
282
|
+
// Skip for pure-utility commands that don't touch .planning/ to avoid unnecessary
|
|
283
|
+
// filesystem traversal on every invocation.
|
|
284
|
+
const SKIP_ROOT_RESOLUTION = new Set([
|
|
285
|
+
'generate-slug', 'current-timestamp', 'verify-path-exists',
|
|
286
|
+
'verify-summary', 'template', 'frontmatter',
|
|
287
|
+
]);
|
|
288
|
+
if (!SKIP_ROOT_RESOLUTION.has(command)) {
|
|
289
|
+
cwd = findProjectRoot(cwd);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// When --pick is active, intercept stdout to extract the requested field.
|
|
293
|
+
if (pickField) {
|
|
294
|
+
const origWriteSync = fs.writeSync;
|
|
295
|
+
const chunks = [];
|
|
296
|
+
fs.writeSync = function (fd, data, ...rest) {
|
|
297
|
+
if (fd === 1) { chunks.push(String(data)); return; }
|
|
298
|
+
return origWriteSync.call(fs, fd, data, ...rest);
|
|
299
|
+
};
|
|
300
|
+
const cleanup = () => {
|
|
301
|
+
fs.writeSync = origWriteSync;
|
|
302
|
+
const captured = chunks.join('');
|
|
303
|
+
let jsonStr = captured;
|
|
304
|
+
if (jsonStr.startsWith('@file:')) {
|
|
305
|
+
jsonStr = fs.readFileSync(jsonStr.slice(6), 'utf-8');
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const obj = JSON.parse(jsonStr);
|
|
309
|
+
const value = extractField(obj, pickField);
|
|
310
|
+
const result = value === null || value === undefined ? '' : String(value);
|
|
311
|
+
origWriteSync.call(fs, 1, result);
|
|
312
|
+
} catch {
|
|
313
|
+
origWriteSync.call(fs, 1, captured);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
try {
|
|
317
|
+
await runCommand(command, args, cwd, raw);
|
|
318
|
+
cleanup();
|
|
319
|
+
} catch (e) {
|
|
320
|
+
fs.writeSync = origWriteSync;
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
await runCommand(command, args, cwd, raw);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Extract a field from an object using dot-notation and bracket syntax.
|
|
331
|
+
* Supports: 'field', 'parent.child', 'arr[-1]', 'arr[0]'
|
|
332
|
+
*/
|
|
333
|
+
function extractField(obj, fieldPath) {
|
|
334
|
+
const parts = fieldPath.split('.');
|
|
335
|
+
let current = obj;
|
|
336
|
+
for (const part of parts) {
|
|
337
|
+
if (current === null || current === undefined) return undefined;
|
|
338
|
+
const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
|
|
339
|
+
if (bracketMatch) {
|
|
340
|
+
const key = bracketMatch[1];
|
|
341
|
+
const index = parseInt(bracketMatch[2], 10);
|
|
342
|
+
current = current[key];
|
|
343
|
+
if (!Array.isArray(current)) return undefined;
|
|
344
|
+
current = index < 0 ? current[current.length + index] : current[index];
|
|
345
|
+
} else {
|
|
346
|
+
current = current[part];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return current;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function runCommand(command, args, cwd, raw) {
|
|
353
|
+
switch (command) {
|
|
354
|
+
case 'state': {
|
|
355
|
+
const subcommand = args[1];
|
|
356
|
+
if (subcommand === 'json') {
|
|
357
|
+
state.cmdStateJson(cwd, raw);
|
|
358
|
+
} else if (subcommand === 'update') {
|
|
359
|
+
state.cmdStateUpdate(cwd, args[2], args[3]);
|
|
360
|
+
} else if (subcommand === 'get') {
|
|
361
|
+
state.cmdStateGet(cwd, args[2], raw);
|
|
362
|
+
} else if (subcommand === 'patch') {
|
|
363
|
+
const patches = {};
|
|
364
|
+
for (let i = 2; i < args.length; i += 2) {
|
|
365
|
+
const key = args[i].replace(/^--/, '');
|
|
366
|
+
const value = args[i + 1];
|
|
367
|
+
if (key && value !== undefined) {
|
|
368
|
+
patches[key] = value;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
state.cmdStatePatch(cwd, patches, raw);
|
|
372
|
+
} else if (subcommand === 'advance-plan') {
|
|
373
|
+
state.cmdStateAdvancePlan(cwd, raw);
|
|
374
|
+
} else if (subcommand === 'record-metric') {
|
|
375
|
+
const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
|
|
376
|
+
state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
|
|
377
|
+
} else if (subcommand === 'update-progress') {
|
|
378
|
+
state.cmdStateUpdateProgress(cwd, raw);
|
|
379
|
+
} else if (subcommand === 'add-decision') {
|
|
380
|
+
const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
|
|
381
|
+
state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
|
|
382
|
+
} else if (subcommand === 'add-blocker') {
|
|
383
|
+
const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
|
|
384
|
+
state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
|
|
385
|
+
} else if (subcommand === 'resolve-blocker') {
|
|
386
|
+
state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
|
|
387
|
+
} else if (subcommand === 'record-session') {
|
|
388
|
+
const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
|
|
389
|
+
state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
|
|
390
|
+
} else if (subcommand === 'begin-phase') {
|
|
391
|
+
const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
|
392
|
+
state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
|
|
393
|
+
} else if (subcommand === 'signal-waiting') {
|
|
394
|
+
const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
|
|
395
|
+
state.cmdSignalWaiting(cwd, type, question, options, p, raw);
|
|
396
|
+
} else if (subcommand === 'signal-resume') {
|
|
397
|
+
state.cmdSignalResume(cwd, raw);
|
|
398
|
+
} else {
|
|
399
|
+
state.cmdStateLoad(cwd, raw);
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
case 'resolve-model': {
|
|
405
|
+
commands.cmdResolveModel(cwd, args[1], raw);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
case 'find-phase': {
|
|
410
|
+
phase.cmdFindPhase(cwd, args[1], raw);
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
case 'commit': {
|
|
415
|
+
const amend = args.includes('--amend');
|
|
416
|
+
const noVerify = args.includes('--no-verify');
|
|
417
|
+
const filesIndex = args.indexOf('--files');
|
|
418
|
+
// Collect all positional args between command name and first flag,
|
|
419
|
+
// then join them — handles both quoted ("multi word msg") and
|
|
420
|
+
// unquoted (multi word msg) invocations from different shells
|
|
421
|
+
const endIndex = filesIndex !== -1 ? filesIndex : args.length;
|
|
422
|
+
const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
|
|
423
|
+
const message = messageArgs.join(' ') || undefined;
|
|
424
|
+
const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
|
|
425
|
+
commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
case 'commit-to-subrepo': {
|
|
430
|
+
const message = args[1];
|
|
431
|
+
const filesIndex = args.indexOf('--files');
|
|
432
|
+
const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
|
|
433
|
+
commands.cmdCommitToSubrepo(cwd, message, files, raw);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
case 'verify-summary': {
|
|
438
|
+
const summaryPath = args[1];
|
|
439
|
+
const countIndex = args.indexOf('--check-count');
|
|
440
|
+
const checkCount = countIndex !== -1 ? parseInt(args[countIndex + 1], 10) : 2;
|
|
441
|
+
verify.cmdVerifySummary(cwd, summaryPath, checkCount, raw);
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
case 'template': {
|
|
446
|
+
const subcommand = args[1];
|
|
447
|
+
if (subcommand === 'select') {
|
|
448
|
+
template.cmdTemplateSelect(cwd, args[2], raw);
|
|
449
|
+
} else if (subcommand === 'fill') {
|
|
450
|
+
const templateType = args[2];
|
|
451
|
+
const { phase, plan, name, type, wave, fields: fieldsRaw } = parseNamedArgs(args, ['phase', 'plan', 'name', 'type', 'wave', 'fields']);
|
|
452
|
+
let fields = {};
|
|
453
|
+
if (fieldsRaw) {
|
|
454
|
+
const { safeJsonParse } = require('./lib/security.cjs');
|
|
455
|
+
const result = safeJsonParse(fieldsRaw, { label: '--fields' });
|
|
456
|
+
if (!result.ok) error(result.error);
|
|
457
|
+
fields = result.value;
|
|
458
|
+
}
|
|
459
|
+
template.cmdTemplateFill(cwd, templateType, {
|
|
460
|
+
phase, plan, name, fields,
|
|
461
|
+
type: type || 'execute',
|
|
462
|
+
wave: wave || '1',
|
|
463
|
+
}, raw);
|
|
464
|
+
} else {
|
|
465
|
+
error('Unknown template subcommand. Available: select, fill');
|
|
466
|
+
}
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
case 'frontmatter': {
|
|
471
|
+
const subcommand = args[1];
|
|
472
|
+
const file = args[2];
|
|
473
|
+
if (subcommand === 'get') {
|
|
474
|
+
frontmatter.cmdFrontmatterGet(cwd, file, parseNamedArgs(args, ['field']).field, raw);
|
|
475
|
+
} else if (subcommand === 'set') {
|
|
476
|
+
const { field, value } = parseNamedArgs(args, ['field', 'value']);
|
|
477
|
+
frontmatter.cmdFrontmatterSet(cwd, file, field, value !== null ? value : undefined, raw);
|
|
478
|
+
} else if (subcommand === 'merge') {
|
|
479
|
+
frontmatter.cmdFrontmatterMerge(cwd, file, parseNamedArgs(args, ['data']).data, raw);
|
|
480
|
+
} else if (subcommand === 'validate') {
|
|
481
|
+
frontmatter.cmdFrontmatterValidate(cwd, file, parseNamedArgs(args, ['schema']).schema, raw);
|
|
482
|
+
} else {
|
|
483
|
+
error('Unknown frontmatter subcommand. Available: get, set, merge, validate');
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
case 'verify': {
|
|
489
|
+
const subcommand = args[1];
|
|
490
|
+
if (subcommand === 'plan-structure') {
|
|
491
|
+
verify.cmdVerifyPlanStructure(cwd, args[2], raw);
|
|
492
|
+
} else if (subcommand === 'phase-completeness') {
|
|
493
|
+
verify.cmdVerifyPhaseCompleteness(cwd, args[2], raw);
|
|
494
|
+
} else if (subcommand === 'references') {
|
|
495
|
+
verify.cmdVerifyReferences(cwd, args[2], raw);
|
|
496
|
+
} else if (subcommand === 'commits') {
|
|
497
|
+
verify.cmdVerifyCommits(cwd, args.slice(2), raw);
|
|
498
|
+
} else if (subcommand === 'artifacts') {
|
|
499
|
+
verify.cmdVerifyArtifacts(cwd, args[2], raw);
|
|
500
|
+
} else if (subcommand === 'key-links') {
|
|
501
|
+
verify.cmdVerifyKeyLinks(cwd, args[2], raw);
|
|
502
|
+
} else {
|
|
503
|
+
error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links');
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
case 'generate-slug': {
|
|
509
|
+
commands.cmdGenerateSlug(args[1], raw);
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
case 'current-timestamp': {
|
|
514
|
+
commands.cmdCurrentTimestamp(args[1] || 'full', raw);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
case 'list-todos': {
|
|
519
|
+
commands.cmdListTodos(cwd, args[1], raw);
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
case 'verify-path-exists': {
|
|
524
|
+
commands.cmdVerifyPathExists(cwd, args[1], raw);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
case 'config-ensure-section': {
|
|
529
|
+
config.cmdConfigEnsureSection(cwd, raw);
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
case 'config-set': {
|
|
534
|
+
config.cmdConfigSet(cwd, args[1], args[2], raw);
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
case "config-set-model-profile": {
|
|
539
|
+
config.cmdConfigSetModelProfile(cwd, args[1], raw);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
case 'config-get': {
|
|
544
|
+
config.cmdConfigGet(cwd, args[1], raw);
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
case 'config-new-project': {
|
|
549
|
+
config.cmdConfigNewProject(cwd, args[1], raw);
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
case 'agent-skills': {
|
|
554
|
+
init.cmdAgentSkills(cwd, args[1], raw);
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
case 'history-digest': {
|
|
559
|
+
commands.cmdHistoryDigest(cwd, raw);
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
case 'phases': {
|
|
564
|
+
const subcommand = args[1];
|
|
565
|
+
if (subcommand === 'list') {
|
|
566
|
+
const typeIndex = args.indexOf('--type');
|
|
567
|
+
const phaseIndex = args.indexOf('--phase');
|
|
568
|
+
const options = {
|
|
569
|
+
type: typeIndex !== -1 ? args[typeIndex + 1] : null,
|
|
570
|
+
phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
|
|
571
|
+
includeArchived: args.includes('--include-archived'),
|
|
572
|
+
};
|
|
573
|
+
phase.cmdPhasesList(cwd, options, raw);
|
|
574
|
+
} else {
|
|
575
|
+
error('Unknown phases subcommand. Available: list');
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
case 'roadmap': {
|
|
581
|
+
const subcommand = args[1];
|
|
582
|
+
if (subcommand === 'get-phase') {
|
|
583
|
+
roadmap.cmdRoadmapGetPhase(cwd, args[2], raw);
|
|
584
|
+
} else if (subcommand === 'analyze') {
|
|
585
|
+
roadmap.cmdRoadmapAnalyze(cwd, raw);
|
|
586
|
+
} else if (subcommand === 'update-plan-progress') {
|
|
587
|
+
roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
|
|
588
|
+
} else {
|
|
589
|
+
error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress');
|
|
590
|
+
}
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
case 'requirements': {
|
|
595
|
+
const subcommand = args[1];
|
|
596
|
+
if (subcommand === 'mark-complete') {
|
|
597
|
+
milestone.cmdRequirementsMarkComplete(cwd, args.slice(2), raw);
|
|
598
|
+
} else {
|
|
599
|
+
error('Unknown requirements subcommand. Available: mark-complete');
|
|
600
|
+
}
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
case 'phase': {
|
|
605
|
+
const subcommand = args[1];
|
|
606
|
+
if (subcommand === 'next-decimal') {
|
|
607
|
+
phase.cmdPhaseNextDecimal(cwd, args[2], raw);
|
|
608
|
+
} else if (subcommand === 'add') {
|
|
609
|
+
const idIdx = args.indexOf('--id');
|
|
610
|
+
let customId = null;
|
|
611
|
+
const descArgs = [];
|
|
612
|
+
for (let i = 2; i < args.length; i++) {
|
|
613
|
+
if (args[i] === '--id' && i + 1 < args.length) {
|
|
614
|
+
customId = args[i + 1];
|
|
615
|
+
i++; // skip value
|
|
616
|
+
} else {
|
|
617
|
+
descArgs.push(args[i]);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
|
|
621
|
+
} else if (subcommand === 'insert') {
|
|
622
|
+
phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
|
|
623
|
+
} else if (subcommand === 'remove') {
|
|
624
|
+
const forceFlag = args.includes('--force');
|
|
625
|
+
phase.cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
|
|
626
|
+
} else if (subcommand === 'complete') {
|
|
627
|
+
phase.cmdPhaseComplete(cwd, args[2], raw);
|
|
628
|
+
} else {
|
|
629
|
+
error('Unknown phase subcommand. Available: next-decimal, add, insert, remove, complete');
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
case 'milestone': {
|
|
635
|
+
const subcommand = args[1];
|
|
636
|
+
if (subcommand === 'complete') {
|
|
637
|
+
const milestoneName = parseMultiwordArg(args, 'name');
|
|
638
|
+
const archivePhases = args.includes('--archive-phases');
|
|
639
|
+
milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases }, raw);
|
|
640
|
+
} else {
|
|
641
|
+
error('Unknown milestone subcommand. Available: complete');
|
|
642
|
+
}
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
case 'validate': {
|
|
647
|
+
const subcommand = args[1];
|
|
648
|
+
if (subcommand === 'consistency') {
|
|
649
|
+
verify.cmdValidateConsistency(cwd, raw);
|
|
650
|
+
} else if (subcommand === 'health') {
|
|
651
|
+
const repairFlag = args.includes('--repair');
|
|
652
|
+
verify.cmdValidateHealth(cwd, { repair: repairFlag }, raw);
|
|
653
|
+
} else if (subcommand === 'agents') {
|
|
654
|
+
verify.cmdValidateAgents(cwd, raw);
|
|
655
|
+
} else {
|
|
656
|
+
error('Unknown validate subcommand. Available: consistency, health, agents');
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
case 'progress': {
|
|
662
|
+
const subcommand = args[1] || 'json';
|
|
663
|
+
commands.cmdProgressRender(cwd, subcommand, raw);
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
case 'audit-uat': {
|
|
668
|
+
const uat = require('./lib/uat.cjs');
|
|
669
|
+
uat.cmdAuditUat(cwd, raw);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
case 'uat': {
|
|
674
|
+
const subcommand = args[1];
|
|
675
|
+
const uat = require('./lib/uat.cjs');
|
|
676
|
+
if (subcommand === 'render-checkpoint') {
|
|
677
|
+
const options = parseNamedArgs(args, ['file']);
|
|
678
|
+
uat.cmdRenderCheckpoint(cwd, options, raw);
|
|
679
|
+
} else {
|
|
680
|
+
error('Unknown uat subcommand. Available: render-checkpoint');
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
case 'stats': {
|
|
686
|
+
const subcommand = args[1] || 'json';
|
|
687
|
+
commands.cmdStats(cwd, subcommand, raw);
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
case 'todo': {
|
|
692
|
+
const subcommand = args[1];
|
|
693
|
+
if (subcommand === 'complete') {
|
|
694
|
+
commands.cmdTodoComplete(cwd, args[2], raw);
|
|
695
|
+
} else if (subcommand === 'match-phase') {
|
|
696
|
+
commands.cmdTodoMatchPhase(cwd, args[2], raw);
|
|
697
|
+
} else {
|
|
698
|
+
error('Unknown todo subcommand. Available: complete, match-phase');
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
case 'scaffold': {
|
|
704
|
+
const scaffoldType = args[1];
|
|
705
|
+
const scaffoldOptions = {
|
|
706
|
+
phase: parseNamedArgs(args, ['phase']).phase,
|
|
707
|
+
name: parseMultiwordArg(args, 'name'),
|
|
708
|
+
};
|
|
709
|
+
commands.cmdScaffold(cwd, scaffoldType, scaffoldOptions, raw);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
case 'init': {
|
|
714
|
+
const workflow = args[1];
|
|
715
|
+
switch (workflow) {
|
|
716
|
+
case 'execute-phase':
|
|
717
|
+
init.cmdInitExecutePhase(cwd, args[2], raw);
|
|
718
|
+
break;
|
|
719
|
+
case 'plan-phase':
|
|
720
|
+
init.cmdInitPlanPhase(cwd, args[2], raw);
|
|
721
|
+
break;
|
|
722
|
+
case 'new-project':
|
|
723
|
+
init.cmdInitNewProject(cwd, raw);
|
|
724
|
+
break;
|
|
725
|
+
case 'new-milestone':
|
|
726
|
+
init.cmdInitNewMilestone(cwd, raw);
|
|
727
|
+
break;
|
|
728
|
+
case 'quick':
|
|
729
|
+
init.cmdInitQuick(cwd, args.slice(2).join(' '), raw);
|
|
730
|
+
break;
|
|
731
|
+
case 'resume':
|
|
732
|
+
init.cmdInitResume(cwd, raw);
|
|
733
|
+
break;
|
|
734
|
+
case 'verify-work':
|
|
735
|
+
init.cmdInitVerifyWork(cwd, args[2], raw);
|
|
736
|
+
break;
|
|
737
|
+
case 'phase-op':
|
|
738
|
+
init.cmdInitPhaseOp(cwd, args[2], raw);
|
|
739
|
+
break;
|
|
740
|
+
case 'todos':
|
|
741
|
+
init.cmdInitTodos(cwd, args[2], raw);
|
|
742
|
+
break;
|
|
743
|
+
case 'milestone-op':
|
|
744
|
+
init.cmdInitMilestoneOp(cwd, raw);
|
|
745
|
+
break;
|
|
746
|
+
case 'map-codebase':
|
|
747
|
+
init.cmdInitMapCodebase(cwd, raw);
|
|
748
|
+
break;
|
|
749
|
+
case 'progress':
|
|
750
|
+
init.cmdInitProgress(cwd, raw);
|
|
751
|
+
break;
|
|
752
|
+
case 'manager':
|
|
753
|
+
init.cmdInitManager(cwd, raw);
|
|
754
|
+
break;
|
|
755
|
+
case 'new-workspace':
|
|
756
|
+
init.cmdInitNewWorkspace(cwd, raw);
|
|
757
|
+
break;
|
|
758
|
+
case 'list-workspaces':
|
|
759
|
+
init.cmdInitListWorkspaces(cwd, raw);
|
|
760
|
+
break;
|
|
761
|
+
case 'remove-workspace':
|
|
762
|
+
init.cmdInitRemoveWorkspace(cwd, args[2], raw);
|
|
763
|
+
break;
|
|
764
|
+
default:
|
|
765
|
+
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`);
|
|
766
|
+
}
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
case 'phase-plan-index': {
|
|
771
|
+
phase.cmdPhasePlanIndex(cwd, args[1], raw);
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
case 'state-snapshot': {
|
|
776
|
+
state.cmdStateSnapshot(cwd, raw);
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
case 'summary-extract': {
|
|
781
|
+
const summaryPath = args[1];
|
|
782
|
+
const fieldsIndex = args.indexOf('--fields');
|
|
783
|
+
const fields = fieldsIndex !== -1 ? args[fieldsIndex + 1].split(',') : null;
|
|
784
|
+
commands.cmdSummaryExtract(cwd, summaryPath, fields, raw);
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
case 'websearch': {
|
|
789
|
+
const query = args[1];
|
|
790
|
+
const limitIdx = args.indexOf('--limit');
|
|
791
|
+
const freshnessIdx = args.indexOf('--freshness');
|
|
792
|
+
await commands.cmdWebsearch(query, {
|
|
793
|
+
limit: limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 10,
|
|
794
|
+
freshness: freshnessIdx !== -1 ? args[freshnessIdx + 1] : null,
|
|
795
|
+
}, raw);
|
|
796
|
+
break;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// ─── Profiling Pipeline ────────────────────────────────────────────────
|
|
800
|
+
|
|
801
|
+
case 'scan-sessions': {
|
|
802
|
+
const pathIdx = args.indexOf('--path');
|
|
803
|
+
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
804
|
+
const verboseFlag = args.includes('--verbose');
|
|
805
|
+
const jsonFlag = args.includes('--json');
|
|
806
|
+
await profilePipeline.cmdScanSessions(sessionsPath, { verbose: verboseFlag, json: jsonFlag }, raw);
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
case 'extract-messages': {
|
|
811
|
+
const sessionIdx = args.indexOf('--session');
|
|
812
|
+
const sessionId = sessionIdx !== -1 ? args[sessionIdx + 1] : null;
|
|
813
|
+
const limitIdx = args.indexOf('--limit');
|
|
814
|
+
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : null;
|
|
815
|
+
const pathIdx = args.indexOf('--path');
|
|
816
|
+
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
817
|
+
const projectArg = args[1];
|
|
818
|
+
if (!projectArg || projectArg.startsWith('--')) {
|
|
819
|
+
error('Usage: gsd-tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.');
|
|
820
|
+
}
|
|
821
|
+
await profilePipeline.cmdExtractMessages(projectArg, { sessionId, limit }, raw, sessionsPath);
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
case 'profile-sample': {
|
|
826
|
+
const pathIdx = args.indexOf('--path');
|
|
827
|
+
const sessionsPath = pathIdx !== -1 ? args[pathIdx + 1] : null;
|
|
828
|
+
const limitIdx = args.indexOf('--limit');
|
|
829
|
+
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 150;
|
|
830
|
+
const maxPerIdx = args.indexOf('--max-per-project');
|
|
831
|
+
const maxPerProject = maxPerIdx !== -1 ? parseInt(args[maxPerIdx + 1], 10) : null;
|
|
832
|
+
const maxCharsIdx = args.indexOf('--max-chars');
|
|
833
|
+
const maxChars = maxCharsIdx !== -1 ? parseInt(args[maxCharsIdx + 1], 10) : 500;
|
|
834
|
+
await profilePipeline.cmdProfileSample(sessionsPath, { limit, maxPerProject, maxChars }, raw);
|
|
835
|
+
break;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// ─── Profile Output ──────────────────────────────────────────────────
|
|
839
|
+
|
|
840
|
+
case 'write-profile': {
|
|
841
|
+
const inputIdx = args.indexOf('--input');
|
|
842
|
+
const inputPath = inputIdx !== -1 ? args[inputIdx + 1] : null;
|
|
843
|
+
if (!inputPath) error('--input <analysis-json-path> is required');
|
|
844
|
+
const outputIdx = args.indexOf('--output');
|
|
845
|
+
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
846
|
+
profileOutput.cmdWriteProfile(cwd, { input: inputPath, output: outputPath }, raw);
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
case 'profile-questionnaire': {
|
|
851
|
+
const answersIdx = args.indexOf('--answers');
|
|
852
|
+
const answers = answersIdx !== -1 ? args[answersIdx + 1] : null;
|
|
853
|
+
profileOutput.cmdProfileQuestionnaire({ answers }, raw);
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
case 'generate-dev-preferences': {
|
|
858
|
+
const analysisIdx = args.indexOf('--analysis');
|
|
859
|
+
const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
|
|
860
|
+
const outputIdx = args.indexOf('--output');
|
|
861
|
+
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
862
|
+
const stackIdx = args.indexOf('--stack');
|
|
863
|
+
const stack = stackIdx !== -1 ? args[stackIdx + 1] : null;
|
|
864
|
+
profileOutput.cmdGenerateDevPreferences(cwd, { analysis: analysisPath, output: outputPath, stack }, raw);
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
case 'generate-claude-profile': {
|
|
869
|
+
const analysisIdx = args.indexOf('--analysis');
|
|
870
|
+
const analysisPath = analysisIdx !== -1 ? args[analysisIdx + 1] : null;
|
|
871
|
+
const outputIdx = args.indexOf('--output');
|
|
872
|
+
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
873
|
+
const globalFlag = args.includes('--global');
|
|
874
|
+
profileOutput.cmdGenerateClaudeProfile(cwd, { analysis: analysisPath, output: outputPath, global: globalFlag }, raw);
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
case 'generate-claude-md': {
|
|
879
|
+
const outputIdx = args.indexOf('--output');
|
|
880
|
+
const outputPath = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
881
|
+
const autoFlag = args.includes('--auto');
|
|
882
|
+
const forceFlag = args.includes('--force');
|
|
883
|
+
profileOutput.cmdGenerateClaudeMd(cwd, { output: outputPath, auto: autoFlag, force: forceFlag }, raw);
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
case 'workstream': {
|
|
888
|
+
const subcommand = args[1];
|
|
889
|
+
if (subcommand === 'create') {
|
|
890
|
+
const migrateNameIdx = args.indexOf('--migrate-name');
|
|
891
|
+
const noMigrate = args.includes('--no-migrate');
|
|
892
|
+
workstream.cmdWorkstreamCreate(cwd, args[2], {
|
|
893
|
+
migrate: !noMigrate,
|
|
894
|
+
migrateName: migrateNameIdx !== -1 ? args[migrateNameIdx + 1] : null,
|
|
895
|
+
}, raw);
|
|
896
|
+
} else if (subcommand === 'list') {
|
|
897
|
+
workstream.cmdWorkstreamList(cwd, raw);
|
|
898
|
+
} else if (subcommand === 'status') {
|
|
899
|
+
workstream.cmdWorkstreamStatus(cwd, args[2], raw);
|
|
900
|
+
} else if (subcommand === 'complete') {
|
|
901
|
+
workstream.cmdWorkstreamComplete(cwd, args[2], {}, raw);
|
|
902
|
+
} else if (subcommand === 'set') {
|
|
903
|
+
workstream.cmdWorkstreamSet(cwd, args[2], raw);
|
|
904
|
+
} else if (subcommand === 'get') {
|
|
905
|
+
workstream.cmdWorkstreamGet(cwd, raw);
|
|
906
|
+
} else if (subcommand === 'progress') {
|
|
907
|
+
workstream.cmdWorkstreamProgress(cwd, raw);
|
|
908
|
+
} else {
|
|
909
|
+
error('Unknown workstream subcommand. Available: create, list, status, complete, set, get, progress');
|
|
910
|
+
}
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
case 'extract-tags': {
|
|
915
|
+
const allArgs = args.slice(1);
|
|
916
|
+
const { phase: phaseFilter, type: typeFilter, format, output: outputFile } = parseNamedArgs(allArgs, ['phase', 'type', 'format', 'output']);
|
|
917
|
+
const targetPath = allArgs.find(a => !a.startsWith('--')) || cwd;
|
|
918
|
+
arcScanner.cmdExtractTags(cwd, targetPath, {
|
|
919
|
+
phaseFilter,
|
|
920
|
+
typeFilter,
|
|
921
|
+
format: format || 'json',
|
|
922
|
+
outputFile,
|
|
923
|
+
});
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
case 'set-mode': {
|
|
928
|
+
const allArgs = args.slice(1);
|
|
929
|
+
const named = parseNamedArgs(allArgs, ['phase']);
|
|
930
|
+
const modeValue = allArgs.find(a => !a.startsWith('--'));
|
|
931
|
+
const VALID_MODES = new Set(['code-first', 'plan-first', 'hybrid']);
|
|
932
|
+
if (!modeValue || !VALID_MODES.has(modeValue)) {
|
|
933
|
+
error('Usage: set-mode <code-first|plan-first|hybrid> [--phase N]');
|
|
934
|
+
}
|
|
935
|
+
const keyPath = named.phase ? `phase_modes.${named.phase}` : 'default_phase_mode';
|
|
936
|
+
const result = config.setConfigValue(cwd, keyPath, modeValue);
|
|
937
|
+
core.output(result, raw, `Mode set: ${keyPath} = ${modeValue}`);
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
default:
|
|
942
|
+
error(`Unknown command: ${command}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
main();
|