@opengsd/gsd-core 1.9.1 → 1.10.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-executor.md +7 -5
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-plan-checker.md +9 -0
- package/agents/gsd-planner.md +5 -8
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-verifier.md +14 -70
- package/bin/install.js +453 -289
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/plan-phase.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +579 -66
- package/gsd-core/bin/lib/active-workstream-store.cjs +25 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +38 -6
- package/gsd-core/bin/lib/api-coverage.cjs +120 -0
- package/gsd-core/bin/lib/audit.cjs +89 -1
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- package/gsd-core/bin/lib/capability-registry.cjs +96 -110
- package/gsd-core/bin/lib/capability-validator.cjs +12 -2
- package/gsd-core/bin/lib/check-command-router.cjs +43 -1
- package/gsd-core/bin/lib/command-aliases.cjs +72 -0
- package/gsd-core/bin/lib/commands.cjs +26 -25
- package/gsd-core/bin/lib/commonjs-marker.cjs +136 -0
- package/gsd-core/bin/lib/config-loader.cjs +1 -0
- package/gsd-core/bin/lib/config.cjs +12 -1
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +91 -12
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/frontmatter.cjs +84 -12
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +58 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +27 -4
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1021 -57
- package/gsd-core/bin/lib/install-engine.cjs +64 -10
- package/gsd-core/bin/lib/install-profiles.cjs +27 -1
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +87 -1
- package/gsd-core/bin/lib/io.cjs +28 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +6 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone.cjs +75 -47
- package/gsd-core/bin/lib/phase-id.cjs +63 -0
- package/gsd-core/bin/lib/phase-locator.cjs +138 -45
- package/gsd-core/bin/lib/phase.cjs +260 -28
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +232 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +4 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +80 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +99 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +30 -6
- package/gsd-core/bin/lib/roadmap-command-router.cjs +42 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +100 -18
- package/gsd-core/bin/lib/roadmap.cjs +37 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +195 -62
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +15 -3
- package/gsd-core/bin/lib/runtime-homes.cjs +154 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +105 -41
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +113 -27
- package/gsd-core/bin/lib/smart-entry.cjs +12 -0
- package/gsd-core/bin/lib/state-transition.cjs +73 -8
- package/gsd-core/bin/lib/state.cjs +151 -62
- package/gsd-core/bin/lib/surface.cjs +12 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +11 -1
- package/gsd-core/bin/lib/uat.cjs +320 -21
- package/gsd-core/bin/lib/unusable-input.cjs +9 -0
- package/gsd-core/bin/lib/verification.cjs +29 -12
- package/gsd-core/bin/lib/verify.cjs +10 -2
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +181 -18
- package/gsd-core/bin/lib/workstream-inventory.cjs +519 -27
- package/gsd-core/bin/lib/workstream.cjs +6 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +276 -118
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/specless-probe-fallback.md +7 -6
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +32 -69
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +83 -0
- package/gsd-core/workflows/code-review.md +42 -160
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +23 -81
- package/gsd-core/workflows/debug.md +9 -12
- package/gsd-core/workflows/diagnose-issues.md +22 -0
- package/gsd-core/workflows/discovery-phase.md +4 -4
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +5 -16
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +34 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +19 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +65 -137
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/ingest-docs.md +2 -1
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +21 -38
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +13 -226
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +49 -193
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +11 -153
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +46 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +20 -390
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +15 -8
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +17 -14
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +49 -11
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +8 -21
- package/gsd-core/workflows/ui-phase.md +8 -7
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +18 -7
- package/gsd-core/workflows/verify-phase.md +4 -7
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +8 -58
- package/hooks/dist/gsd-agent-isolation-guard.js +428 -0
- package/hooks/dist/gsd-check-update-worker.js +14 -5
- package/hooks/dist/gsd-cursor-subagent-start.js +532 -26
- package/hooks/dist/gsd-read-injection-scanner.js +7 -0
- package/hooks/dist/gsd-statusline.js +72 -6
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/isolation-sentinel.js +268 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +428 -0
- package/hooks/gsd-check-update-worker.js +14 -5
- package/hooks/gsd-cursor-subagent-start.js +532 -26
- package/hooks/gsd-read-injection-scanner.js +7 -0
- package/hooks/gsd-statusline.js +72 -6
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/isolation-sentinel.js +268 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +14 -5
- package/pi/gsd.cjs +57 -12
- package/scripts/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +9 -2
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-context-index.cjs +448 -0
- package/scripts/gen-inventory-manifest.cjs +101 -1
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +17 -31
- package/scripts/lint-compiled-artifact-sync.cjs +6 -1
- package/scripts/lint-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-test-file-count.allowlist.json +27 -1
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/prompt-injection-scan.sh +27 -6
- package/scripts/run-tests.cjs +3 -2
- package/skills/gsd-autonomous/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-plan-phase/SKILL.md +2 -2
- package/vscode/package.json +1 -1
- package/scripts/gen-emitted-baseline.cjs +0 -145
|
@@ -44,6 +44,15 @@ PLAN_STRATEGY="local"
|
|
|
44
44
|
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-(converge|cross-ai)([[:space:]]|$)'; then
|
|
45
45
|
PLAN_STRATEGY="converge"
|
|
46
46
|
fi
|
|
47
|
+
|
|
48
|
+
CONVERGE_PARAM=""
|
|
49
|
+
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-converge([[:space:]]|$)'; then
|
|
50
|
+
CONVERGE_PARAM="--converge"
|
|
51
|
+
fi
|
|
52
|
+
CROSS_AI_PARAM=""
|
|
53
|
+
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-cross-ai([[:space:]]|$)'; then
|
|
54
|
+
CROSS_AI_PARAM="--cross-ai"
|
|
55
|
+
fi
|
|
47
56
|
```
|
|
48
57
|
|
|
49
58
|
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
|
@@ -58,8 +67,12 @@ Bootstrap via milestone-level init:
|
|
|
58
67
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
59
68
|
INIT=$(gsd_run query init.milestone-op)
|
|
60
69
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
70
|
+
INIT_AUTONOMOUS=$(gsd_run query init.autonomous $CONVERGE_PARAM $CROSS_AI_PARAM)
|
|
71
|
+
if [[ "$INIT_AUTONOMOUS" == @file:* ]]; then INIT_AUTONOMOUS=$(cat "${INIT_AUTONOMOUS#@file:}"); fi
|
|
61
72
|
```
|
|
62
73
|
|
|
74
|
+
Extract `section_manifest` from `INIT_AUTONOMOUS` (used by the `converge-*` sections below and in step 3).
|
|
75
|
+
|
|
63
76
|
If `PLAN_STRATEGY` is `converge`, fail fast unless the existing convergence feature gate is enabled:
|
|
64
77
|
|
|
65
78
|
```bash
|
|
@@ -79,23 +92,12 @@ if echo "$ARGUMENTS" | grep -qE '\-\-max-cycles\s+[0-9]+'; then
|
|
|
79
92
|
MAX_CYCLES_ARG=$(echo "$ARGUMENTS" | grep -oE '\-\-max-cycles\s+[0-9]+' | awk '{print $2}')
|
|
80
93
|
CONVERGENCE_ARGS="${CONVERGENCE_ARGS} --max-cycles ${MAX_CYCLES_ARG}"
|
|
81
94
|
fi
|
|
82
|
-
|
|
83
|
-
if [ "$PLAN_STRATEGY" = "converge" ]; then
|
|
84
|
-
CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
|
|
85
|
-
if [ "$CONVERGENCE_ENABLED" != "true" ]; then
|
|
86
|
-
printf '%s\n' \
|
|
87
|
-
'gsd-autonomous --converge is disabled (workflow.plan_review_convergence=false).' \
|
|
88
|
-
'' \
|
|
89
|
-
'Enable plan convergence with:' \
|
|
90
|
-
'' \
|
|
91
|
-
' gsd config-set workflow.plan_review_convergence true' \
|
|
92
|
-
'' \
|
|
93
|
-
'Then re-run the autonomous command with --converge.'
|
|
94
|
-
exit 1
|
|
95
|
-
fi
|
|
96
|
-
fi
|
|
97
95
|
```
|
|
98
96
|
|
|
97
|
+
<!-- gsd:section id="converge-fail-fast" when="state:plan-strategy-converge" -->
|
|
98
|
+
If `section_manifest` is `null` or `"converge-fail-fast"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-fail-fast.md`. Otherwise skip — do not read the file.
|
|
99
|
+
<!-- /gsd:section -->
|
|
100
|
+
|
|
99
101
|
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`.
|
|
100
102
|
|
|
101
103
|
**If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd:new-milestone` first."
|
|
@@ -116,7 +118,9 @@ If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
|
|
|
116
118
|
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
|
117
119
|
If `TO_PHASE` is set, display: `Stopping after phase ${TO_PHASE}`
|
|
118
120
|
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute inline — background on Codex only)`
|
|
119
|
-
|
|
121
|
+
<!-- gsd:section id="converge-banner" when="state:plan-strategy-converge" -->
|
|
122
|
+
If `section_manifest` is `null` or `"converge-banner"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-banner.md`. Otherwise skip — do not read the file.
|
|
123
|
+
<!-- /gsd:section -->
|
|
120
124
|
|
|
121
125
|
**Agent skills (delegated agents self-load):** This workflow delegates plan/execute/review via flat `Skill()` invocations rather than resolving `agent_skills` itself. Each consumer agent (`gsd-planner`, `gsd-executor`, `gsd-plan-checker`, `gsd-verifier`, …) self-loads its configured `.planning/config.json` `agent_skills` in its own mandatory init step per `@~/.claude/gsd-core/references/agent-skills-bootstrap.md`. This is the durable path that works on every runtime — including Cursor, where `Skill()`-delegated workflow bash init does not reliably execute. No per-delegation injection is needed here. See open-gsd/gsd-core#1866.
|
|
122
126
|
|
|
@@ -332,42 +336,11 @@ Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHA
|
|
|
332
336
|
|
|
333
337
|
**3a.5. UI Design Contract (Frontend Phases)**
|
|
334
338
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
```bash
|
|
338
|
-
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
339
|
-
HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline). **Compute the active UI step hooks** = entries from `activeHooks` where `kind == "step"` and `ref.skill` is set. **If there are NO active step hooks → skip silently to 3b.** (This covers `workflow.ui_phase=false` — including configurations where only a gate-only entry is present, e.g. `ui_phase=false` + `ui_safety_gate=true` produces `activeHooks=[{kind:"gate"}]`. Autonomous never runs the plan:pre gate — it is always pipeline mode — so a gate-only active set is equivalent to no active step and is silently skipped here. This matches OLD §3a.5 behaviour.)
|
|
343
|
-
|
|
344
|
-
(At least one active step hook ⇒ `workflow.ui_phase` is on.) Run the UI-SPEC gate:
|
|
345
|
-
|
|
346
|
-
```bash
|
|
347
|
-
GATE=$(gsd_run check ui-plan-gate "${PHASE_NUM}" --raw)
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Read `frontend` and `hasUiSpec` from `GATE` (in-context).
|
|
351
|
-
|
|
352
|
-
**If `frontend` is false:** Skip silently to 3b.
|
|
353
|
-
|
|
354
|
-
**If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3b.
|
|
355
|
-
|
|
356
|
-
**Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
|
|
339
|
+
> Full instructions are in `gsd-core/references/autonomous-ui-design-contract.md`. Read that file now and follow it exactly.
|
|
357
340
|
|
|
358
|
-
|
|
359
|
-
Skill(skill="gsd-${ref.skill}", args="${PHASE_NUM}")
|
|
360
|
-
```
|
|
341
|
+
**Inputs:** `PHASE_NUM`, `PHASE_DIR` from execute_phase. Resolves whether the phase needs a UI-SPEC.md generated before planning via active `plan:pre` step hooks. Always non-blocking — proceeds to 3b regardless of outcome.
|
|
361
342
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
After all step hooks return, re-read:
|
|
365
|
-
|
|
366
|
-
```bash
|
|
367
|
-
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
**If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3b. NON-BLOCKING.
|
|
343
|
+
Read and execute: `$HOME/.claude/gsd-core/references/autonomous-ui-design-contract.md`
|
|
371
344
|
|
|
372
345
|
**3b. Plan**
|
|
373
346
|
|
|
@@ -379,15 +352,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|
|
379
352
|
|
|
380
353
|
- **If `FLATTEN` is `false`:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
|
381
354
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
Agent(
|
|
386
|
-
description="Plan convergence phase ${PHASE_NUM}: ${PHASE_NAME}",
|
|
387
|
-
run_in_background=true,
|
|
388
|
-
prompt="Run plan convergence for phase ${PHASE_NUM}: Skill(skill=\"gsd-plan-review-convergence\", args=\"${PHASE_NUM} ${CONVERGENCE_ARGS}\")"
|
|
389
|
-
)
|
|
390
|
-
```
|
|
355
|
+
<!-- gsd:section id="converge-dispatch-bg" when="state:plan-strategy-converge" -->
|
|
356
|
+
If `section_manifest` is `null` or `"converge-dispatch-bg"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md`. Otherwise skip — do not read the file.
|
|
357
|
+
<!-- /gsd:section -->
|
|
391
358
|
|
|
392
359
|
- Otherwise, print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
|
393
360
|
|
|
@@ -403,11 +370,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|
|
403
370
|
|
|
404
371
|
- **Otherwise (`FLATTEN` is `true` — run inline):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
|
|
405
372
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
410
|
-
```
|
|
373
|
+
<!-- gsd:section id="converge-dispatch-inline" when="state:plan-strategy-converge" -->
|
|
374
|
+
If `section_manifest` is `null` or `"converge-dispatch-inline"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md`. Otherwise skip — do not read the file.
|
|
375
|
+
<!-- /gsd:section -->
|
|
411
376
|
|
|
412
377
|
- Otherwise (local planning):
|
|
413
378
|
|
|
@@ -417,11 +382,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|
|
417
382
|
|
|
418
383
|
**If `INTERACTIVE` is NOT set (default):** Run plan inline.
|
|
419
384
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
424
|
-
```
|
|
385
|
+
<!-- gsd:section id="converge-loop" when="state:plan-strategy-converge" -->
|
|
386
|
+
If `section_manifest` is `null` or `"converge-loop"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-loop.md`. Otherwise skip — do not read the file.
|
|
387
|
+
<!-- /gsd:section -->
|
|
425
388
|
|
|
426
389
|
If `PLAN_STRATEGY=local`, run the regular planner:
|
|
427
390
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<step name="dispatch_fix">
|
|
2
|
+
If the `--fix` flag was passed (`FIX_FLAG=true`), delegate to the `code-review-fix.md` workflow
|
|
3
|
+
to auto-apply findings from the REVIEW.md that was just written (or that already existed).
|
|
4
|
+
|
|
5
|
+
This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
|
|
6
|
+
is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
|
|
7
|
+
workflow handles the missing-review error and exits cleanly.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
if [ "$FIX_FLAG" = "true" ]; then
|
|
11
|
+
echo ""
|
|
12
|
+
echo "─────────────────────────────────────────────────────────────────"
|
|
13
|
+
echo " --fix: delegating to code-review-fix.md"
|
|
14
|
+
echo "─────────────────────────────────────────────────────────────────"
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
|
|
18
|
+
FIX_ARGS="${PHASE_ARG}"
|
|
19
|
+
if [ "$FIX_ALL" = "true" ]; then
|
|
20
|
+
FIX_ARGS="${FIX_ARGS} --all"
|
|
21
|
+
fi
|
|
22
|
+
if [ "$FIX_AUTO" = "true" ]; then
|
|
23
|
+
FIX_ARGS="${FIX_ARGS} --auto"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Load and execute the code-review-fix workflow.
|
|
27
|
+
# The fix workflow is the canonical implementation for all fix logic:
|
|
28
|
+
# gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
|
|
29
|
+
# and result presentation. Do not duplicate that logic here.
|
|
30
|
+
Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
|
|
31
|
+
|
|
32
|
+
# Exit after fix workflow completes — present_results is for review-only output.
|
|
33
|
+
# The fix workflow has its own present_results step.
|
|
34
|
+
# Exit workflow.
|
|
35
|
+
fi
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
|
|
39
|
+
</step>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
When `FALLOW_ENABLED=true`:
|
|
2
|
+
|
|
3
|
+
1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
|
|
4
|
+
```bash
|
|
5
|
+
FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
|
|
6
|
+
const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
|
|
7
|
+
const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
|
|
8
|
+
if (resolved) process.stdout.write(resolved);
|
|
9
|
+
")
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2) If binary is missing, fail with actionable message:
|
|
13
|
+
```bash
|
|
14
|
+
if [ -z \"$FALLOW_BIN\" ]; then
|
|
15
|
+
echo \"Error: fallow is enabled but no binary was found.\"
|
|
16
|
+
echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
|
|
17
|
+
# Exit workflow
|
|
18
|
+
fi
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
|
|
22
|
+
```bash
|
|
23
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
24
|
+
FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
|
|
25
|
+
FALLOW_STDERR_TMP=$(mktemp)
|
|
26
|
+
|
|
27
|
+
# Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
|
|
28
|
+
# Derive the phase base commit; if none is found, fall back to repo scope (fallow
|
|
29
|
+
# auto-detects the base branch).
|
|
30
|
+
FALLOW_SCOPE_ARGS=()
|
|
31
|
+
if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
|
|
32
|
+
FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
|
|
33
|
+
if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
|
|
34
|
+
FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
|
|
35
|
+
FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
|
|
40
|
+
FALLOW_EXIT=$?
|
|
41
|
+
|
|
42
|
+
# fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
|
|
43
|
+
# valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
|
|
44
|
+
# Decide success by whether the output parses as a fallow report, not by exit code.
|
|
45
|
+
FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
|
|
46
|
+
try {
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
|
|
49
|
+
const o = JSON.parse(txt);
|
|
50
|
+
process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
|
|
51
|
+
} catch { process.stdout.write('0'); }
|
|
52
|
+
\")
|
|
53
|
+
if [ \"$FALLOW_OK\" != \"1\" ]; then
|
|
54
|
+
FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
|
|
55
|
+
rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
|
|
56
|
+
# #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
|
|
57
|
+
# but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
|
|
58
|
+
# 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
|
|
59
|
+
# CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
|
|
60
|
+
# not found. A non-zero exit here with a resolved binary means fallow is
|
|
61
|
+
# installed but did not produce a report — surface that loudly so a Windows
|
|
62
|
+
# user does not mistake it for "fallow absent".
|
|
63
|
+
case \"$FALLOW_EXIT\" in
|
|
64
|
+
124) FALLOW_FAIL_KIND=\"timed out\" ;;
|
|
65
|
+
2) FALLOW_FAIL_KIND=\"usage error\" ;;
|
|
66
|
+
125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
|
|
67
|
+
126) FALLOW_FAIL_KIND=\"not executable\" ;;
|
|
68
|
+
127) FALLOW_FAIL_KIND=\"not found\" ;;
|
|
69
|
+
*) FALLOW_FAIL_KIND=\"crashed\" ;;
|
|
70
|
+
esac
|
|
71
|
+
echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
|
72
|
+
FALLOW_JSON_PATH=\"\"
|
|
73
|
+
else
|
|
74
|
+
mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
|
|
75
|
+
rm -f \"$FALLOW_STDERR_TMP\"
|
|
76
|
+
fi
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
|
|
80
|
+
|
|
81
|
+
4) Optional MCP bridge path (runtime-dependent):
|
|
82
|
+
- If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
|
|
83
|
+
- Otherwise pass static JSON findings from `FALLOW.json`.
|
|
@@ -19,7 +19,33 @@ Parse arguments and load project state:
|
|
|
19
19
|
```bash
|
|
20
20
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
21
21
|
PHASE_ARG="${1}"
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
# Parse all code-review flags into a structured IR via code-review-flags.cjs.
|
|
24
|
+
# This is the canonical flag-parsing surface — do not replicate inline bash parsing
|
|
25
|
+
# for --fix/--all/--auto here; the module handles all flag extraction and implication
|
|
26
|
+
# logic (e.g., --all and --auto imply --fix). Resolved BEFORE the init call below so
|
|
27
|
+
# the section-manifest gate forwards the RESOLVED (post-implication) fix decision, not
|
|
28
|
+
# just a literal --fix token check.
|
|
29
|
+
FLAGS_JSON=$(node -e "
|
|
30
|
+
const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
|
|
31
|
+
const flags = parseCodeReviewFlags(process.argv.slice(1));
|
|
32
|
+
process.stdout.write(JSON.stringify(flags));
|
|
33
|
+
" -- "$@" 2>/dev/null)
|
|
34
|
+
|
|
35
|
+
# Extract individual flag values from the IR
|
|
36
|
+
FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
|
|
37
|
+
FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
|
|
38
|
+
FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
|
|
39
|
+
DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
|
|
40
|
+
FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
|
|
41
|
+
|
|
42
|
+
# Forward the resolved fix decision (--fix itself, or --all/--auto implying it) to
|
|
43
|
+
# init.code-review so section_manifest's flag:--fix gating (dispatch-fix section)
|
|
44
|
+
# matches code-review-flags.cjs's own implication logic rather than a raw token scan.
|
|
45
|
+
FIX_PARAM=""
|
|
46
|
+
if [ "$FIX_FLAG" = "true" ]; then FIX_PARAM="--fix"; fi
|
|
47
|
+
|
|
48
|
+
INIT=$(gsd_run query init.code-review "${PHASE_ARG}" $FIX_PARAM)
|
|
23
49
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
24
50
|
AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
|
25
51
|
# #2072: resolve the routed model so model_overrides / models.verification are honored
|
|
@@ -27,7 +53,7 @@ AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
|
|
27
53
|
REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
|
|
28
54
|
```
|
|
29
55
|
|
|
30
|
-
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
|
56
|
+
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`, `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap`.
|
|
31
57
|
|
|
32
58
|
**Input sanitization (defense-in-depth):**
|
|
33
59
|
```bash
|
|
@@ -46,27 +72,6 @@ Error: Phase ${PHASE_ARG} not found. Run /gsd:progress to see available phases.
|
|
|
46
72
|
|
|
47
73
|
This runs BEFORE config gate check so user errors are surfaced immediately regardless of config state.
|
|
48
74
|
|
|
49
|
-
Parse optional flags from $ARGUMENTS using the typed flag parser:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# Parse all code-review flags into a structured IR via code-review-flags.cjs.
|
|
53
|
-
# This is the canonical flag-parsing surface — do not replicate inline bash parsing
|
|
54
|
-
# for --fix/--all/--auto here; the module handles all flag extraction and implication
|
|
55
|
-
# logic (e.g., --all and --auto imply --fix).
|
|
56
|
-
FLAGS_JSON=$(node -e "
|
|
57
|
-
const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
|
|
58
|
-
const flags = parseCodeReviewFlags(process.argv.slice(1));
|
|
59
|
-
process.stdout.write(JSON.stringify(flags));
|
|
60
|
-
" -- "$@" 2>/dev/null)
|
|
61
|
-
|
|
62
|
-
# Extract individual flag values from the IR
|
|
63
|
-
FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
|
|
64
|
-
FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
|
|
65
|
-
FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
|
|
66
|
-
DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
|
|
67
|
-
FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
75
|
If FILES_OVERRIDE is set, split by comma into array:
|
|
71
76
|
```bash
|
|
72
77
|
if [ -n "$FILES_OVERRIDE" ]; then
|
|
@@ -231,8 +236,13 @@ Additionally, whenever a reliable diff base is available, cross-check the SUMMAR
|
|
|
231
236
|
against the diff and warn about (then add) any changed files the SUMMARY extractor did not
|
|
232
237
|
surface — so a partial SUMMARY result can no longer silently mask the rest of the phase.
|
|
233
238
|
```bash
|
|
234
|
-
# Compute diff base from phase commits — fail closed if no reliable base found
|
|
235
|
-
|
|
239
|
+
# Compute diff base from phase commits — fail closed if no reliable base found.
|
|
240
|
+
# #2989: anchor the grep to the phase-mention convention ("Phase N" / "phase N"
|
|
241
|
+
# with a word boundary) so a bare digit substring doesn't match version strings,
|
|
242
|
+
# dates, issue refs, or other phases' numbers. With --extended-regexp, \b is
|
|
243
|
+
# a word boundary. When no commit genuinely references the phase, this yields
|
|
244
|
+
# empty and the fail-closed warning below actually fires.
|
|
245
|
+
PHASE_COMMITS=$(git log --oneline --all --grep="[Pp]hase ${PADDED_PHASE}\b" --extended-regexp --format="%H" 2>/dev/null)
|
|
236
246
|
DIFF_BASE=""
|
|
237
247
|
if [ -n "$PHASE_COMMITS" ]; then
|
|
238
248
|
DIFF_BASE=$(echo "$PHASE_COMMITS" | tail -1)^
|
|
@@ -389,20 +399,7 @@ Exit workflow. Do NOT spawn agent or create REVIEW.md.
|
|
|
389
399
|
<step name="structural_pre_pass">
|
|
390
400
|
Optional structural cross-module pass powered by fallow.
|
|
391
401
|
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
FALLOW_ENABLED=$(gsd_run query config-get code_quality.fallow.enabled 2>/dev/null || echo "false")
|
|
395
|
-
FALLOW_SCOPE=$(gsd_run query config-get code_quality.fallow.scope 2>/dev/null || echo "phase")
|
|
396
|
-
FALLOW_PROFILE=$(gsd_run query config-get code_quality.fallow.profile 2>/dev/null || echo "standard")
|
|
397
|
-
FALLOW_MCP=$(gsd_run query config-get code_quality.fallow.mcp 2>/dev/null || echo "false")
|
|
398
|
-
# profile maps to a --max-crap threshold since fallow has no native profile concept.
|
|
399
|
-
# minimal=50 (more lenient), standard=30 (default), strict=15 (tighter).
|
|
400
|
-
case "$FALLOW_PROFILE" in
|
|
401
|
-
minimal) FALLOW_MAX_CRAP=50 ;;
|
|
402
|
-
strict) FALLOW_MAX_CRAP=15 ;;
|
|
403
|
-
*) FALLOW_MAX_CRAP=30 ;; # standard (default)
|
|
404
|
-
esac
|
|
405
|
-
```
|
|
402
|
+
Parse `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap` from the init JSON as `FALLOW_ENABLED`, `FALLOW_SCOPE`, `FALLOW_PROFILE`, `FALLOW_MCP`, `FALLOW_MAX_CRAP`. These are resolved once by `init.code-review` at init time — consuming the pre-resolved values here (instead of a `config-get` call inside this step) avoids gating this section's own inclusion on a fact its own body would otherwise compute (see `state:fallow-enabled` in docs/reference/workflow-fragments.md).
|
|
406
403
|
|
|
407
404
|
Defaults are fail-closed and opt-in:
|
|
408
405
|
- `enabled=false` (skip entirely)
|
|
@@ -410,88 +407,9 @@ Defaults are fail-closed and opt-in:
|
|
|
410
407
|
- `profile=standard` (maps to `--max-crap 30`; minimal=50, standard=30, strict=15 — fallow has no native profile concept)
|
|
411
408
|
- `mcp=false`
|
|
412
409
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
```bash
|
|
417
|
-
FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
|
|
418
|
-
const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
|
|
419
|
-
const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
|
|
420
|
-
if (resolved) process.stdout.write(resolved);
|
|
421
|
-
")
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
2) If binary is missing, fail with actionable message:
|
|
425
|
-
```bash
|
|
426
|
-
if [ -z \"$FALLOW_BIN\" ]; then
|
|
427
|
-
echo \"Error: fallow is enabled but no binary was found.\"
|
|
428
|
-
echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
|
|
429
|
-
# Exit workflow
|
|
430
|
-
fi
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
|
|
434
|
-
```bash
|
|
435
|
-
FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
|
|
436
|
-
FALLOW_STDERR_TMP=$(mktemp)
|
|
437
|
-
|
|
438
|
-
# Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
|
|
439
|
-
# Derive the phase base commit; if none is found, fall back to repo scope (fallow
|
|
440
|
-
# auto-detects the base branch).
|
|
441
|
-
FALLOW_SCOPE_ARGS=()
|
|
442
|
-
if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
|
|
443
|
-
FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
|
|
444
|
-
if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
|
|
445
|
-
FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
|
|
446
|
-
FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
|
|
447
|
-
fi
|
|
448
|
-
fi
|
|
449
|
-
|
|
450
|
-
gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
|
|
451
|
-
FALLOW_EXIT=$?
|
|
452
|
-
|
|
453
|
-
# fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
|
|
454
|
-
# valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
|
|
455
|
-
# Decide success by whether the output parses as a fallow report, not by exit code.
|
|
456
|
-
FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
|
|
457
|
-
try {
|
|
458
|
-
const fs = require('fs');
|
|
459
|
-
const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
|
|
460
|
-
const o = JSON.parse(txt);
|
|
461
|
-
process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
|
|
462
|
-
} catch { process.stdout.write('0'); }
|
|
463
|
-
\")
|
|
464
|
-
if [ \"$FALLOW_OK\" != \"1\" ]; then
|
|
465
|
-
FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
|
|
466
|
-
rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
|
|
467
|
-
# #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
|
|
468
|
-
# but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
|
|
469
|
-
# 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
|
|
470
|
-
# CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
|
|
471
|
-
# not found. A non-zero exit here with a resolved binary means fallow is
|
|
472
|
-
# installed but did not produce a report — surface that loudly so a Windows
|
|
473
|
-
# user does not mistake it for "fallow absent".
|
|
474
|
-
case \"$FALLOW_EXIT\" in
|
|
475
|
-
124) FALLOW_FAIL_KIND=\"timed out\" ;;
|
|
476
|
-
2) FALLOW_FAIL_KIND=\"usage error\" ;;
|
|
477
|
-
125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
|
|
478
|
-
126) FALLOW_FAIL_KIND=\"not executable\" ;;
|
|
479
|
-
127) FALLOW_FAIL_KIND=\"not found\" ;;
|
|
480
|
-
*) FALLOW_FAIL_KIND=\"crashed\" ;;
|
|
481
|
-
esac
|
|
482
|
-
echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
|
483
|
-
FALLOW_JSON_PATH=\"\"
|
|
484
|
-
else
|
|
485
|
-
mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
|
|
486
|
-
rm -f \"$FALLOW_STDERR_TMP\"
|
|
487
|
-
fi
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
|
|
491
|
-
|
|
492
|
-
4) Optional MCP bridge path (runtime-dependent):
|
|
493
|
-
- If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
|
|
494
|
-
- Otherwise pass static JSON findings from `FALLOW.json`.
|
|
410
|
+
<!-- gsd:section id="structural-pre-pass" when="state:fallow-enabled" -->
|
|
411
|
+
If `section_manifest` is `null` or `"structural-pre-pass"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/structural-pre-pass.md`. Otherwise skip — do not read the file.
|
|
412
|
+
<!-- /gsd:section -->
|
|
495
413
|
|
|
496
414
|
When disabled, set:
|
|
497
415
|
```bash
|
|
@@ -636,45 +554,9 @@ fi
|
|
|
636
554
|
```
|
|
637
555
|
</step>
|
|
638
556
|
|
|
639
|
-
|
|
640
|
-
If
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
|
|
644
|
-
is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
|
|
645
|
-
workflow handles the missing-review error and exits cleanly.
|
|
646
|
-
|
|
647
|
-
```bash
|
|
648
|
-
if [ "$FIX_FLAG" = "true" ]; then
|
|
649
|
-
echo ""
|
|
650
|
-
echo "─────────────────────────────────────────────────────────────────"
|
|
651
|
-
echo " --fix: delegating to code-review-fix.md"
|
|
652
|
-
echo "─────────────────────────────────────────────────────────────────"
|
|
653
|
-
echo ""
|
|
654
|
-
|
|
655
|
-
# Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
|
|
656
|
-
FIX_ARGS="${PHASE_ARG}"
|
|
657
|
-
if [ "$FIX_ALL" = "true" ]; then
|
|
658
|
-
FIX_ARGS="${FIX_ARGS} --all"
|
|
659
|
-
fi
|
|
660
|
-
if [ "$FIX_AUTO" = "true" ]; then
|
|
661
|
-
FIX_ARGS="${FIX_ARGS} --auto"
|
|
662
|
-
fi
|
|
663
|
-
|
|
664
|
-
# Load and execute the code-review-fix workflow.
|
|
665
|
-
# The fix workflow is the canonical implementation for all fix logic:
|
|
666
|
-
# gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
|
|
667
|
-
# and result presentation. Do not duplicate that logic here.
|
|
668
|
-
Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
|
|
669
|
-
|
|
670
|
-
# Exit after fix workflow completes — present_results is for review-only output.
|
|
671
|
-
# The fix workflow has its own present_results step.
|
|
672
|
-
# Exit workflow.
|
|
673
|
-
fi
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
|
|
677
|
-
</step>
|
|
557
|
+
<!-- gsd:section id="dispatch-fix" when="flag:--fix" -->
|
|
558
|
+
If `section_manifest` is `null` or `"dispatch-fix"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/dispatch-fix.md`. Otherwise skip — do not read the file; proceed to `present_results`.
|
|
559
|
+
<!-- /gsd:section -->
|
|
678
560
|
|
|
679
561
|
<step name="present_results">
|
|
680
562
|
Read the REVIEW.md YAML frontmatter to extract finding counts.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<step name="git_tag">
|
|
2
|
+
|
|
3
|
+
Create git tag:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Pre-check: skip if tag already exists (prevents silent failure on retry)
|
|
7
|
+
if git rev-parse "v[X.Y]" >/dev/null 2>&1; then echo "Tag v[X.Y] already exists, skipping"; exit 0; fi
|
|
8
|
+
git tag -a v[X.Y] -m "v[X.Y] [Name]
|
|
9
|
+
|
|
10
|
+
Delivered: [One sentence]
|
|
11
|
+
|
|
12
|
+
Key accomplishments:
|
|
13
|
+
- [Item 1]
|
|
14
|
+
- [Item 2]
|
|
15
|
+
- [Item 3]
|
|
16
|
+
|
|
17
|
+
See .planning/MILESTONES.md for full details."
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Confirm: "Tagged: v[X.Y]"
|
|
21
|
+
|
|
22
|
+
Ask: "Push tag to remote? (y/n)"
|
|
23
|
+
|
|
24
|
+
If yes:
|
|
25
|
+
```bash
|
|
26
|
+
git push origin v[X.Y]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
</step>
|