@opengsd/gsd-core 1.19.0 → 1.20.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.compact.md +5 -5
- package/agents/gsd-code-fixer.md +9 -4
- package/agents/gsd-code-reviewer.compact.md +49 -8
- package/agents/gsd-code-reviewer.md +53 -14
- package/agents/gsd-executor.md +8 -4
- package/bin/install.js +36 -40
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/new-project.md +2 -2
- package/commands/gsd/update.md +5 -2
- package/gsd-core/bin/gsd-tools.cjs +177 -93
- package/gsd-core/bin/lib/capability-lifecycle.cjs +1 -12
- package/gsd-core/bin/lib/capability-registry.cjs +284 -93
- package/gsd-core/bin/lib/capability-validator.cjs +15 -0
- package/gsd-core/bin/lib/commands.cjs +46 -17
- package/gsd-core/bin/lib/config-loader.cjs +3 -3
- package/gsd-core/bin/lib/core-utils.cjs +11 -0
- package/gsd-core/bin/lib/frontmatter.cjs +21 -3
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
- package/gsd-core/bin/lib/gate-context-drift.cjs +1 -1
- package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
- package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
- package/gsd-core/bin/lib/git-info-exclude.cjs +1 -6
- package/gsd-core/bin/lib/gsd-tool-dir.cjs +134 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +13 -9
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -3
- package/gsd-core/bin/lib/init.cjs +181 -68
- package/gsd-core/bin/lib/install-engine.cjs +9 -9
- package/gsd-core/bin/lib/install-manifest-record.cjs +86 -0
- package/gsd-core/bin/lib/install-profiles.cjs +4 -2
- package/gsd-core/bin/lib/io.cjs +6 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +34 -1
- package/gsd-core/bin/lib/markdown-table.cjs +23 -0
- package/gsd-core/bin/lib/mcp-server.cjs +8 -5
- package/gsd-core/bin/lib/milestone.cjs +18 -50
- package/gsd-core/bin/lib/phase-entry-grammar.cjs +119 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
- package/gsd-core/bin/lib/phase-id.cjs +627 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -74
- package/gsd-core/bin/lib/phase-locator.cjs +328 -19
- package/gsd-core/bin/lib/phase.cjs +3039 -666
- package/gsd-core/bin/lib/planning-document.cjs +26 -5
- package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
- package/gsd-core/bin/lib/planning-snapshot.cjs +12 -7
- package/gsd-core/bin/lib/planning-workspace.cjs +3 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +23 -2
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +759 -144
- package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +8 -5
- package/gsd-core/bin/lib/roadmap.cjs +358 -106
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -130
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +6 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +36 -9
- package/gsd-core/bin/lib/smart-entry.cjs +22 -11
- package/gsd-core/bin/lib/state-contract.cjs +11 -7
- package/gsd-core/bin/lib/state-document.cjs +332 -23
- package/gsd-core/bin/lib/state-transition.cjs +51 -15
- package/gsd-core/bin/lib/state.cjs +499 -180
- package/gsd-core/bin/lib/template.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +3 -2
- package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
- package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
- package/gsd-core/bin/lib/verify.cjs +2 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +27 -21
- package/gsd-core/bin/lib/worktree-base-ref.cjs +32 -2
- package/gsd-core/bin/verify-reapply-patches.cjs +411 -10
- package/gsd-core/references/cicd-review-patterns.md +45 -0
- package/gsd-core/references/necessity-review.md +48 -0
- package/gsd-core/workflows/code-review-fix.md +46 -5
- package/gsd-core/workflows/code-review.md +3 -0
- package/gsd-core/workflows/execute-phase.md +1 -1
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
- package/gsd-core/workflows/plan-phase.md +1 -1
- package/gsd-core/workflows/pr-branch.md +2 -0
- package/gsd-core/workflows/reapply-patches.md +104 -0
- package/gsd-core/workflows/review.md +54 -141
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +57 -9
- package/gsd-core/workflows/ship.md +12 -0
- package/hooks/dist/gsd-agent-isolation-guard.js +1 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +1 -1
- package/hooks/dist/gsd-graphify-update.sh +6 -0
- package/hooks/dist/gsd-statusline.js +3 -2
- package/hooks/dist/gsd-windsurf-pre-write.js +4 -4
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +4 -4
- package/hooks/dist/lib/git-probe.js +16 -0
- package/hooks/dist/lib/isolation-sentinel.js +77 -2
- package/hooks/gsd-agent-isolation-guard.js +1 -1
- package/hooks/gsd-cursor-subagent-start.js +1 -1
- package/hooks/gsd-graphify-update.sh +6 -0
- package/hooks/gsd-statusline.js +3 -2
- package/hooks/gsd-windsurf-pre-write.js +4 -4
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +4 -4
- package/hooks/lib/git-probe.js +16 -0
- package/hooks/lib/isolation-sentinel.js +77 -2
- package/package.json +3 -3
- package/pi/package-agents/gsd-code-fixer.md +9 -4
- package/pi/package-agents/gsd-code-reviewer.md +53 -14
- package/pi/package-agents/gsd-executor.md +8 -4
- package/pi/package-agents/manifest.json +3 -3
- package/pi/package-skills/gsd-new-milestone/SKILL.md +1 -1
- package/pi/package-skills/gsd-new-project/SKILL.md +1 -1
- package/pi/package-skills/gsd-update/SKILL.md +4 -1
- package/scripts/changeset/lint.cjs +28 -3
- package/scripts/ci-next-health.cjs +246 -23
- package/scripts/docs-guard-registry.cjs +2 -0
- package/scripts/gen-features.cjs +14 -2
- package/scripts/lib/drift-scan.cjs +27 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +10 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +15 -0
- package/scripts/lint-phase-enumeration-drift.cjs +8 -4
- package/scripts/lint-roadmap-progress-drift.cjs +421 -0
- package/scripts/merge-commit-pr.cjs +186 -0
- package/scripts/mutation-matrix.cjs +23 -1
- package/scripts/release-ancestry.cjs +86 -0
- package/scripts/release-pipeline-pr.cjs +92 -0
- package/scripts/require-issue-link-policy.cjs +18 -9
- package/scripts/sync-next-version.cjs +48 -7
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-new-project/SKILL.md +1 -1
- package/skills/gsd-update/SKILL.md +5 -2
- package/vscode/package.json +1 -1
|
@@ -256,17 +256,17 @@ Treat this as a finally-block obligation: even on early exit (config error, no f
|
|
|
256
256
|
|
|
257
257
|
<step name="load_context">
|
|
258
258
|
1. Read all `<required_reading>` files if present.
|
|
259
|
-
2. Parse `<config>` block: `phase_dir`, `padded_phase`, `review_path` (full path to REVIEW.md), `fix_scope` ("critical_warning" default, or "all" includes Info), `fix_report_path` (output REVIEW-FIX.md path).
|
|
259
|
+
2. Parse `<config>` block: `phase_dir`, `padded_phase`, `review_path` (full path to REVIEW.md), `fix_scope` ("critical_warning" default, or "all" includes Info and necessity), `fix_report_path` (output REVIEW-FIX.md path).
|
|
260
260
|
3. `cat {review_path}`.
|
|
261
261
|
4. Parse frontmatter `status:`. If `"clean"` or `"skipped"`: exit with "No issues to fix -- REVIEW.md status is {status}." — do NOT create REVIEW-FIX.md, exit 0 (not an error).
|
|
262
262
|
5. Load project context (`<project_context>`): CLAUDE.md, skills.
|
|
263
263
|
</step>
|
|
264
264
|
|
|
265
265
|
<step name="parse_findings">
|
|
266
|
-
1. Extract findings via `<finding_parser>` rules: `id`, `severity` (Critical CR-*/BL-*, Warning WR-*, Info IN-*), `title`, `file` (primary), `files` (all referenced, for multi-file fixes), `line` (or null), `issue`, `fix` (may be multi-line/code fences).
|
|
267
|
-
2. Filter by `fix_scope`: `critical_warning` → CR-*/BL-*/WR-* only; `all` → + IN
|
|
268
|
-
3. Sort: Critical first, then Warning, then Info; same-severity keeps document order.
|
|
269
|
-
4. Record `findings_in_scope` count for frontmatter.
|
|
266
|
+
1. Extract findings via `<finding_parser>` rules: `id`, `severity` (Critical CR-*/BL-*, Warning WR-*, Info IN-*), `category` (`necessity` when the finding carries a `**Category:** necessity` line, #4100 — unnecessary code, not broken code), `title`, `file` (primary), `files` (all referenced, for multi-file fixes), `line` (or null), `issue`, `fix` (may be multi-line/code fences).
|
|
267
|
+
2. Filter by `fix_scope`: `critical_warning` → CR-*/BL-*/WR-* only, and `critical_warning` excludes necessity findings (removal is the human's scope call; the marker overrides the prefix, so a WR-* carrying `**Category:** necessity` is out of scope and skipped with no edit); `all` → + IN-*, and `all` includes necessity findings, ordered last — record each as `fixed: requires human verification`.
|
|
268
|
+
3. Sort: Critical first, then Warning, then Info, then necessity; same-severity keeps document order.
|
|
269
|
+
4. Record `findings_in_scope` count for frontmatter. If `findings_in_scope` is 0, apply nothing: write REVIEW-FIX.md with `fixed: 0`, `skipped: 0`, `status: all_fixed`, and a body stating no finding was in scope and the out-of-scope findings remain in REVIEW.md.
|
|
270
270
|
</step>
|
|
271
271
|
|
|
272
272
|
<step name="apply_fixes">
|
package/agents/gsd-code-fixer.md
CHANGED
|
@@ -158,6 +158,8 @@ Each finding starts with:
|
|
|
158
158
|
|
|
159
159
|
Where ID matches: `CR-\d+` or `BL-\d+` (Critical-tier-equivalent), `WR-\d+` (Warning), or `IN-\d+` (Info)
|
|
160
160
|
|
|
161
|
+
A finding whose body carries a `**Category:** necessity` line is a **necessity finding** (#4100): code the reviewer judged unnecessary, not broken. Its prefix still gives its severity.
|
|
162
|
+
|
|
161
163
|
**Required Fields:**
|
|
162
164
|
|
|
163
165
|
- **File:** line contains primary file path
|
|
@@ -439,7 +441,7 @@ This cleanup is unconditional when a worktree was created — register it mental
|
|
|
439
441
|
- `phase_dir`: Path to phase directory (e.g., `.planning/phases/02-code-review-command`)
|
|
440
442
|
- `padded_phase`: Zero-padded phase number (e.g., "02")
|
|
441
443
|
- `review_path`: Full path to REVIEW.md (e.g., `.planning/phases/02-code-review-command/02-REVIEW.md`)
|
|
442
|
-
- `fix_scope`: "critical_warning" (default) or "all" (includes Info findings)
|
|
444
|
+
- `fix_scope`: "critical_warning" (default) or "all" (includes Info and necessity findings)
|
|
443
445
|
- `fix_report_path`: Full path for REVIEW-FIX.md output (e.g., `.planning/phases/02-code-review-command/02-REVIEW-FIX.md`)
|
|
444
446
|
|
|
445
447
|
**3. Read REVIEW.md:**
|
|
@@ -465,6 +467,7 @@ Read `./CLAUDE.md` and check for `.claude/skills/` or `.agents/skills/` (as desc
|
|
|
465
467
|
For each finding, extract:
|
|
466
468
|
- `id`: Finding identifier (e.g., CR-01, WR-03, IN-12)
|
|
467
469
|
- `severity`: Critical (CR-* or BL-*), Warning (WR-*), Info (IN-*)
|
|
470
|
+
- `category`: `necessity` when the finding carries a `**Category:** necessity` line, otherwise none
|
|
468
471
|
- `title`: Issue title from `### ` heading
|
|
469
472
|
- `file`: Primary file path from **File:** line
|
|
470
473
|
- `files`: ALL file paths referenced in finding (including in Fix section) — for multi-file fixes
|
|
@@ -473,15 +476,17 @@ For each finding, extract:
|
|
|
473
476
|
- `fix`: Full fix content from **Fix:** section (may be multi-line, may contain code fences)
|
|
474
477
|
|
|
475
478
|
**2. Filter by fix_scope:**
|
|
476
|
-
- If `fix_scope == "critical_warning"`: include only CR-*, BL-*, and WR-* findings
|
|
477
|
-
- If `fix_scope == "all"`: include CR-*, BL-*, WR-*, and IN-* findings
|
|
479
|
+
- If `fix_scope == "critical_warning"`: include only CR-*, BL-*, and WR-* findings; `critical_warning` excludes necessity findings (removing code is a scope decision for the human, #4100). The marker overrides the prefix: a WR-* finding carrying `**Category:** necessity` is OUT of scope here, and is skipped without applying any edit
|
|
480
|
+
- If `fix_scope == "all"`: include CR-*, BL-*, WR-*, and IN-* findings; `all` includes necessity findings, ordered last. A necessity fix removes code, so record it as `fixed: requires human verification`
|
|
478
481
|
|
|
479
482
|
**3. Sort findings by severity:**
|
|
480
|
-
- Critical (CR-* and BL-*) first, then Warning, then Info
|
|
483
|
+
- Critical (CR-* and BL-*) first, then Warning, then Info, then necessity findings
|
|
481
484
|
- Within same severity, maintain document order
|
|
482
485
|
|
|
483
486
|
**4. Count findings in scope:**
|
|
484
487
|
Record `findings_in_scope` for REVIEW-FIX.md frontmatter.
|
|
488
|
+
|
|
489
|
+
If `findings_in_scope` is 0, apply nothing: write REVIEW-FIX.md with `fixed: 0`, `skipped: 0`, `status: all_fixed`, and a body stating that no finding was in scope and the out-of-scope findings remain in REVIEW.md.
|
|
485
490
|
</step>
|
|
486
491
|
|
|
487
492
|
<step name="apply_fixes">
|
|
@@ -49,6 +49,8 @@ Read `./CLAUDE.md` if present — follow project guidelines, security requiremen
|
|
|
49
49
|
|
|
50
50
|
**3. Code Quality** — dead code, unused imports/variables, poor naming, missing error handling, inconsistent patterns, overly complex functions (high cyclomatic complexity), code duplication, magic numbers, commented-out code
|
|
51
51
|
|
|
52
|
+
**4. Necessity** (#4100) — reachable code whose guarded-against problem cannot occur in this stack: impossible-state guard, already-defended-elsewhere, cargo-culted pattern, phantom requirement, generality without instantiation. At `standard`/`deep`, Read `~/.claude/gsd-core/references/necessity-review.md` before classifying; if unreadable, say so in the Summary and omit `necessity:`. `quick` does not assess necessity. Warning when provable from the repo, Info when judgment, Never Critical; cite the evidence the state cannot occur and add a `**Category:** necessity` line.
|
|
53
|
+
|
|
52
54
|
**Out of Scope (v1):** performance issues (O(n²) algorithms, memory leaks, inefficient queries) — NOT in scope. Focus on correctness, security, maintainability.
|
|
53
55
|
|
|
54
56
|
</review_scope>
|
|
@@ -56,10 +58,12 @@ Read `./CLAUDE.md` if present — follow project guidelines, security requiremen
|
|
|
56
58
|
<depth_levels>
|
|
57
59
|
|
|
58
60
|
**quick** — pattern-matching only, grep/regex scan for common anti-patterns, no full file reads. Target: <2 min.
|
|
59
|
-
|
|
61
|
+
Checks (commands in `review_by_depth`): Hardcoded secrets; Dangerous functions; Debug artifacts; Empty catch blocks; Commented-out code; Unpinned actions; Non-idempotent install; Missing job timeout.
|
|
62
|
+
The last three: GitHub Actions workflows only.
|
|
60
63
|
|
|
61
64
|
**standard** (default) — Read each changed file, check bugs/security/quality in context, cross-reference imports/exports. Target: 5-15 min.
|
|
62
65
|
Language-aware checks: **JS/TS** unchecked `.length`, missing `await`, unhandled promise rejection, `as any`, `==` vs `===`, null coalescing issues. **Python** bare `except:`, mutable default args, f-string injection, `eval()`, missing `with` for file ops. **Go** unchecked error returns, goroutine leaks, context not passed, `defer` in loops, race conditions. **C/C++** buffer overflow patterns, use-after-free, null pointer deref, missing bounds checks, memory leaks. **Shell** unquoted variables, `eval`, missing `set -e`, command injection via interpolation.
|
|
66
|
+
**CI/CD pipelines** (by path: `.github/workflows/*.yml`, `Dockerfile*`, `docker-compose*.yml`, deploy scripts): Read `~/.claude/gsd-core/references/cicd-review-patterns.md`; if it cannot be read, say so in the Summary and check: non-idempotent steps (`npm install` vs `npm ci`, unlocked `pip install`, unguarded `mkdir`/`rm`), no job `timeout-minutes`, deploy without `concurrency` + `cancel-in-progress`, per-environment rebuild (not build-once), no health check or rollback, load-bearing `continue-on-error`, static credentials where OIDC works, unpinned actions.
|
|
63
67
|
|
|
64
68
|
**deep** — all of standard + cross-file analysis: trace call chains across imports, check type consistency at API boundaries (TS interfaces, API contracts), verify error propagation (thrown errors caught by callers), check state mutation consistency across modules, detect circular dependencies/coupling. Target: 15-30 min.
|
|
65
69
|
|
|
@@ -109,6 +113,7 @@ Use its `files`. A `status` of `unresolvable` (exit 69, JSON still printed), or
|
|
|
109
113
|
NOTE: do NOT exclude all `.md` — commands, workflows, and agents are source code in this codebase.
|
|
110
114
|
|
|
111
115
|
**2. Group by language/type:** JS/TS (`.js`,`.jsx`,`.ts`,`.tsx`), Python (`.py`), Go (`.go`), C/C++ (`.c`,`.cpp`,`.h`,`.hpp`), Shell (`.sh`,`.bash`), other → generic.
|
|
116
|
+
CI/CD (by path): `.github/workflows/*.yml`/`*.yaml`, `Dockerfile*`, `docker-compose*.yml`, `deploy*.sh` (also Shell).
|
|
112
117
|
|
|
113
118
|
**3. Exit early if empty:** create REVIEW.md with `status: skipped`, all finding counts 0. Body: "No source files to review after filtering. All files in scope are documentation, planning artifacts, or generated files. Use `status: skipped` (not `clean`) because no actual review was performed."
|
|
114
119
|
|
|
@@ -116,14 +121,47 @@ NOTE: `status: clean` = reviewed, no issues. `status: skipped` = no reviewable f
|
|
|
116
121
|
</step>
|
|
117
122
|
|
|
118
123
|
<step name="review_by_depth">
|
|
119
|
-
**depth=quick:** run
|
|
124
|
+
**depth=quick:** run every check from `<depth_levels>`:
|
|
120
125
|
```bash
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
grep -n -E "
|
|
126
|
+
# Set FILE per scoped path with: while IFS= read -r FILE (never paste a name into code).
|
|
127
|
+
|
|
128
|
+
# Hardcoded secrets
|
|
129
|
+
grep -n -E -- "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"][^'\"]+['\"]" "$FILE"
|
|
130
|
+
|
|
131
|
+
# Dangerous functions
|
|
132
|
+
grep -n -E -- "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru" "$FILE"
|
|
133
|
+
|
|
134
|
+
# Debug artifacts
|
|
135
|
+
grep -n -E -- "console\.log|debugger;|TODO|FIXME|XXX|HACK" "$FILE"
|
|
136
|
+
|
|
137
|
+
# Empty catch blocks
|
|
138
|
+
grep -n -E -- "catch\s*\([^)]*\)\s*\{\s*\}" "$FILE"
|
|
139
|
+
|
|
140
|
+
# Commented-out code
|
|
141
|
+
grep -n -E -- "^\s*//.*[{};]|^\s*#[[:space:]]*(if|for|while|def|class|elif|else|try|except|with)[[:space:](].*:[[:space:]]*$|^\s*/\*[^*].*[{};]" "$FILE"
|
|
142
|
+
|
|
143
|
+
# Unpinned actions
|
|
144
|
+
grep -n -E -- "^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]*['\"]?[^[:space:]'\"]+@(main|master|latest)['\"]?([[:space:]]|$)" "$FILE"
|
|
145
|
+
|
|
146
|
+
# Non-idempotent install
|
|
147
|
+
[ -f package-lock.json ] && grep -n -E -- "(^|[^[:alnum:]_-])npm[[:space:]]+(install|i)([[:space:]]|$)" "$FILE" | grep -v -E "^[0-9]+:[[:space:]]*#|[[:space:]](-g|--global)([[:space:]]|$)"
|
|
148
|
+
|
|
149
|
+
# Missing job timeout
|
|
150
|
+
awk '
|
|
151
|
+
function flush() { if (job != "" && runs && !timed) print line ": " job ": no job-level timeout-minutes"; job = "" }
|
|
152
|
+
{ sub(/\r$/, "") }
|
|
153
|
+
/^[^[:space:]#]/ { flush(); injobs = ($0 ~ /^jobs:[[:space:]]*(#.*)?$/); jind = -1; next }
|
|
154
|
+
!injobs || /^[[:space:]]*(#.*)?$/ { next }
|
|
155
|
+
{ ind = match($0, /[^ ]/) - 1 }
|
|
156
|
+
jind < 0 { jind = ind }
|
|
157
|
+
ind == jind { flush(); job = $0; sub(/^ +/, "", job); sub(/:.*/, "", job); line = NR; kind = -1; runs = 0; timed = 0; next }
|
|
158
|
+
kind < 0 { kind = ind }
|
|
159
|
+
ind == kind && /^ *runs-on:/ { runs = 1 }
|
|
160
|
+
ind == kind && /^ *timeout-minutes:/ { timed = 1 }
|
|
161
|
+
END { flush() }
|
|
162
|
+
' "$FILE"
|
|
125
163
|
```
|
|
126
|
-
Severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning.
|
|
164
|
+
Severity: secrets/dangerous=Critical, debug and commented-out code=Info, empty catch and the three CI/CD checks=Warning.
|
|
127
165
|
|
|
128
166
|
**depth=standard:** per file — Read full content, apply language-specific checks, check for: functions >50 lines, deep nesting (>4 levels), missing error handling in async functions, hardcoded config values, type safety issues (TS `any`, loose Python typing). Record findings with file path, line number, description.
|
|
129
167
|
|
|
@@ -133,7 +171,7 @@ Severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning.
|
|
|
133
171
|
<step name="classify_findings">
|
|
134
172
|
**Critical** — security vulnerabilities, data loss, crashes, auth bypasses: SQL/command/path-traversal injection, hardcoded secrets in production code, null pointer derefs that crash, auth/authz bypasses, unsafe deserialization, buffer overflows.
|
|
135
173
|
|
|
136
|
-
**Warning** — logic errors, unhandled edge cases, missing error handling, code smells that could cause bugs: unchecked array access, missing async error handling, off-by-one errors, `==` vs `===` coercion, unhandled promise rejections, dead code paths indicating logic errors.
|
|
174
|
+
**Warning** — logic errors, unhandled edge cases, missing error handling, code smells that could cause bugs: unchecked array access, missing async error handling, off-by-one errors, `==` vs `===` coercion, unhandled promise rejections, dead code paths indicating logic errors; CI/CD pipeline findings: every check in the CI/CD entry.
|
|
137
175
|
|
|
138
176
|
**Info** — style, naming, dead code, unused imports, suggestions: unused imports/variables, poor naming (single letters except loop counters), commented-out code, TODO/FIXME, magic numbers, duplication.
|
|
139
177
|
|
|
@@ -158,10 +196,13 @@ findings:
|
|
|
158
196
|
warning: N
|
|
159
197
|
info: N
|
|
160
198
|
total: N
|
|
199
|
+
necessity: N
|
|
161
200
|
status: clean | issues_found
|
|
162
201
|
---
|
|
163
202
|
```
|
|
164
203
|
|
|
204
|
+
`necessity:` only at standard/deep: the necessity findings among `warning` + `info`, never added to `total`.
|
|
205
|
+
|
|
165
206
|
**3. Body sections (required order):**
|
|
166
207
|
1) `## Structural Findings (fallow)` — only if structural findings provided; normalized items first.
|
|
167
208
|
2) `## Narrative Findings (AI reviewer)` — your adversarial findings, including any external claim independently verified (`(external: {slug})`).
|
|
@@ -61,6 +61,8 @@ This ensures project-specific patterns, conventions, and best practices are appl
|
|
|
61
61
|
|
|
62
62
|
**3. Code Quality** — Dead code, unused imports/variables, poor naming conventions, missing error handling, inconsistent patterns, overly complex functions (high cyclomatic complexity), code duplication, magic numbers, commented-out code
|
|
63
63
|
|
|
64
|
+
**4. Necessity** (#4100) — reachable code whose guarded-against problem cannot occur in this stack: impossible-state guard, already-defended-elsewhere, cargo-culted pattern, phantom requirement, generality without instantiation. At `standard`/`deep`, Read `~/.claude/gsd-core/references/necessity-review.md` before classifying findings; if it cannot be read, say so in the Summary and omit `necessity:`. `quick` does not assess necessity. Warning when provable from the repo, Info when it rests on judgment, Never Critical; each finding cites the evidence that the state cannot occur and carries a `**Category:** necessity` line.
|
|
65
|
+
|
|
64
66
|
**Out of Scope (v1):** Performance issues (O(n²) algorithms, memory leaks, inefficient queries) are NOT in scope for v1. Focus on correctness, security, and maintainability.
|
|
65
67
|
|
|
66
68
|
</review_scope>
|
|
@@ -71,12 +73,17 @@ This ensures project-specific patterns, conventions, and best practices are appl
|
|
|
71
73
|
|
|
72
74
|
**quick** — Pattern-matching only. Use grep/regex to scan for common anti-patterns without reading full file contents. Target: under 2 minutes.
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
- Hardcoded secrets
|
|
76
|
-
- Dangerous functions
|
|
77
|
-
- Debug artifacts
|
|
78
|
-
- Empty catch blocks
|
|
79
|
-
- Commented-out code
|
|
76
|
+
Checks (commands in `review_by_depth`):
|
|
77
|
+
- Hardcoded secrets
|
|
78
|
+
- Dangerous functions
|
|
79
|
+
- Debug artifacts
|
|
80
|
+
- Empty catch blocks
|
|
81
|
+
- Commented-out code
|
|
82
|
+
- Unpinned actions
|
|
83
|
+
- Non-idempotent install
|
|
84
|
+
- Missing job timeout
|
|
85
|
+
|
|
86
|
+
The last three: GitHub Actions workflows only.
|
|
80
87
|
|
|
81
88
|
**standard** (default) — Read each changed file. Check for bugs, security issues, and quality problems in context. Cross-reference imports and exports. Target: 5-15 minutes.
|
|
82
89
|
|
|
@@ -86,6 +93,7 @@ Language-aware checks:
|
|
|
86
93
|
- **Go**: Unchecked error returns, goroutine leaks, context not passed, `defer` in loops, race conditions
|
|
87
94
|
- **C/C++**: Buffer overflow patterns, use-after-free indicators, null pointer dereferences, missing bounds checks, memory leaks
|
|
88
95
|
- **Shell**: Unquoted variables, `eval` usage, missing `set -e`, command injection via interpolation
|
|
96
|
+
- **CI/CD pipelines** (by path: `.github/workflows/*.yml`, `Dockerfile*`, `docker-compose*.yml`, deploy scripts): Read `~/.claude/gsd-core/references/cicd-review-patterns.md`; if it cannot be read, say so in the Summary and check: non-idempotent steps (`npm install` vs `npm ci`, unlocked `pip install`, unguarded `mkdir`/`rm`), no job `timeout-minutes`, deploy without `concurrency` + `cancel-in-progress`, per-environment rebuild (not build-once), no health check or rollback, load-bearing `continue-on-error`, static credentials where OIDC works, unpinned actions
|
|
89
97
|
|
|
90
98
|
**deep** — All of standard, plus cross-file analysis. Trace function call chains across imports. Target: 15-30 minutes.
|
|
91
99
|
|
|
@@ -174,6 +182,7 @@ NOTE: Do NOT exclude all `.md` files — commands, workflows, and agents are sou
|
|
|
174
182
|
- Go: `.go`
|
|
175
183
|
- C/C++: `.c`, `.cpp`, `.h`, `.hpp`
|
|
176
184
|
- Shell: `.sh`, `.bash`
|
|
185
|
+
- CI/CD (by path): `.github/workflows/*.yml`/`*.yaml`, `Dockerfile*`, `docker-compose*.yml`, `deploy*.sh` (also Shell)
|
|
177
186
|
- Other: Review generically
|
|
178
187
|
|
|
179
188
|
**3. Exit early if empty:** If no source files remain after filtering, create REVIEW.md with:
|
|
@@ -194,22 +203,48 @@ NOTE: `status: clean` means "reviewed and found no issues." `status: skipped` me
|
|
|
194
203
|
Branch on depth level:
|
|
195
204
|
|
|
196
205
|
**For depth=quick:**
|
|
197
|
-
Run
|
|
206
|
+
Run every check from `<depth_levels>` quick section:
|
|
198
207
|
```bash
|
|
208
|
+
# Set FILE per scoped path with: while IFS= read -r FILE (never paste a name into code).
|
|
209
|
+
|
|
199
210
|
# Hardcoded secrets
|
|
200
|
-
grep -n -E "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"]\
|
|
211
|
+
grep -n -E -- "(password|secret|api_key|token|apikey|api-key)\s*[=:]\s*['\"][^'\"]+['\"]" "$FILE"
|
|
201
212
|
|
|
202
213
|
# Dangerous functions
|
|
203
|
-
grep -n -E "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec"
|
|
214
|
+
grep -n -E -- "eval\(|innerHTML|dangerouslySetInnerHTML|exec\(|system\(|shell_exec|passthru" "$FILE"
|
|
204
215
|
|
|
205
216
|
# Debug artifacts
|
|
206
|
-
grep -n -E "console\.log|debugger;|TODO|FIXME|XXX|HACK"
|
|
207
|
-
|
|
208
|
-
# Empty catch
|
|
209
|
-
grep -n -E "catch\s*\([^)]*\)\s*\{\s*\}"
|
|
217
|
+
grep -n -E -- "console\.log|debugger;|TODO|FIXME|XXX|HACK" "$FILE"
|
|
218
|
+
|
|
219
|
+
# Empty catch blocks
|
|
220
|
+
grep -n -E -- "catch\s*\([^)]*\)\s*\{\s*\}" "$FILE"
|
|
221
|
+
|
|
222
|
+
# Commented-out code
|
|
223
|
+
grep -n -E -- "^\s*//.*[{};]|^\s*#[[:space:]]*(if|for|while|def|class|elif|else|try|except|with)[[:space:](].*:[[:space:]]*$|^\s*/\*[^*].*[{};]" "$FILE"
|
|
224
|
+
|
|
225
|
+
# Unpinned actions
|
|
226
|
+
grep -n -E -- "^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]*['\"]?[^[:space:]'\"]+@(main|master|latest)['\"]?([[:space:]]|$)" "$FILE"
|
|
227
|
+
|
|
228
|
+
# Non-idempotent install
|
|
229
|
+
[ -f package-lock.json ] && grep -n -E -- "(^|[^[:alnum:]_-])npm[[:space:]]+(install|i)([[:space:]]|$)" "$FILE" | grep -v -E "^[0-9]+:[[:space:]]*#|[[:space:]](-g|--global)([[:space:]]|$)"
|
|
230
|
+
|
|
231
|
+
# Missing job timeout
|
|
232
|
+
awk '
|
|
233
|
+
function flush() { if (job != "" && runs && !timed) print line ": " job ": no job-level timeout-minutes"; job = "" }
|
|
234
|
+
{ sub(/\r$/, "") }
|
|
235
|
+
/^[^[:space:]#]/ { flush(); injobs = ($0 ~ /^jobs:[[:space:]]*(#.*)?$/); jind = -1; next }
|
|
236
|
+
!injobs || /^[[:space:]]*(#.*)?$/ { next }
|
|
237
|
+
{ ind = match($0, /[^ ]/) - 1 }
|
|
238
|
+
jind < 0 { jind = ind }
|
|
239
|
+
ind == jind { flush(); job = $0; sub(/^ +/, "", job); sub(/:.*/, "", job); line = NR; kind = -1; runs = 0; timed = 0; next }
|
|
240
|
+
kind < 0 { kind = ind }
|
|
241
|
+
ind == kind && /^ *runs-on:/ { runs = 1 }
|
|
242
|
+
ind == kind && /^ *timeout-minutes:/ { timed = 1 }
|
|
243
|
+
END { flush() }
|
|
244
|
+
' "$FILE"
|
|
210
245
|
```
|
|
211
246
|
|
|
212
|
-
Record findings with severity: secrets/dangerous=Critical, debug=Info, empty catch=Warning
|
|
247
|
+
Record findings with severity: secrets/dangerous=Critical, debug and commented-out code=Info, empty catch and the three CI/CD checks=Warning
|
|
213
248
|
|
|
214
249
|
**For depth=standard:**
|
|
215
250
|
For each file:
|
|
@@ -253,6 +288,7 @@ For each finding, assign severity:
|
|
|
253
288
|
- Type coercion issues (`==` vs `===`)
|
|
254
289
|
- Unhandled promise rejections
|
|
255
290
|
- Dead code paths that indicate logic errors
|
|
291
|
+
- CI/CD pipeline findings: every check in the CI/CD entry
|
|
256
292
|
|
|
257
293
|
**Info** — Style issues, naming improvements, dead code, unused imports, suggestions:
|
|
258
294
|
- Unused imports/variables
|
|
@@ -287,10 +323,13 @@ findings:
|
|
|
287
323
|
warning: N
|
|
288
324
|
info: N
|
|
289
325
|
total: N
|
|
326
|
+
necessity: N
|
|
290
327
|
status: clean | issues_found
|
|
291
328
|
---
|
|
292
329
|
```
|
|
293
330
|
|
|
331
|
+
Emit `necessity:` only at standard/deep depth. It counts the necessity findings among `warning` + `info` and is never added to `total`.
|
|
332
|
+
|
|
294
333
|
**3. Body sections (required order):**
|
|
295
334
|
1) `## Structural Findings (fallow)` — only when structural findings were provided; list normalized items first.
|
|
296
335
|
2) `## Narrative Findings (AI reviewer)` — your adversarial findings from direct code review, including any external-reviewer claim you independently verified (`(external: {slug})`, see `load_context` step 5).
|
package/agents/gsd-executor.md
CHANGED
|
@@ -766,7 +766,14 @@ Do NOT skip. Do NOT proceed to state updates if self-check fails.
|
|
|
766
766
|
</self_check>
|
|
767
767
|
|
|
768
768
|
<state_updates>
|
|
769
|
-
After SUMMARY.md, update STATE.md
|
|
769
|
+
After SUMMARY.md, update ROADMAP.md progress FIRST, then STATE.md. The ROADMAP write is the step that can decline (`missing_phase_details`), and a decline stops the plan before any STATE.md write, so STATE.md never advances past a plan the ROADMAP did not record:
|
|
770
|
+
|
|
771
|
+
```bash
|
|
772
|
+
# Update ROADMAP.md progress for this phase (plan counts, status). A decline stops here, before STATE.md moves.
|
|
773
|
+
PROGRESS=$(gsd_run query roadmap.update-plan-progress "${PHASE_NUMBER}") && REASON=$(jq -er '.reason//""' <<<"$PROGRESS") && [ "$REASON" != missing_phase_details ] || { echo "ERROR: ROADMAP update-plan-progress did not complete for ${PHASE_NUMBER}-${PLAN} (missing_phase_details decline, failed query, or jq not installed). STOP: skip this plan's completion steps." >&2; exit 1; }
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Then update STATE.md using `gsd-tools query` state handlers (named flags):
|
|
770
777
|
|
|
771
778
|
```bash
|
|
772
779
|
# Advance plan counter (handles edge cases automatically)
|
|
@@ -793,9 +800,6 @@ gsd_run query state.record-session \
|
|
|
793
800
|
```
|
|
794
801
|
|
|
795
802
|
```bash
|
|
796
|
-
# Update ROADMAP.md progress for this phase (plan counts, status)
|
|
797
|
-
gsd_run query roadmap.update-plan-progress "${PHASE_NUMBER}"
|
|
798
|
-
|
|
799
803
|
# Mark completed requirements from PLAN.md frontmatter
|
|
800
804
|
# Extract the `requirements` array from the plan's frontmatter, then mark each complete
|
|
801
805
|
gsd_run query requirements.mark-complete ${REQ_IDS}
|
package/bin/install.js
CHANGED
|
@@ -2659,7 +2659,15 @@ function convertSlashCommandsToTraeSkillMentions(content) {
|
|
|
2659
2659
|
});
|
|
2660
2660
|
}
|
|
2661
2661
|
|
|
2662
|
-
function convertClaudeToTraeMarkdown(content) {
|
|
2662
|
+
function convertClaudeToTraeMarkdown(content, pathPrefix) {
|
|
2663
|
+
return runtimeArtifactConversion._shieldEmittedPrefix(
|
|
2664
|
+
runtimeArtifactConversion._resolveTraeInstructionFileAtPrefix(content, pathPrefix),
|
|
2665
|
+
pathPrefix,
|
|
2666
|
+
convertClaudeToTraeMarkdownBody,
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
function convertClaudeToTraeMarkdownBody(content) {
|
|
2663
2671
|
let converted = convertSlashCommandsToTraeSkillMentions(filterRuntimeNotesForTarget(content, 'trae'));
|
|
2664
2672
|
converted = converted.replace(/\bBash\(/g, 'Shell(');
|
|
2665
2673
|
converted = converted.replace(/\bEdit\(/g, 'StrReplace(');
|
|
@@ -2719,9 +2727,10 @@ function convertClaudeToTraeMarkdown(content) {
|
|
|
2719
2727
|
converted = converted.replace(/\.\/\.claude\//g, './.trae/');
|
|
2720
2728
|
converted = converted.replace(/\.claude\//g, '.trae/');
|
|
2721
2729
|
// Bare forms (no trailing slash) — after slash forms to avoid double-rewrite.
|
|
2722
|
-
// Use negative lookahead (?![\w-]) to preserve .claude-plugin and .
|
|
2723
|
-
|
|
2724
|
-
converted = converted.replace(
|
|
2730
|
+
// Use negative lookahead (?![\w-]|\.\w) to preserve .claude-plugin, .claudeignore and .claude.json.
|
|
2731
|
+
// #5715: an emitted prefix is shielded by convertClaudeToTraeMarkdown; the skills path runs this before the prefix pass, and the profile maps the own-dir forms.
|
|
2732
|
+
converted = converted.replace(/~\/\.claude(?![\w-]|\.\w)/g, '~/.trae');
|
|
2733
|
+
converted = converted.replace(/\$HOME\/\.claude(?![\w-]|\.\w)/g, () => '$HOME/.trae');
|
|
2725
2734
|
// Environment variable name rewrite
|
|
2726
2735
|
converted = converted.replace(/\bCLAUDE_CONFIG_DIR\b/g, 'TRAE_CONFIG_DIR');
|
|
2727
2736
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
@@ -2765,8 +2774,8 @@ function convertClaudeCommandToTraeSkill(content, skillName) {
|
|
|
2765
2774
|
return `${fm}\n${body}`;
|
|
2766
2775
|
}
|
|
2767
2776
|
|
|
2768
|
-
function convertClaudeAgentToTraeAgent(content) {
|
|
2769
|
-
let converted = convertClaudeToTraeMarkdown(content);
|
|
2777
|
+
function convertClaudeAgentToTraeAgent(content, _isGlobal, meta) {
|
|
2778
|
+
let converted = convertClaudeToTraeMarkdown(content, meta && meta.pathPrefix);
|
|
2770
2779
|
|
|
2771
2780
|
const { frontmatter, body } = extractFrontmatterAndBody(converted);
|
|
2772
2781
|
if (!frontmatter) return converted;
|
|
@@ -7279,14 +7288,16 @@ function installCodexConfig(targetDir, agentsSrc, sandboxTier = 'codex-agent-san
|
|
|
7279
7288
|
content = composeWorkflow(content, { sourcePath: agentTomlSourcePath });
|
|
7280
7289
|
// Replace full .claude/gsd-core prefix so path resolves to the Codex
|
|
7281
7290
|
// GSD install before generic .claude → .codex conversion rewrites it.
|
|
7282
|
-
|
|
7283
|
-
|
|
7291
|
+
// #5715: one pass, function replacement: a string replacement would expand `$&` in a
|
|
7292
|
+
// config dir, and a second pass would rescan the path the first emitted.
|
|
7293
|
+
content = content.replace(/(?:~|\$HOME)\/\.claude\/gsd-core\//g, () => codexGsdPath);
|
|
7284
7294
|
// Route TOML emit through the same full Claude→Codex conversion pipeline
|
|
7285
7295
|
// used on the `.md` emit path (#2639). Covers: slash-command rewrites,
|
|
7286
7296
|
// $ARGUMENTS → {{GSD_ARGS}}, /clear removal, anchored and bare .claude/
|
|
7287
7297
|
// paths, .claudeignore → .codexignore, and standalone "Claude" /
|
|
7288
7298
|
// CLAUDE.md neutralization via neutralizeAgentReferences(..., 'AGENTS.md').
|
|
7289
|
-
|
|
7299
|
+
// #5715: the converter runs after the path above was emitted; shield it from the converter.
|
|
7300
|
+
content = runtimeArtifactConversion._shieldEmittedPrefix(content, codexGsdPath, convertClaudeToCodexMarkdown);
|
|
7290
7301
|
const { frontmatter } = extractFrontmatterAndBody(content);
|
|
7291
7302
|
// #3897 security review F1 (blocker, post-merge): this loop used to key
|
|
7292
7303
|
// the sandbox/hold decision off ONLY the filename stem while the emitted
|
|
@@ -7949,7 +7960,9 @@ const RUNTIME_CONTENT_DISPATCH = {
|
|
|
7949
7960
|
},
|
|
7950
7961
|
},
|
|
7951
7962
|
trae: {
|
|
7952
|
-
|
|
7963
|
+
// #5715: the Trae converter shields (and resolves) the emitted prefix itself.
|
|
7964
|
+
mdShieldsPrefix: true,
|
|
7965
|
+
md: (content, ctx) => convertClaudeToTraeMarkdown(content, ctx && ctx.pathPrefix),
|
|
7953
7966
|
js: (content) => {
|
|
7954
7967
|
content = content.replace(/\/gsd:([a-z0-9-]+)/g, (_, commandName) => {
|
|
7955
7968
|
return `/gsd-${commandName}`;
|
|
@@ -8152,33 +8165,8 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
8152
8165
|
// balanced masking grammar used by this path and the rewrite engine.
|
|
8153
8166
|
const rewriteGenericPaths = (body) => {
|
|
8154
8167
|
content = body;
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
const localClaudeRegex = /\.\/\.claude\//g;
|
|
8158
|
-
content = content.replace(globalClaudeRegex, pathPrefix);
|
|
8159
|
-
content = content.replace(globalClaudeHomeRegex, pathPrefix);
|
|
8160
|
-
content = content.replace(localClaudeRegex, `./${dirName}/`);
|
|
8161
|
-
// #3544 review (Finding 1 fallout): guarded with the SAME
|
|
8162
|
-
// negative-lookahead convention already used at ~:2859-2860 below
|
|
8163
|
-
// ("preserve .claude-plugin and .claudeignore"). A naive `\b` here
|
|
8164
|
-
// is satisfied by ANY non-word character, including '-' — so for a
|
|
8165
|
-
// --config-dir whose name EXTENDS '.claude' (e.g. '.claude-work',
|
|
8166
|
-
// pathPrefix '$HOME/.claude-work/'), this pass re-matched the
|
|
8167
|
-
// '$HOME/.claude' PREFIX of its own slash-form output (lines above)
|
|
8168
|
-
// and re-appended the full prefix, corrupting every emitted path to
|
|
8169
|
-
// '$HOME/.claude-work-work/...'. Harmless no-op for the literal
|
|
8170
|
-
// default '.claude' (self-replace with an identical string), which
|
|
8171
|
-
// is why this went undetected until a non-default config-dir name
|
|
8172
|
-
// was exercised.
|
|
8173
|
-
content = content.replace(/~\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
|
|
8174
|
-
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
|
|
8175
|
-
content = content.replace(/\.\/\.claude\b/g, `./${dirName}`);
|
|
8176
|
-
content = content.replace(/~\/\.qwen\//g, pathPrefix);
|
|
8177
|
-
content = content.replace(/\$HOME\/\.qwen\//g, pathPrefix);
|
|
8178
|
-
content = content.replace(/\.\/\.qwen\//g, `./${dirName}/`);
|
|
8179
|
-
content = content.replace(/~\/\.hermes\//g, pathPrefix);
|
|
8180
|
-
content = content.replace(/\$HOME\/\.hermes\//g, pathPrefix);
|
|
8181
|
-
content = content.replace(/\.\/\.hermes\//g, `./${dirName}/`);
|
|
8168
|
+
// #5715: every generic source form in ONE pass (no rescan of the emitted prefix).
|
|
8169
|
+
content = runtimeArtifactConversion._rewriteSpecTreeConfigDirForms(content, pathPrefix, dirName);
|
|
8182
8170
|
// #3544: restore @-file-reference lines to the tilde form Claude Code
|
|
8183
8171
|
// actually expands — the SAME correction #3133 already applies to
|
|
8184
8172
|
// skill/command bodies via _applyRuntimeRewrites's 'claude' case (see
|
|
@@ -8215,7 +8203,15 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
8215
8203
|
content = normalizeAgentBodyForRuntime(content, runtime, readGsdCommandNames());
|
|
8216
8204
|
|
|
8217
8205
|
// Apply per-runtime .md converter (if any)
|
|
8218
|
-
|
|
8206
|
+
// #5715: the generic pass above emitted `pathPrefix`; shield it from the converter
|
|
8207
|
+
// (a converter that skips the generic pass emitted nothing to shield).
|
|
8208
|
+
if (dispatch.md) {
|
|
8209
|
+
content = runtimeArtifactConversion._shieldEmittedPrefix(
|
|
8210
|
+
content,
|
|
8211
|
+
dispatch.mdSkipGenericRewrite || dispatch.mdShieldsPrefix ? undefined : pathPrefix,
|
|
8212
|
+
(shielded) => dispatch.md(shielded, ctx),
|
|
8213
|
+
);
|
|
8214
|
+
}
|
|
8219
8215
|
|
|
8220
8216
|
// Re-run attribution after converter for runtimes that need it (copilot, antigravity)
|
|
8221
8217
|
if (dispatch.mdReattributeAfter) content = processAttribution(content, getCommitAttribution(runtime));
|
|
@@ -10087,8 +10083,8 @@ const MANIFEST_NAME = 'gsd-file-manifest.json';
|
|
|
10087
10083
|
* Compute SHA256 hash of file contents
|
|
10088
10084
|
*/
|
|
10089
10085
|
function fileHash(filePath) {
|
|
10090
|
-
|
|
10091
|
-
return
|
|
10086
|
+
// #5683: single owner of the per-file hash, shared with `effort sync`.
|
|
10087
|
+
return require('../gsd-core/bin/lib/install-manifest-record.cjs').fileHash(filePath);
|
|
10092
10088
|
}
|
|
10093
10089
|
|
|
10094
10090
|
/**
|
|
@@ -9,7 +9,7 @@ allowed-tools:
|
|
|
9
9
|
- Grep
|
|
10
10
|
- Agent
|
|
11
11
|
- AskUserQuestion
|
|
12
|
-
requires: [new-project, phase, plan-phase]
|
|
12
|
+
requires: [new-project, phase, plan-phase, discuss-phase, execute-phase]
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
<arguments>$ARGUMENTS</arguments>
|
|
@@ -28,7 +28,7 @@ Brownfield equivalent of new-project. Project exists, PROJECT.md has history. Ga
|
|
|
28
28
|
- `.planning/ROADMAP.md` — phase structure (continues numbering)
|
|
29
29
|
- `.planning/STATE.md` — reset for new milestone
|
|
30
30
|
|
|
31
|
-
**After:** `/gsd:plan-phase [N]` to
|
|
31
|
+
**After:** Run `/gsd:discuss-phase [N]` to clarify the approach (or `/gsd:plan-phase [N]` to plan directly); then `/gsd:execute-phase [N]` to execute.
|
|
32
32
|
</objective>
|
|
33
33
|
|
|
34
34
|
<execution_context>
|
|
@@ -9,7 +9,7 @@ allowed-tools:
|
|
|
9
9
|
- Write
|
|
10
10
|
- Agent
|
|
11
11
|
- AskUserQuestion
|
|
12
|
-
requires: [config, phase, plan-phase]
|
|
12
|
+
requires: [config, phase, plan-phase, discuss-phase, execute-phase]
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
<arguments>$ARGUMENTS</arguments>
|
|
@@ -36,7 +36,7 @@ Initialize a new project through unified flow: questioning → research (optiona
|
|
|
36
36
|
- `.planning/ROADMAP.md` — phase structure
|
|
37
37
|
- `.planning/STATE.md` — project memory
|
|
38
38
|
|
|
39
|
-
**After this command:** Run `/gsd:plan-phase 1` to
|
|
39
|
+
**After this command:** Run `/gsd:discuss-phase 1` to clarify the approach (or `/gsd:plan-phase 1` to plan directly); then `/gsd:execute-phase 1` to execute.
|
|
40
40
|
</objective>
|
|
41
41
|
|
|
42
42
|
<execution_context>
|
package/commands/gsd/update.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gsd:update
|
|
3
3
|
description: Update GSD to latest version with changelog display
|
|
4
|
-
argument-hint: "[--sync | --reapply | --next | --rc]"
|
|
4
|
+
argument-hint: "[--sync | --reapply | --next | --rc] [--interactive=exceptions|all]"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Write
|
|
@@ -35,14 +35,17 @@ Routes to the update workflow which handles:
|
|
|
35
35
|
<flags>
|
|
36
36
|
- **--sync**: Sync managed GSD skills across runtime roots so multi-runtime users stay aligned after an update. Runs the sync-skills workflow (--from, --to, --dry-run, --apply flags supported).
|
|
37
37
|
- **--reapply**: Reapply local modifications after a GSD update. Uses three-way comparison (pristine baseline, user-modified backup, newly installed version) to merge user customizations back. Runs the reapply-patches workflow.
|
|
38
|
+
- **--interactive[=exceptions|all]** (with `--reapply` only): show each user edit next to the stock text with a recommended disposition and wait for a keep / retire / rewrite decision per edit. `all` shows every edit; `exceptions` (bare `--interactive`) shows only edits upstream adopted, partly adopted, reworded around, or orphaned, and keeps the rest. Without it, `--reapply` behaves exactly as before.
|
|
38
39
|
- **--next** (alias **--rc**): Target the `@next` RC dist-tag instead of `@latest` so you can install or refresh a release candidate (e.g. `1.4.0-rc.1`) through the normal update flow — scope/runtime detection, changelog preview, custom-file backup, and cache clearing all still apply. Omitting it keeps targeting `@latest` (no change). See ADR #660 for the RC channel.
|
|
39
40
|
- **(no flag)**: Standard update — check for new version, show changelog, install.
|
|
40
41
|
</flags>
|
|
41
42
|
|
|
42
43
|
<process>
|
|
44
|
+
If the arguments contain `--interactive` (bare or with a value) but not `--reapply`, STOP with: `--interactive applies only to --reapply (for example: /gsd:update --reapply --interactive=all)`. Do nothing else.
|
|
45
|
+
|
|
43
46
|
Parse the first token of the `<arguments>` block:
|
|
44
47
|
- If it is `--sync`: strip the flag, execute the sync-skills workflow (passing remaining args for --from/--to/--dry-run/--apply).
|
|
45
|
-
- If it is `--reapply`: strip the flag, execute the reapply-patches workflow.
|
|
48
|
+
- If it is `--reapply`: strip the flag, execute the reapply-patches workflow, passing the remaining arguments (for example `--interactive=all`) to it.
|
|
46
49
|
- Otherwise (including `--next` / `--rc`): execute the update workflow end-to-end, passing the `<arguments>` block through so the workflow's parse_update_channel step can select the release channel.
|
|
47
50
|
|
|
48
51
|
</process>
|