@opengsd/gsd-core 1.13.0 → 1.15.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 +1 -1
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +39 -209
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +45 -187
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// Thresholds:
|
|
15
15
|
// WARNING (remaining <= 35%): Agent should wrap up current task
|
|
16
16
|
// CRITICAL (remaining <= 25%): Agent should stop immediately and save state
|
|
17
|
+
// Both fire-points are overridable per project via .planning/config.json
|
|
18
|
+
// (hooks.context_warning_threshold / hooks.context_critical_threshold, #4285);
|
|
19
|
+
// the values above are the defaults used when the keys are absent or unusable.
|
|
17
20
|
//
|
|
18
21
|
// Debounce: 5 tool uses between warnings to avoid spam
|
|
19
22
|
// Severity escalation bypasses debounce (WARNING -> CRITICAL fires immediately)
|
|
@@ -30,8 +33,8 @@ const { HOOK_ON_CRASH, allow, crash } = require('./lib/hook-exit.js');
|
|
|
30
33
|
// context warning is far cheaper than stalling the agent's work (#3911).
|
|
31
34
|
const ON_CRASH = HOOK_ON_CRASH.ALLOW;
|
|
32
35
|
|
|
33
|
-
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35%
|
|
34
|
-
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25%
|
|
36
|
+
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35% (default, see resolveThresholds)
|
|
37
|
+
const CRITICAL_THRESHOLD = 25; // remaining_percentage <= 25% (default, see resolveThresholds)
|
|
35
38
|
const STALE_SECONDS = 60; // ignore metrics older than 60s
|
|
36
39
|
const DEBOUNCE_CALLS = 5; // min tool uses between warnings
|
|
37
40
|
// How long after a PreCompact readings stay suspect. The watermark records the
|
|
@@ -57,6 +60,41 @@ const COMPACT_GRACE_SECONDS = 60;
|
|
|
57
60
|
// watermark this far ahead pushes first recovery from +61 to +66 (measured).
|
|
58
61
|
const WATERMARK_SKEW_SECONDS = 5;
|
|
59
62
|
|
|
63
|
+
// Resolve the two fire-points from the project's `.planning/config.json`
|
|
64
|
+
// (#4285). The constants above are the DEFAULTS; a project overrides either one
|
|
65
|
+
// through `hooks.context_warning_threshold` / `hooks.context_critical_threshold`,
|
|
66
|
+
// which is what keeps a tuned fire-point alive across updates — this file is in
|
|
67
|
+
// the MANAGED registry, so an edit to the constants is re-staged away by the
|
|
68
|
+
// next install.
|
|
69
|
+
//
|
|
70
|
+
// TOTAL and never-throwing: this hook must not block the tool call it rides in
|
|
71
|
+
// on, so every unusable input degrades to the default instead of raising.
|
|
72
|
+
// Unusable is decided by Number.isFinite, which is type-strict (the string
|
|
73
|
+
// "30" and true are both rejected, unlike the global isFinite), plus the 0-100
|
|
74
|
+
// domain of the remaining_percentage these are compared against.
|
|
75
|
+
//
|
|
76
|
+
// The PAIR is validated too, and falls back TOGETHER. `critical >= warning` has
|
|
77
|
+
// no coherent reading — critical fires deeper into the window than warning —
|
|
78
|
+
// and honouring one side of an inconsistent pair silently picks which of the
|
|
79
|
+
// operator's two numbers to discard. This also rejects a single override that
|
|
80
|
+
// contradicts the OTHER key's default (warning 20 with critical absent, i.e.
|
|
81
|
+
// 25); the resulting pair is the same nonsense either way. Set-time validation
|
|
82
|
+
// cannot stand in for this check: `config-set` writes one key per call, so
|
|
83
|
+
// tuning both (warning first, then critical) is transiently inconsistent on
|
|
84
|
+
// disk, and refusing it there would block a legitimate configuration.
|
|
85
|
+
function resolveThresholds(hooks) {
|
|
86
|
+
const defaults = { warning: WARNING_THRESHOLD, critical: CRITICAL_THRESHOLD };
|
|
87
|
+
if (!hooks || typeof hooks !== 'object') return defaults;
|
|
88
|
+
|
|
89
|
+
const usable = (value, fallback) =>
|
|
90
|
+
(Number.isFinite(value) && value >= 0 && value <= 100) ? value : fallback;
|
|
91
|
+
|
|
92
|
+
const warning = usable(hooks.context_warning_threshold, WARNING_THRESHOLD);
|
|
93
|
+
const critical = usable(hooks.context_critical_threshold, CRITICAL_THRESHOLD);
|
|
94
|
+
|
|
95
|
+
return critical < warning ? { warning, critical } : defaults;
|
|
96
|
+
}
|
|
97
|
+
|
|
60
98
|
// One DEFINITION of what counts as a lifecycle event name, shared by the #3709
|
|
61
99
|
// PreCompact reset and the #2289 output-envelope allowlist. Two call sites, one
|
|
62
100
|
// rule — so the two cannot drift into disagreeing about what "no event name" is.
|
|
@@ -164,14 +202,11 @@ function writeSentinel(target, payload) {
|
|
|
164
202
|
}
|
|
165
203
|
|
|
166
204
|
let input = '';
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
process.stdin.setEncoding('utf8');
|
|
173
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
174
|
-
process.stdin.on('end', () => {
|
|
205
|
+
// Assigned by main(); the handler below clears it. Declared out here rather
|
|
206
|
+
// than inside main() because the handler closes over it.
|
|
207
|
+
let stdinTimeout = null;
|
|
208
|
+
|
|
209
|
+
const handleStdinEnd = () => {
|
|
175
210
|
clearTimeout(stdinTimeout);
|
|
176
211
|
try {
|
|
177
212
|
const data = JSON.parse(input);
|
|
@@ -266,18 +301,26 @@ process.stdin.on('end', () => {
|
|
|
266
301
|
allow(undefined);
|
|
267
302
|
}
|
|
268
303
|
|
|
269
|
-
// Check if context warnings are disabled via config
|
|
304
|
+
// Check if context warnings are disabled via config, and resolve the two
|
|
305
|
+
// fire-points from the same read (#4285 — one config read, not two).
|
|
270
306
|
// Collapsed existsSync+readFileSync into a single read guarded by try/catch
|
|
271
307
|
// (ENOENT or parse error → use defaults, same as old "planningDir absent" branch).
|
|
272
308
|
const cwd = data.cwd || process.cwd();
|
|
309
|
+
let thresholds = { warning: WARNING_THRESHOLD, critical: CRITICAL_THRESHOLD };
|
|
273
310
|
try {
|
|
274
311
|
const configPath = path.join(cwd, '.planning', 'config.json');
|
|
275
312
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
276
313
|
if (config.hooks?.context_warnings === false) {
|
|
277
314
|
allow(undefined);
|
|
278
315
|
}
|
|
316
|
+
// After the disable check, not before: a disabled monitor exits above and
|
|
317
|
+
// never reaches a threshold, so resolving first would only add work to the
|
|
318
|
+
// path that does nothing. allow() exits the process (it does not throw),
|
|
319
|
+
// so this line is unreachable when warnings are off.
|
|
320
|
+
thresholds = resolveThresholds(config.hooks);
|
|
279
321
|
} catch (e) {
|
|
280
|
-
// Missing or unparseable config → proceed with defaults (context warnings
|
|
322
|
+
// Missing or unparseable config → proceed with defaults (context warnings
|
|
323
|
+
// enabled, thresholds at the constants above, which `thresholds` already holds)
|
|
281
324
|
}
|
|
282
325
|
|
|
283
326
|
// If no metrics file, this is a subagent or fresh session -- exit silently.
|
|
@@ -352,7 +395,7 @@ process.stdin.on('end', () => {
|
|
|
352
395
|
const usedPct = metrics.used_pct;
|
|
353
396
|
|
|
354
397
|
// No warning needed
|
|
355
|
-
if (remaining >
|
|
398
|
+
if (remaining > thresholds.warning) {
|
|
356
399
|
allow(undefined);
|
|
357
400
|
}
|
|
358
401
|
|
|
@@ -389,7 +432,7 @@ process.stdin.on('end', () => {
|
|
|
389
432
|
|
|
390
433
|
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
391
434
|
|
|
392
|
-
const isCritical = remaining <=
|
|
435
|
+
const isCritical = remaining <= thresholds.critical;
|
|
393
436
|
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
394
437
|
|
|
395
438
|
// Emit immediately on first warning, then debounce subsequent ones
|
|
@@ -491,4 +534,34 @@ process.stdin.on('end', () => {
|
|
|
491
534
|
// exit(0) fail-open behavior exactly (#3911).
|
|
492
535
|
crash(ON_CRASH, undefined);
|
|
493
536
|
}
|
|
494
|
-
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// The stdin adapter is the only side-effecting statement in this file, so it is
|
|
540
|
+
// the only thing that must not run on `require()`. Gating it lets a test import
|
|
541
|
+
// `resolveThresholds` and drive it directly — the repo's own conclusion for a
|
|
542
|
+
// seam like this (CONTEXT-INDEX, on the ROADMAP Requirements parser: a closure
|
|
543
|
+
// reachable only by spawning the CLI is one "no fast-check property can do").
|
|
544
|
+
// A spawn-per-case property test is not the same test: it would exercise the
|
|
545
|
+
// resolver at whatever pairs survive to an observable severity, not over its
|
|
546
|
+
// whole numeric domain.
|
|
547
|
+
/* istanbul ignore next -- stdin adapter, exercised via spawnSync in tests */
|
|
548
|
+
function main() {
|
|
549
|
+
// Timeout guard: if stdin doesn't close within 10s (e.g. pipe issues on
|
|
550
|
+
// Windows/Git Bash, or slow Claude Code piping during large outputs),
|
|
551
|
+
// exit silently instead of hanging until Claude Code kills the process
|
|
552
|
+
// and reports "hook error". See #775, #1162.
|
|
553
|
+
stdinTimeout = setTimeout(() => allow(undefined), 10000);
|
|
554
|
+
process.stdin.setEncoding('utf8');
|
|
555
|
+
process.stdin.on('data', chunk => input += chunk);
|
|
556
|
+
process.stdin.on('end', handleStdinEnd);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (require.main === module) {
|
|
560
|
+
main();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Exported for the #4285 property test only. The two constants ride along so a
|
|
564
|
+
// test asserts the fallback pair against the SOURCE of truth rather than
|
|
565
|
+
// re-hardcoding 35/25 — a test carrying its own copy of the defaults would stay
|
|
566
|
+
// green if the constants were edited.
|
|
567
|
+
module.exports = { resolveThresholds, WARNING_THRESHOLD, CRITICAL_THRESHOLD };
|
|
@@ -62,8 +62,8 @@ const { allow } = require('./lib/hook-exit.js');
|
|
|
62
62
|
// hooks/lib/cursor-workspace.js. Staged next to these scripts by
|
|
63
63
|
// writeCursorHooksJson so the require always resolves post-install.
|
|
64
64
|
const { resolveStatePath } = require('./lib/cursor-workspace.js');
|
|
65
|
-
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
|
|
66
|
-
const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
|
|
65
|
+
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch, buildSentinelDiscard } = require('./lib/isolation-sentinel.js');
|
|
66
|
+
const { REASON_CODE, describeSentinelDiscard } = require('./lib/isolation-deny-reason.js');
|
|
67
67
|
// #3582: gsd-core/bin/lib/*.cjs (runtime-homes.cjs, worktree-safety.cjs,
|
|
68
68
|
// runtime-name-policy.cjs, capability-registry.cjs — required below, inside
|
|
69
69
|
// resolveIsolationEvidence and resolveFallbackIsolation) are tsc build
|
|
@@ -491,18 +491,25 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
491
491
|
`Refusing to allow this subagent to spawn until the runtime library is built — a guard ` +
|
|
492
492
|
`that cannot verify must not answer "safe" (#3050).`,
|
|
493
493
|
reasonCode: REASON_CODE.RUNTIME_BUILD_FAILED,
|
|
494
|
+
sentinelDiscarded: null,
|
|
494
495
|
};
|
|
495
496
|
}
|
|
496
497
|
|
|
498
|
+
// #3045 BLOCKER fix: a fresh sentinel is authoritative for THIS dispatch's
|
|
499
|
+
// actual resolved isolation — see the doc comment above.
|
|
500
|
+
// #3045 SECURITY F2: a fresh sentinel that names a DIFFERENT plan/phase
|
|
501
|
+
// than this dispatch is not applicable to it — fall through to the
|
|
502
|
+
// conservative fallback exactly as a stale sentinel would.
|
|
503
|
+
// Hoisted (readSentinel never throws) so the "present, fresh, but did not
|
|
504
|
+
// apply" case (#4594 row 15) can be reported on every deny path below
|
|
505
|
+
// instead of silently discarded.
|
|
506
|
+
const sentinel = readSentinel(root, { clock });
|
|
507
|
+
const applies = sentinelAppliesToDispatch(sentinel, dispatchIds);
|
|
508
|
+
const sentinelDiscarded = buildSentinelDiscard(sentinel, dispatchIds);
|
|
509
|
+
|
|
497
510
|
let declaredIsolation;
|
|
498
511
|
try {
|
|
499
|
-
|
|
500
|
-
// dispatch's actual resolved isolation — see the doc comment above.
|
|
501
|
-
// #3045 SECURITY F2: a fresh sentinel that names a DIFFERENT
|
|
502
|
-
// plan/phase than this dispatch is not applicable to it — fall through
|
|
503
|
-
// to the conservative fallback exactly as a stale sentinel would.
|
|
504
|
-
const sentinel = readSentinel(root, { clock });
|
|
505
|
-
declaredIsolation = (sentinel.present && !sentinel.stale && sentinelAppliesToDispatch(sentinel, dispatchIds))
|
|
512
|
+
declaredIsolation = (sentinel.present && !sentinel.stale && applies)
|
|
506
513
|
? sentinel.isolation
|
|
507
514
|
: resolveFallbackIsolation(root, configPath);
|
|
508
515
|
} catch {
|
|
@@ -513,8 +520,10 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
513
520
|
`dispatch-isolation configuration ('.planning/config.json' exists under "${root}"). ` +
|
|
514
521
|
`Refusing to allow this subagent to spawn without being able to verify whether ` +
|
|
515
522
|
`isolation is required — a guard that cannot verify must not answer "safe" (#3050). ` +
|
|
516
|
-
`Retry once the project configuration is readable
|
|
523
|
+
`Retry once the project configuration is readable.` +
|
|
524
|
+
(sentinelDiscarded ? describeSentinelDiscard(sentinelDiscarded) : ''),
|
|
517
525
|
reasonCode: REASON_CODE.CONFIG_UNREADABLE,
|
|
526
|
+
sentinelDiscarded,
|
|
518
527
|
};
|
|
519
528
|
}
|
|
520
529
|
|
|
@@ -530,8 +539,10 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
530
539
|
`GSD subagent isolation guard: this project's dispatch isolation resolves to ` +
|
|
531
540
|
`"harness-worktree", but the subagentStart payload for this dispatch carries no usable ` +
|
|
532
541
|
`subagent_type. Refusing to allow it to spawn without being able to confirm whether it ` +
|
|
533
|
-
`is a GSD executor — a guard that cannot verify must not answer "safe" (#3050)
|
|
542
|
+
`is a GSD executor — a guard that cannot verify must not answer "safe" (#3050).` +
|
|
543
|
+
(sentinelDiscarded ? describeSentinelDiscard(sentinelDiscarded) : ''),
|
|
534
544
|
reasonCode: REASON_CODE.NO_SUBAGENT_TYPE,
|
|
545
|
+
sentinelDiscarded,
|
|
535
546
|
};
|
|
536
547
|
}
|
|
537
548
|
|
|
@@ -547,8 +558,10 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
547
558
|
`"harness-worktree", but whether "${root}" is running in an isolated Cursor worktree ` +
|
|
548
559
|
`could not be determined (git did not respond). Refusing to allow subagent_type=` +
|
|
549
560
|
`"${subagentType}" to spawn without being able to verify isolation — a guard that ` +
|
|
550
|
-
`cannot verify must not answer "safe" (#3050). Retry once git is responsive
|
|
561
|
+
`cannot verify must not answer "safe" (#3050). Retry once git is responsive.` +
|
|
562
|
+
(sentinelDiscarded ? describeSentinelDiscard(sentinelDiscarded) : ''),
|
|
551
563
|
reasonCode: REASON_CODE.CANNOT_DETERMINE_ISOLATION,
|
|
564
|
+
sentinelDiscarded,
|
|
552
565
|
};
|
|
553
566
|
}
|
|
554
567
|
|
|
@@ -560,8 +573,10 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
|
|
|
560
573
|
`which is not an isolated Cursor worktree — it would edit the user's primary checkout ` +
|
|
561
574
|
`directly, with no consent and no warning. Start an isolated session first (the ` +
|
|
562
575
|
`"--worktree" CLI flag or the "/worktree" chat command; Cursor manages these worktrees ` +
|
|
563
|
-
`under "~/.cursor/worktrees/") and retry
|
|
576
|
+
`under "~/.cursor/worktrees/") and retry.` +
|
|
577
|
+
(sentinelDiscarded ? describeSentinelDiscard(sentinelDiscarded) : ''),
|
|
564
578
|
reasonCode: REASON_CODE.NOT_ISOLATED_WORKTREE,
|
|
579
|
+
sentinelDiscarded,
|
|
565
580
|
};
|
|
566
581
|
}
|
|
567
582
|
|
|
@@ -611,7 +626,12 @@ function main() {
|
|
|
611
626
|
decision = { action: 'allow' };
|
|
612
627
|
}
|
|
613
628
|
if (decision.action === 'deny') {
|
|
614
|
-
const out = {
|
|
629
|
+
const out = {
|
|
630
|
+
permission: 'deny',
|
|
631
|
+
user_message: decision.reason,
|
|
632
|
+
reason_code: decision.reasonCode,
|
|
633
|
+
sentinel_discarded: decision.sentinelDiscarded ?? null,
|
|
634
|
+
};
|
|
615
635
|
if (additionalContext !== null) out.additional_context = additionalContext;
|
|
616
636
|
process.stdout.write(JSON.stringify(out));
|
|
617
637
|
return;
|
|
@@ -26,12 +26,24 @@
|
|
|
26
26
|
// .env, .secrets, and .env.<suffix> — EXCEPT .env.example / .env.sample /
|
|
27
27
|
// .env.template / .env.dist, which are the non-secret templates GSD's own
|
|
28
28
|
// phase prompt tells executors to read.
|
|
29
|
-
// Stated cost:
|
|
30
|
-
//
|
|
29
|
+
// Stated cost (#4580): the exemption matches the token's FINAL EXTENSION,
|
|
30
|
+
// not the whole name, so the trusted set is `.env.<anything>.example` /
|
|
31
|
+
// `.sample` / `.template` / `.dist` — an unbounded family, not four fixed
|
|
32
|
+
// names. A real secret named `.env.prod-real-secrets.example` is NOT
|
|
33
|
+
// protected, and renaming any secret to end in one of those four
|
|
34
|
+
// extensions bypasses the guard across Read, Grep and Bash alike. This is
|
|
35
|
+
// the deliberate cost of #4580, which fixed the prior whole-name
|
|
36
|
+
// comparison wrongly refusing committed, secret-free templates like
|
|
37
|
+
// `.env.local.example`.
|
|
31
38
|
// A token containing `:` is also tested on the part after its LAST `:`,
|
|
32
39
|
// so `git show HEAD:.env`, `origin/main:config/.env` and `C:\proj\.env`
|
|
33
|
-
// are caught without git-specific parsing.
|
|
34
|
-
// commit message `fix: .env parsing` yields
|
|
40
|
+
// are caught without git-specific parsing. Leading/interior whitespace is
|
|
41
|
+
// still NOT trimmed: the commit message `fix: .env parsing` yields
|
|
42
|
+
// ` .env parsing`, which is prose, not a name. TRAILING dots and spaces ARE
|
|
43
|
+
// stripped from the basename before classification (`.env.`, `.env..`,
|
|
44
|
+
// `.env `, `.env. ` all normalize to `.env`), because Win32 strips trailing
|
|
45
|
+
// dots and spaces from each path component, so these are aliases for the
|
|
46
|
+
// same on-disk file, not distinct names.
|
|
35
47
|
//
|
|
36
48
|
// Bash analysis is a two-pass token scan, not a shell:
|
|
37
49
|
// pass 1 tokenizes with quote state, comments, redirect operators (with fd
|
|
@@ -74,7 +86,12 @@
|
|
|
74
86
|
// on plain commands, without arming the compound-`cd` prompt". Writes to
|
|
75
87
|
// secret files are out of scope (Write/Edit were never gated). Commands
|
|
76
88
|
// over 1 MiB are denied outright (`command-too-large`) rather than
|
|
77
|
-
// scanned partially or waved through.
|
|
89
|
+
// scanned partially or waved through. (#4639 adds one more, by design: the
|
|
90
|
+
// value of `--env-file` under a container runtime is exempt, so the
|
|
91
|
+
// container's own command can print the interpolated environment
|
|
92
|
+
// (`alpine printenv`, `docker compose config`) — the same exposure class
|
|
93
|
+
// as the pre-existing volume-mount gap (`-v .env:/s`); the flag's value
|
|
94
|
+
// itself is a name, never contents.)
|
|
78
95
|
//
|
|
79
96
|
// Triggers on: Read, Grep, Bash tool calls (Kimi: ReadFile, Grep, Shell)
|
|
80
97
|
// Action: BLOCK (decision: 'block', exit 2) — codes secret-read |
|
|
@@ -86,6 +103,7 @@
|
|
|
86
103
|
'use strict';
|
|
87
104
|
|
|
88
105
|
const { HOOK_ON_CRASH, allow, deny, crash } = require('./lib/hook-exit.js');
|
|
106
|
+
const { finalExtension, normalizeWindowsBasename, lastSegment } = require('./lib/filename-classification.js');
|
|
89
107
|
|
|
90
108
|
// Fail open on a hook-internal error (see header). Declared ONCE so the
|
|
91
109
|
// outer catch states its policy explicitly (#3911).
|
|
@@ -114,6 +132,15 @@ const NON_READING_COMMANDS = new Set([
|
|
|
114
132
|
'basename', 'dirname', 'realpath', 'file', 'echo', 'printf',
|
|
115
133
|
]);
|
|
116
134
|
|
|
135
|
+
// #4639: container runtimes take `--env-file <file>` — the runtime opens the
|
|
136
|
+
// file itself, interpolates it into the container environment, and returns
|
|
137
|
+
// nothing to the agent, so the flag's VALUE is a name, never contents. The
|
|
138
|
+
// same category NON_READING_COMMANDS encodes, expressed as a flag value. Only
|
|
139
|
+
// the flag's value under these runtimes is exempt; every other operand in the
|
|
140
|
+
// segment is still checked, so the carve-out cannot launder a read.
|
|
141
|
+
const CONTAINER_RUNTIMES = new Set(['docker', 'docker-compose', 'podman', 'nerdctl']);
|
|
142
|
+
const ENV_FILE_FLAG_RE = /^--env-file(=|$)/;
|
|
143
|
+
|
|
117
144
|
// Shell interpreters that run a script from `-c`, a file operand, or stdin
|
|
118
145
|
// (heredoc / here-string / piped `echo`|`printf`). `su` is here for its `-c`
|
|
119
146
|
// form (`su [user] -c 'cmd'`); a bare `su user` resolves to file mode, which
|
|
@@ -148,21 +175,18 @@ const GLOB_PROBES = [
|
|
|
148
175
|
// ---------------------------------------------------------------------------
|
|
149
176
|
|
|
150
177
|
function isSecretBasename(name) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
// Win32 strips trailing dots/spaces per path component, so `.env.`,
|
|
179
|
+
// `.env ` etc. resolve to the real `.env` on Windows — normalize FIRST so
|
|
180
|
+
// those aliases can't bypass classification.
|
|
181
|
+
const n = normalizeWindowsBasename(name);
|
|
182
|
+
if (n === '.env' || n === '.secrets') return true;
|
|
183
|
+
if (n.startsWith('.env.')) {
|
|
184
|
+
const suffix = n.slice('.env.'.length);
|
|
185
|
+
return suffix !== '' && !NON_SECRET_ENV_SUFFIXES.has(finalExtension(suffix).toLowerCase());
|
|
155
186
|
}
|
|
156
187
|
return false;
|
|
157
188
|
}
|
|
158
189
|
|
|
159
|
-
// Last `/`- or `\`-separated segment, ignoring trailing separators.
|
|
160
|
-
function lastSegment(tok) {
|
|
161
|
-
const s = tok.replace(/[\\/]+$/, '');
|
|
162
|
-
const i = Math.max(s.lastIndexOf('/'), s.lastIndexOf('\\'));
|
|
163
|
-
return i === -1 ? s : s.slice(i + 1);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
190
|
// True when the token's basename — or the basename of the part after its
|
|
167
191
|
// last `:` (git `<ref>:<path>`, Windows drive) — is a secret name. Folded to
|
|
168
192
|
// lower case once at the top so `.ENV` / `.Secrets` match on the
|
|
@@ -247,7 +271,19 @@ function globAltSelectsSecret(alt) {
|
|
|
247
271
|
if (/^[*?]+$/.test(alt)) return false; // pure wildcard: equivalent to no glob
|
|
248
272
|
const wild = alt.search(/[*?[]/);
|
|
249
273
|
const lit = wild === -1 ? alt : alt.slice(0, wild);
|
|
250
|
-
|
|
274
|
+
// #4580: when there is no wildcard, `alt` (== `lit`) is a WHOLE literal
|
|
275
|
+
// filename, so classify it exactly the same way Read/Bash do (by its
|
|
276
|
+
// FINAL extension, via isSecretBasename) rather than by a `.env.`-prefix
|
|
277
|
+
// heuristic — that heuristic mis-blocked multi-dot templates like
|
|
278
|
+
// `.env.local.example`. When a wildcard IS present, `lit` is only a
|
|
279
|
+
// PARTIAL literal prefix (`.env.local.exam*` can still select
|
|
280
|
+
// `.env.local`), which cannot be classified exactly, so the original
|
|
281
|
+
// conservative prefix rule stays.
|
|
282
|
+
if (wild === -1) {
|
|
283
|
+
if (isSecretBasename(lit)) return true;
|
|
284
|
+
} else if (lit.startsWith('.env.')) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
251
287
|
if (lit !== '' && ('.env.'.startsWith(lit) || '.secrets'.startsWith(lit))) return true;
|
|
252
288
|
let re;
|
|
253
289
|
try {
|
|
@@ -260,10 +296,14 @@ function globAltSelectsSecret(alt) {
|
|
|
260
296
|
|
|
261
297
|
// Returns null (allowed), 'secret-read', or 'glob-too-complex'.
|
|
262
298
|
function classifyGrepGlob(glob) {
|
|
263
|
-
|
|
299
|
+
// Segment via the SAME `lastSegment` helper Read/Bash use (namesSecret),
|
|
300
|
+
// rather than a hand-rolled forward-slash-only split — the two used to
|
|
301
|
+
// diverge on a backslash-bearing glob (`config\.env`), which `lastSegment`
|
|
302
|
+
// reduces to `.env` but a `/`-only split left untouched, letting it escape
|
|
303
|
+
// this arm's predicate while Read/Bash still blocked it.
|
|
264
304
|
// Case-fold the last segment (GLOB_PROBES are lower case) so `.ENV*` and
|
|
265
305
|
// `*.ENV` select the secret namespace on case-insensitive filesystems.
|
|
266
|
-
const segment = (
|
|
306
|
+
const segment = lastSegment(glob).toLowerCase();
|
|
267
307
|
const alts = expandBraces(segment);
|
|
268
308
|
if (alts === null) return 'glob-too-complex';
|
|
269
309
|
return alts.some(globAltSelectsSecret) ? 'secret-read' : null;
|
|
@@ -814,7 +854,19 @@ function findSecretRead(command, depth) {
|
|
|
814
854
|
|
|
815
855
|
if (NON_READING_COMMANDS.has(base)) continue;
|
|
816
856
|
|
|
857
|
+
// #4639: exempt ONLY the value of `--env-file`, and only when the
|
|
858
|
+
// segment's command word is a container runtime. A bare `--env-file`
|
|
859
|
+
// consumes the next operand; `--env-file=<value>` is a single word.
|
|
860
|
+
const envFileExempt = CONTAINER_RUNTIMES.has(base);
|
|
861
|
+
let skipNext = false;
|
|
817
862
|
for (const w of operands) {
|
|
863
|
+
if (envFileExempt) {
|
|
864
|
+
if (skipNext) { skipNext = false; continue; }
|
|
865
|
+
if (ENV_FILE_FLAG_RE.test(w.text)) {
|
|
866
|
+
if (!w.text.includes('=')) skipNext = true; // bare flag consumes the next operand
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
818
870
|
if (namesSecret(normalizeOperand(w.text))) return w.text;
|
|
819
871
|
}
|
|
820
872
|
}
|
|
@@ -42,6 +42,10 @@ if (require.main === module) {
|
|
|
42
42
|
const { isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs');
|
|
43
43
|
const { PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
|
|
44
44
|
const { normalizeStateStatus } = require('../gsd-core/bin/lib/state-document.cjs');
|
|
45
|
+
const {
|
|
46
|
+
renderBracketPhaseDisplay,
|
|
47
|
+
renderBracketMilestoneDisplay,
|
|
48
|
+
} = require('../gsd-core/bin/lib/phase-id-display.cjs');
|
|
45
49
|
// #2850: reuse the existing workstream resolution seams rather than
|
|
46
50
|
// re-implementing CLI>env>store precedence or path construction inline.
|
|
47
51
|
// peekActiveWorkstream is the read-only sibling of the store-tier lookup
|
|
@@ -359,16 +363,26 @@ function renderProgressBar(percent) {
|
|
|
359
363
|
* Progress bar is opt-in: appended to the milestone segment only when
|
|
360
364
|
* progress.percent is present in frontmatter; absent → empty string.
|
|
361
365
|
*/
|
|
362
|
-
function formatGsdState(s) {
|
|
366
|
+
function formatGsdState(s, opts = {}) {
|
|
363
367
|
// #2850: workstream mode with nothing resolvable — an observable signal,
|
|
364
368
|
// never silent emptiness (distinguishes from "GSD isn't installed here").
|
|
365
369
|
if (s.noActiveWorkstream) return NO_ACTIVE_WORKSTREAM_LABEL;
|
|
366
370
|
|
|
367
371
|
const parts = [];
|
|
372
|
+
const bracket = opts.convention === 'bracket';
|
|
373
|
+
const bracketMilestone = bracket
|
|
374
|
+
? renderBracketMilestoneDisplay(s.milestone, opts.projectCode)
|
|
375
|
+
: null;
|
|
376
|
+
const phaseLabel = (phase) => {
|
|
377
|
+
const display = bracket
|
|
378
|
+
? renderBracketPhaseDisplay(s.milestone, phase, opts.projectCode)
|
|
379
|
+
: null;
|
|
380
|
+
return display ?? `Phase ${phase}`;
|
|
381
|
+
};
|
|
368
382
|
|
|
369
383
|
// Milestone segment: version + name + (opt-in) progress bar
|
|
370
384
|
if (s.milestone || s.milestoneName) {
|
|
371
|
-
const ver = s.milestone
|
|
385
|
+
const ver = bracketMilestone ?? s.milestone ?? '';
|
|
372
386
|
const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
|
|
373
387
|
const bar = renderProgressBar(s.percent);
|
|
374
388
|
const pieces = [ver, name, bar].filter(Boolean);
|
|
@@ -384,7 +398,8 @@ function formatGsdState(s) {
|
|
|
384
398
|
// stage = whichever lifecycle status was written by the orchestrator
|
|
385
399
|
// (discussing / planning / executing / verifying)
|
|
386
400
|
const stage = s.status || '';
|
|
387
|
-
|
|
401
|
+
const phase = phaseLabel(s.activePhase);
|
|
402
|
+
parts.push(stage ? `${phase} ${stage}` : phase);
|
|
388
403
|
} else if (s.nextAction && phasesStr) {
|
|
389
404
|
// Scene 2: idle + a recommended next command is visible to the user.
|
|
390
405
|
// Surfaces "what to run next" without the user opening STATE.md.
|
|
@@ -402,9 +417,16 @@ function formatGsdState(s) {
|
|
|
402
417
|
// earlier so no existing project's status-line changes shape.
|
|
403
418
|
if (s.status) parts.push(s.status);
|
|
404
419
|
if (s.phaseNum && s.phaseTotal) {
|
|
420
|
+
const bracketPhase = bracket
|
|
421
|
+
? renderBracketPhaseDisplay(s.milestone, s.phaseNum, opts.projectCode)
|
|
422
|
+
: null;
|
|
405
423
|
const phase = s.phaseName
|
|
406
|
-
?
|
|
407
|
-
|
|
424
|
+
? bracketPhase
|
|
425
|
+
? `${bracketPhase} ${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
|
|
426
|
+
: `${s.phaseName} (${s.phaseNum}/${s.phaseTotal})`
|
|
427
|
+
: bracketPhase
|
|
428
|
+
? `${bracketPhase} (${s.phaseNum}/${s.phaseTotal})`
|
|
429
|
+
: `ph ${s.phaseNum}/${s.phaseTotal}`;
|
|
408
430
|
parts.push(phase);
|
|
409
431
|
}
|
|
410
432
|
}
|
|
@@ -449,13 +471,17 @@ function contextTokenSuffix(currentUsage) {
|
|
|
449
471
|
// --- Compact state format (opt-in) ---------------------------------------------
|
|
450
472
|
|
|
451
473
|
/**
|
|
452
|
-
* Collapse GSD's
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
474
|
+
* Collapse GSD's status value to a single keyword, built on the canonical
|
|
475
|
+
* normalizer (#2162 approval condition): normalizeStateStatus() in
|
|
476
|
+
* state-document.cjs owns the status vocabulary (discussing / planning /
|
|
477
|
+
* executing / verifying / completed / paused) so the two can't drift.
|
|
478
|
+
* #4186: the normalizer recognizes the DECLARED vocabulary by anchored
|
|
479
|
+
* whole-field match — vocabulary values (the state writer persists tokens)
|
|
480
|
+
* collapse to their keyword; free-text narratives are no longer
|
|
481
|
+
* keyword-guessed from substrings (a `.planning/` mention in non-English
|
|
482
|
+
* prose used to render `planning`), and pass through unrecognized to the
|
|
483
|
+
* first-word fallback below. "paused" — the canonical stuck state — is
|
|
484
|
+
* uppercased to PAUSED, the one state worth shouting about. The fallback is
|
|
459
485
|
* capped at 16 chars so a rogue STATE.md can't blow up the line.
|
|
460
486
|
* Returns null for empty input.
|
|
461
487
|
*/
|
|
@@ -486,17 +512,24 @@ function shortGsdStatus(status) {
|
|
|
486
512
|
* default format — and collapses narrative statuses via shortGsdStatus().
|
|
487
513
|
* The default "full" format is untouched.
|
|
488
514
|
*/
|
|
489
|
-
function formatGsdStateCompact(s) {
|
|
515
|
+
function formatGsdStateCompact(s, opts = {}) {
|
|
490
516
|
// #2850: mirrors formatGsdState's observable "nothing resolvable" signal.
|
|
491
517
|
if (s.noActiveWorkstream) return NO_ACTIVE_WORKSTREAM_LABEL;
|
|
492
518
|
|
|
493
519
|
const parts = [];
|
|
520
|
+
const bracket = opts.convention === 'bracket';
|
|
521
|
+
const bracketMilestone = bracket
|
|
522
|
+
? renderBracketMilestoneDisplay(s.milestone, opts.projectCode)
|
|
523
|
+
: null;
|
|
494
524
|
|
|
495
|
-
if (s.milestone) parts.push(s.milestone);
|
|
525
|
+
if (s.milestone) parts.push(bracketMilestone ?? s.milestone);
|
|
496
526
|
|
|
497
527
|
const phaseId = s.activePhase || s.phaseNum;
|
|
498
528
|
if (phaseId) {
|
|
499
|
-
|
|
529
|
+
const bracketPhase = bracket
|
|
530
|
+
? renderBracketPhaseDisplay(s.milestone, phaseId, opts.projectCode)
|
|
531
|
+
: null;
|
|
532
|
+
parts.push(bracketPhase ?? (s.phaseTotal ? `P${phaseId}/${s.phaseTotal}` : `P${phaseId}`));
|
|
500
533
|
}
|
|
501
534
|
|
|
502
535
|
// Scene exclusivity mirrors formatGsdState's if/else chain: an in-flight
|
|
@@ -743,7 +776,7 @@ function formatStateFreshness(fresh) {
|
|
|
743
776
|
* entry points — this collapses both onto one resolver.
|
|
744
777
|
*
|
|
745
778
|
* @param {object} cfg — parsed .planning/config.json (readGsdConfig())
|
|
746
|
-
* @returns {{ showLastCommand: boolean, position: 'end'|'front', stateFormat: 'full'|'compact', showGit: boolean, showStateFreshness: boolean }}
|
|
779
|
+
* @returns {{ showLastCommand: boolean, position: 'end'|'front', stateFormat: 'full'|'compact', showGit: boolean, showStateFreshness: boolean, convention: string|null, projectCode: string|null }}
|
|
747
780
|
*/
|
|
748
781
|
function resolveStatuslineOptions(cfg) {
|
|
749
782
|
const showLastCommand = getConfigValue(cfg, 'statusline.show_last_command') === true;
|
|
@@ -755,7 +788,22 @@ function resolveStatuslineOptions(cfg) {
|
|
|
755
788
|
const stateFormat = getConfigValue(cfg, 'statusline.state_format') === 'compact' ? 'compact' : 'full';
|
|
756
789
|
const showGit = getConfigValue(cfg, 'statusline.show_git') === true;
|
|
757
790
|
const showStateFreshness = getConfigValue(cfg, 'statusline.show_state_freshness') === true;
|
|
758
|
-
|
|
791
|
+
const convention = getConfigValue(cfg, 'phase_id_convention') === 'bracket'
|
|
792
|
+
? 'bracket'
|
|
793
|
+
: null;
|
|
794
|
+
const projectCodeValue = getConfigValue(cfg, 'project_code');
|
|
795
|
+
const projectCode = typeof projectCodeValue === 'string' && projectCodeValue !== ''
|
|
796
|
+
? projectCodeValue
|
|
797
|
+
: null;
|
|
798
|
+
return {
|
|
799
|
+
showLastCommand,
|
|
800
|
+
position,
|
|
801
|
+
stateFormat,
|
|
802
|
+
showGit,
|
|
803
|
+
showStateFreshness,
|
|
804
|
+
convention,
|
|
805
|
+
projectCode,
|
|
806
|
+
};
|
|
759
807
|
}
|
|
760
808
|
|
|
761
809
|
// --- stdin ------------------------------------------------------------------
|
|
@@ -930,7 +978,9 @@ function runStatusline() {
|
|
|
930
978
|
// freshness git spawn here would spend a subprocess on discarded output.
|
|
931
979
|
if (!task) {
|
|
932
980
|
const state = readGsdState(dir, { stateFreshness: options.showStateFreshness }) || {};
|
|
933
|
-
gsdStateStr = options.stateFormat === 'compact'
|
|
981
|
+
gsdStateStr = options.stateFormat === 'compact'
|
|
982
|
+
? formatGsdStateCompact(state, options)
|
|
983
|
+
: formatGsdState(state, options);
|
|
934
984
|
}
|
|
935
985
|
|
|
936
986
|
// Output
|
|
@@ -1038,6 +1088,7 @@ module.exports = {
|
|
|
1038
1088
|
STATE_HEAD_ADVISORY_COMMITS, isValidStateHeadStamp,
|
|
1039
1089
|
readStateHeadCommits, parseRevListCounts, deriveStateFreshness,
|
|
1040
1090
|
formatStateFreshness, resolveStatuslineOptions,
|
|
1091
|
+
renderBracketPhaseDisplay, renderBracketMilestoneDisplay,
|
|
1041
1092
|
};
|
|
1042
1093
|
|
|
1043
1094
|
/**
|
|
@@ -1055,7 +1106,15 @@ function renderStatusline(data) {
|
|
|
1055
1106
|
// key from reaching only one of them.
|
|
1056
1107
|
let lastCmdSuffix = '';
|
|
1057
1108
|
let gitSuffix = '';
|
|
1058
|
-
let options = {
|
|
1109
|
+
let options = {
|
|
1110
|
+
showLastCommand: false,
|
|
1111
|
+
position: 'end',
|
|
1112
|
+
stateFormat: 'full',
|
|
1113
|
+
showGit: false,
|
|
1114
|
+
showStateFreshness: false,
|
|
1115
|
+
convention: null,
|
|
1116
|
+
projectCode: null,
|
|
1117
|
+
};
|
|
1059
1118
|
try {
|
|
1060
1119
|
const cfg = readGsdConfig(dir);
|
|
1061
1120
|
options = resolveStatuslineOptions(cfg);
|
|
@@ -1071,7 +1130,9 @@ function renderStatusline(data) {
|
|
|
1071
1130
|
} catch (e) { /* swallow */ }
|
|
1072
1131
|
|
|
1073
1132
|
const state = readGsdState(dir, { stateFreshness: options.showStateFreshness }) || {};
|
|
1074
|
-
const gsdStateStr = options.stateFormat === 'compact'
|
|
1133
|
+
const gsdStateStr = options.stateFormat === 'compact'
|
|
1134
|
+
? formatGsdStateCompact(state, options)
|
|
1135
|
+
: formatGsdState(state, options);
|
|
1075
1136
|
const middle = gsdStateStr ? `\x1b[2m${gsdStateStr}\x1b[0m` : null;
|
|
1076
1137
|
return composeStatusline({ model, ctx: '', middle, dirname, lastCmdSuffix, gitSuffix, position: options.position });
|
|
1077
1138
|
}
|