@opengsd/gsd-core 1.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ja-JP.md +870 -0
- package/README.ko-KR.md +861 -0
- package/README.md +301 -0
- package/README.pt-BR.md +492 -0
- package/README.zh-CN.md +842 -0
- package/agents/gsd-advisor-researcher.md +127 -0
- package/agents/gsd-ai-researcher.md +133 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-fixer.md +668 -0
- package/agents/gsd-code-reviewer.md +387 -0
- package/agents/gsd-codebase-mapper.md +853 -0
- package/agents/gsd-debug-session-manager.md +314 -0
- package/agents/gsd-debugger.md +1452 -0
- package/agents/gsd-doc-classifier.md +168 -0
- package/agents/gsd-doc-synthesizer.md +204 -0
- package/agents/gsd-doc-verifier.md +217 -0
- package/agents/gsd-doc-writer.md +615 -0
- package/agents/gsd-domain-researcher.md +153 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +772 -0
- package/agents/gsd-framework-selector.md +160 -0
- package/agents/gsd-integration-checker.md +470 -0
- package/agents/gsd-intel-updater.md +342 -0
- package/agents/gsd-nyquist-auditor.md +203 -0
- package/agents/gsd-pattern-mapper.md +335 -0
- package/agents/gsd-phase-researcher.md +928 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1218 -0
- package/agents/gsd-project-researcher.md +677 -0
- package/agents/gsd-research-synthesizer.md +255 -0
- package/agents/gsd-roadmapper.md +688 -0
- package/agents/gsd-security-auditor.md +155 -0
- package/agents/gsd-ui-auditor.md +495 -0
- package/agents/gsd-ui-checker.md +309 -0
- package/agents/gsd-ui-researcher.md +380 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +917 -0
- package/bin/install.js +10936 -0
- package/bin/lib/ui-safety-gate.cjs +107 -0
- package/commands/gsd/add-tests.md +42 -0
- package/commands/gsd/ai-integration-phase.md +37 -0
- package/commands/gsd/audit-fix.md +34 -0
- package/commands/gsd/audit-milestone.md +37 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +46 -0
- package/commands/gsd/capture.md +62 -0
- package/commands/gsd/cleanup.md +24 -0
- package/commands/gsd/code-review.md +59 -0
- package/commands/gsd/complete-milestone.md +143 -0
- package/commands/gsd/config.md +56 -0
- package/commands/gsd/debug.md +52 -0
- package/commands/gsd/discuss-phase.md +76 -0
- package/commands/gsd/docs-update.md +49 -0
- package/commands/gsd/eval-review.md +33 -0
- package/commands/gsd/execute-phase.md +64 -0
- package/commands/gsd/explore.md +27 -0
- package/commands/gsd/extract-learnings.md +23 -0
- package/commands/gsd/fast.md +31 -0
- package/commands/gsd/forensics.md +57 -0
- package/commands/gsd/graphify.md +199 -0
- package/commands/gsd/health.md +31 -0
- package/commands/gsd/help.md +28 -0
- package/commands/gsd/import.md +41 -0
- package/commands/gsd/inbox.md +39 -0
- package/commands/gsd/ingest-docs.md +42 -0
- package/commands/gsd/manager.md +45 -0
- package/commands/gsd/map-codebase.md +83 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/mvp-phase.md +45 -0
- package/commands/gsd/new-milestone.md +45 -0
- package/commands/gsd/new-project.md +47 -0
- package/commands/gsd/ns-context.md +23 -0
- package/commands/gsd/ns-ideate.md +24 -0
- package/commands/gsd/ns-manage.md +29 -0
- package/commands/gsd/ns-project.md +22 -0
- package/commands/gsd/ns-review.md +26 -0
- package/commands/gsd/ns-workflow.md +28 -0
- package/commands/gsd/pause-work.md +43 -0
- package/commands/gsd/phase.md +56 -0
- package/commands/gsd/plan-phase.md +62 -0
- package/commands/gsd/plan-review-convergence.md +59 -0
- package/commands/gsd/pr-branch.md +26 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +47 -0
- package/commands/gsd/quick.md +174 -0
- package/commands/gsd/resume-work.md +30 -0
- package/commands/gsd/review-backlog.md +63 -0
- package/commands/gsd/review.md +41 -0
- package/commands/gsd/secure-phase.md +36 -0
- package/commands/gsd/settings.md +29 -0
- package/commands/gsd/ship.md +24 -0
- package/commands/gsd/sketch.md +60 -0
- package/commands/gsd/spec-phase.md +63 -0
- package/commands/gsd/spike.md +57 -0
- package/commands/gsd/stats.md +19 -0
- package/commands/gsd/surface.md +155 -0
- package/commands/gsd/thread.md +24 -0
- package/commands/gsd/ui-phase.md +35 -0
- package/commands/gsd/ui-review.md +33 -0
- package/commands/gsd/ultraplan-phase.md +34 -0
- package/commands/gsd/undo.md +35 -0
- package/commands/gsd/update.md +48 -0
- package/commands/gsd/validate-phase.md +36 -0
- package/commands/gsd/verify-work.md +39 -0
- package/commands/gsd/workspace.md +52 -0
- package/commands/gsd/workstreams.md +70 -0
- package/get-shit-done/bin/check-latest-version.cjs +106 -0
- package/get-shit-done/bin/gsd-tools.cjs +1676 -0
- package/get-shit-done/bin/lib/active-workstream-store.cjs +302 -0
- package/get-shit-done/bin/lib/adr-parser.cjs +394 -0
- package/get-shit-done/bin/lib/agent-command-router.cjs +65 -0
- package/get-shit-done/bin/lib/artifacts.cjs +53 -0
- package/get-shit-done/bin/lib/audit.cjs +755 -0
- package/get-shit-done/bin/lib/check-command-router.cjs +333 -0
- package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +118 -0
- package/get-shit-done/bin/lib/clock.cjs +96 -0
- package/get-shit-done/bin/lib/clusters.cjs +135 -0
- package/get-shit-done/bin/lib/code-review-flags.cjs +74 -0
- package/get-shit-done/bin/lib/command-aliases.cjs +815 -0
- package/get-shit-done/bin/lib/command-arg-projection.cjs +62 -0
- package/get-shit-done/bin/lib/command-routing-hub.cjs +388 -0
- package/get-shit-done/bin/lib/commands.cjs +1188 -0
- package/get-shit-done/bin/lib/config-schema.cjs +31 -0
- package/get-shit-done/bin/lib/config.cjs +728 -0
- package/get-shit-done/bin/lib/configuration.cjs +248 -0
- package/get-shit-done/bin/lib/context-utilization.cjs +47 -0
- package/get-shit-done/bin/lib/core.cjs +2121 -0
- package/get-shit-done/bin/lib/decisions.cjs +116 -0
- package/get-shit-done/bin/lib/docs.cjs +270 -0
- package/get-shit-done/bin/lib/drift.cjs +388 -0
- package/get-shit-done/bin/lib/fallow-runner.cjs +109 -0
- package/get-shit-done/bin/lib/frontmatter.cjs +389 -0
- package/get-shit-done/bin/lib/gap-checker.cjs +205 -0
- package/get-shit-done/bin/lib/graphify.cjs +592 -0
- package/get-shit-done/bin/lib/gsd2-import.cjs +514 -0
- package/get-shit-done/bin/lib/init-command-router.cjs +58 -0
- package/get-shit-done/bin/lib/init.cjs +2112 -0
- package/get-shit-done/bin/lib/install-profiles.cjs +603 -0
- package/get-shit-done/bin/lib/installer-migration-authoring.cjs +117 -0
- package/get-shit-done/bin/lib/installer-migration-report.cjs +354 -0
- package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
- package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
- package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
- package/get-shit-done/bin/lib/installer-migrations.cjs +778 -0
- package/get-shit-done/bin/lib/intel.cjs +708 -0
- package/get-shit-done/bin/lib/learnings.cjs +421 -0
- package/get-shit-done/bin/lib/milestone.cjs +314 -0
- package/get-shit-done/bin/lib/model-catalog.cjs +212 -0
- package/get-shit-done/bin/lib/model-profiles.cjs +31 -0
- package/get-shit-done/bin/lib/observability/event.cjs +82 -0
- package/get-shit-done/bin/lib/observability/logger.cjs +174 -0
- package/get-shit-done/bin/lib/observability/redaction.cjs +50 -0
- package/get-shit-done/bin/lib/package-identity.cjs +31 -0
- package/get-shit-done/bin/lib/phase-command-router.cjs +191 -0
- package/get-shit-done/bin/lib/phase-lifecycle.cjs +80 -0
- package/get-shit-done/bin/lib/phase.cjs +1607 -0
- package/get-shit-done/bin/lib/phases-command-router.cjs +39 -0
- package/get-shit-done/bin/lib/plan-scan.cjs +97 -0
- package/get-shit-done/bin/lib/planning-workspace.cjs +238 -0
- package/get-shit-done/bin/lib/profile-output.cjs +1141 -0
- package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
- package/get-shit-done/bin/lib/project-root.cjs +112 -0
- package/get-shit-done/bin/lib/prompt-budget.cjs +399 -0
- package/get-shit-done/bin/lib/review-reviewer-selection.cjs +125 -0
- package/get-shit-done/bin/lib/roadmap-command-router.cjs +28 -0
- package/get-shit-done/bin/lib/roadmap.cjs +650 -0
- package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +301 -0
- package/get-shit-done/bin/lib/runtime-homes.cjs +222 -0
- package/get-shit-done/bin/lib/runtime-name-policy.cjs +83 -0
- package/get-shit-done/bin/lib/runtime-slash.cjs +112 -0
- package/get-shit-done/bin/lib/schema-detect.cjs +165 -0
- package/get-shit-done/bin/lib/secrets.cjs +32 -0
- package/get-shit-done/bin/lib/security.cjs +600 -0
- package/get-shit-done/bin/lib/semver-compare.cjs +35 -0
- package/get-shit-done/bin/lib/shell-command-projection.cjs +500 -0
- package/get-shit-done/bin/lib/state-command-router.cjs +252 -0
- package/get-shit-done/bin/lib/state-document.cjs +263 -0
- package/get-shit-done/bin/lib/state.cjs +2038 -0
- package/get-shit-done/bin/lib/surface.cjs +470 -0
- package/get-shit-done/bin/lib/task-command-router.cjs +81 -0
- package/get-shit-done/bin/lib/template.cjs +228 -0
- package/get-shit-done/bin/lib/uat.cjs +289 -0
- package/get-shit-done/bin/lib/update-context.cjs +209 -0
- package/get-shit-done/bin/lib/validate-command-router.cjs +83 -0
- package/get-shit-done/bin/lib/validate.cjs +92 -0
- package/get-shit-done/bin/lib/verify-command-router.cjs +40 -0
- package/get-shit-done/bin/lib/verify.cjs +1511 -0
- package/get-shit-done/bin/lib/workstream-inventory-builder.cjs +74 -0
- package/get-shit-done/bin/lib/workstream-inventory.cjs +146 -0
- package/get-shit-done/bin/lib/workstream-name-policy.cjs +94 -0
- package/get-shit-done/bin/lib/workstream.cjs +389 -0
- package/get-shit-done/bin/lib/worktree-safety.cjs +985 -0
- package/get-shit-done/bin/shared/config-defaults.manifest.json +97 -0
- package/get-shit-done/bin/shared/config-schema.manifest.json +175 -0
- package/get-shit-done/bin/shared/model-catalog.json +122 -0
- package/get-shit-done/bin/shared/runtime-aliases.manifest.json +75 -0
- package/get-shit-done/bin/verify-reapply-patches.cjs +352 -0
- package/get-shit-done/contexts/dev.md +21 -0
- package/get-shit-done/contexts/research.md +22 -0
- package/get-shit-done/contexts/review.md +23 -0
- package/get-shit-done/references/agent-contracts.md +79 -0
- package/get-shit-done/references/ai-evals.md +156 -0
- package/get-shit-done/references/ai-frameworks.md +186 -0
- package/get-shit-done/references/artifact-types.md +131 -0
- package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
- package/get-shit-done/references/checkpoints.md +814 -0
- package/get-shit-done/references/common-bug-patterns.md +114 -0
- package/get-shit-done/references/context-budget.md +85 -0
- package/get-shit-done/references/continuation-format.md +253 -0
- package/get-shit-done/references/debugger-philosophy.md +76 -0
- package/get-shit-done/references/decimal-phase-calculation.md +64 -0
- package/get-shit-done/references/doc-conflict-engine.md +91 -0
- package/get-shit-done/references/domain-probes.md +125 -0
- package/get-shit-done/references/execute-mvp-tdd.md +81 -0
- package/get-shit-done/references/executor-examples.md +110 -0
- package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
- package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
- package/get-shit-done/references/gate-prompts.md +100 -0
- package/get-shit-done/references/gates.md +70 -0
- package/get-shit-done/references/git-integration.md +298 -0
- package/get-shit-done/references/git-planning-commit.md +40 -0
- package/get-shit-done/references/ios-scaffold.md +123 -0
- package/get-shit-done/references/mandatory-initial-read.md +2 -0
- package/get-shit-done/references/model-profile-resolution.md +38 -0
- package/get-shit-done/references/model-profiles.md +245 -0
- package/get-shit-done/references/mvp-concepts.md +49 -0
- package/get-shit-done/references/phase-argument-parsing.md +61 -0
- package/get-shit-done/references/planner-antipatterns.md +89 -0
- package/get-shit-done/references/planner-chunked.md +49 -0
- package/get-shit-done/references/planner-gap-closure.md +62 -0
- package/get-shit-done/references/planner-graphify-auto-update.md +67 -0
- package/get-shit-done/references/planner-human-verify-mode.md +57 -0
- package/get-shit-done/references/planner-interface-context.md +62 -0
- package/get-shit-done/references/planner-mvp-mode.md +53 -0
- package/get-shit-done/references/planner-reviews.md +39 -0
- package/get-shit-done/references/planner-revision.md +87 -0
- package/get-shit-done/references/planner-source-audit.md +73 -0
- package/get-shit-done/references/planning-config.md +471 -0
- package/get-shit-done/references/project-skills-discovery.md +19 -0
- package/get-shit-done/references/questioning.md +162 -0
- package/get-shit-done/references/revision-loop.md +97 -0
- package/get-shit-done/references/scout-codebase.md +51 -0
- package/get-shit-done/references/skeleton-template.md +48 -0
- package/get-shit-done/references/sketch-interactivity.md +41 -0
- package/get-shit-done/references/sketch-theme-system.md +94 -0
- package/get-shit-done/references/sketch-tooling.md +45 -0
- package/get-shit-done/references/sketch-variant-patterns.md +81 -0
- package/get-shit-done/references/spidr-splitting.md +69 -0
- package/get-shit-done/references/tdd.md +330 -0
- package/get-shit-done/references/thinking-models-debug.md +44 -0
- package/get-shit-done/references/thinking-models-execution.md +50 -0
- package/get-shit-done/references/thinking-models-planning.md +62 -0
- package/get-shit-done/references/thinking-models-research.md +50 -0
- package/get-shit-done/references/thinking-models-verification.md +55 -0
- package/get-shit-done/references/thinking-partner.md +96 -0
- package/get-shit-done/references/ui-brand.md +160 -0
- package/get-shit-done/references/universal-anti-patterns.md +63 -0
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/user-story-template.md +58 -0
- package/get-shit-done/references/verification-overrides.md +227 -0
- package/get-shit-done/references/verification-patterns.md +612 -0
- package/get-shit-done/references/verify-mvp-mode.md +85 -0
- package/get-shit-done/references/workstream-flag.md +111 -0
- package/get-shit-done/references/worktree-path-safety.md +89 -0
- package/get-shit-done/templates/AI-SPEC.md +246 -0
- package/get-shit-done/templates/DEBUG.md +169 -0
- package/get-shit-done/templates/README.md +77 -0
- package/get-shit-done/templates/SECURITY.md +61 -0
- package/get-shit-done/templates/UAT.md +265 -0
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/VALIDATION.md +76 -0
- package/get-shit-done/templates/claude-md.md +145 -0
- package/get-shit-done/templates/codebase/architecture.md +255 -0
- package/get-shit-done/templates/codebase/concerns.md +310 -0
- package/get-shit-done/templates/codebase/conventions.md +307 -0
- package/get-shit-done/templates/codebase/integrations.md +280 -0
- package/get-shit-done/templates/codebase/stack.md +186 -0
- package/get-shit-done/templates/codebase/structure.md +285 -0
- package/get-shit-done/templates/codebase/testing.md +480 -0
- package/get-shit-done/templates/config.json +62 -0
- package/get-shit-done/templates/context.md +352 -0
- package/get-shit-done/templates/continue-here.md +78 -0
- package/get-shit-done/templates/copilot-instructions.md +7 -0
- package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discovery.md +146 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/milestone-archive.md +123 -0
- package/get-shit-done/templates/milestone.md +115 -0
- package/get-shit-done/templates/phase-prompt.md +610 -0
- package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
- package/get-shit-done/templates/project.md +186 -0
- package/get-shit-done/templates/requirements.md +231 -0
- package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
- package/get-shit-done/templates/research-project/FEATURES.md +147 -0
- package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
- package/get-shit-done/templates/research-project/STACK.md +120 -0
- package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
- package/get-shit-done/templates/research.md +592 -0
- package/get-shit-done/templates/retrospective.md +54 -0
- package/get-shit-done/templates/roadmap.md +202 -0
- package/get-shit-done/templates/spec.md +307 -0
- package/get-shit-done/templates/state.md +195 -0
- package/get-shit-done/templates/summary-complex.md +59 -0
- package/get-shit-done/templates/summary-minimal.md +41 -0
- package/get-shit-done/templates/summary-standard.md +48 -0
- package/get-shit-done/templates/summary.md +248 -0
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/templates/user-setup.md +311 -0
- package/get-shit-done/templates/verification-report.md +322 -0
- package/get-shit-done/workflows/_runtime-launcher.snippet.sh +1 -0
- package/get-shit-done/workflows/add-backlog.md +91 -0
- package/get-shit-done/workflows/add-phase.md +113 -0
- package/get-shit-done/workflows/add-tests.md +355 -0
- package/get-shit-done/workflows/add-todo.md +161 -0
- package/get-shit-done/workflows/ai-integration-phase.md +295 -0
- package/get-shit-done/workflows/analyze-dependencies.md +96 -0
- package/get-shit-done/workflows/audit-fix.md +178 -0
- package/get-shit-done/workflows/audit-milestone.md +358 -0
- package/get-shit-done/workflows/audit-uat.md +110 -0
- package/get-shit-done/workflows/autonomous.md +795 -0
- package/get-shit-done/workflows/check-todos.md +180 -0
- package/get-shit-done/workflows/cleanup.md +155 -0
- package/get-shit-done/workflows/code-review-fix.md +502 -0
- package/get-shit-done/workflows/code-review.md +656 -0
- package/get-shit-done/workflows/complete-milestone.md +855 -0
- package/get-shit-done/workflows/debug.md +232 -0
- package/get-shit-done/workflows/diagnose-issues.md +241 -0
- package/get-shit-done/workflows/discovery-phase.md +291 -0
- package/get-shit-done/workflows/discuss-phase/modes/advisor.md +176 -0
- package/get-shit-done/workflows/discuss-phase/modes/all.md +28 -0
- package/get-shit-done/workflows/discuss-phase/modes/analyze.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/auto.md +57 -0
- package/get-shit-done/workflows/discuss-phase/modes/batch.md +52 -0
- package/get-shit-done/workflows/discuss-phase/modes/chain.md +98 -0
- package/get-shit-done/workflows/discuss-phase/modes/default.md +141 -0
- package/get-shit-done/workflows/discuss-phase/modes/power.md +44 -0
- package/get-shit-done/workflows/discuss-phase/modes/text.md +55 -0
- package/get-shit-done/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/get-shit-done/workflows/discuss-phase/templates/context.md +136 -0
- package/get-shit-done/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/get-shit-done/workflows/discuss-phase-assumptions.md +675 -0
- package/get-shit-done/workflows/discuss-phase-power.md +291 -0
- package/get-shit-done/workflows/discuss-phase.md +499 -0
- package/get-shit-done/workflows/do.md +111 -0
- package/get-shit-done/workflows/docs-update.md +1162 -0
- package/get-shit-done/workflows/edit-phase.md +295 -0
- package/get-shit-done/workflows/eval-review.md +156 -0
- package/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +82 -0
- package/get-shit-done/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/get-shit-done/workflows/execute-phase/steps/post-merge-gate.md +117 -0
- package/get-shit-done/workflows/execute-phase.md +1709 -0
- package/get-shit-done/workflows/execute-plan.md +526 -0
- package/get-shit-done/workflows/explore.md +144 -0
- package/get-shit-done/workflows/extract-learnings.md +243 -0
- package/get-shit-done/workflows/fast.md +124 -0
- package/get-shit-done/workflows/forensics.md +279 -0
- package/get-shit-done/workflows/graduation.md +196 -0
- package/get-shit-done/workflows/health.md +224 -0
- package/get-shit-done/workflows/help/modes/brief.md +22 -0
- package/get-shit-done/workflows/help/modes/default.md +50 -0
- package/get-shit-done/workflows/help/modes/full.md +784 -0
- package/get-shit-done/workflows/help/modes/topic.md +74 -0
- package/get-shit-done/workflows/help.md +24 -0
- package/get-shit-done/workflows/import.md +254 -0
- package/get-shit-done/workflows/inbox.md +387 -0
- package/get-shit-done/workflows/ingest-docs.md +339 -0
- package/get-shit-done/workflows/insert-phase.md +152 -0
- package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
- package/get-shit-done/workflows/list-workspaces.md +57 -0
- package/get-shit-done/workflows/manager.md +393 -0
- package/get-shit-done/workflows/map-codebase.md +444 -0
- package/get-shit-done/workflows/milestone-summary.md +224 -0
- package/get-shit-done/workflows/mvp-phase.md +222 -0
- package/get-shit-done/workflows/new-milestone.md +635 -0
- package/get-shit-done/workflows/new-project.md +1555 -0
- package/get-shit-done/workflows/new-workspace.md +240 -0
- package/get-shit-done/workflows/next.md +299 -0
- package/get-shit-done/workflows/node-repair.md +92 -0
- package/get-shit-done/workflows/note.md +158 -0
- package/get-shit-done/workflows/pause-work.md +244 -0
- package/get-shit-done/workflows/plan-milestone-gaps.md +281 -0
- package/get-shit-done/workflows/plan-phase.md +1809 -0
- package/get-shit-done/workflows/plan-review-convergence.md +346 -0
- package/get-shit-done/workflows/plant-seed.md +230 -0
- package/get-shit-done/workflows/pr-branch.md +157 -0
- package/get-shit-done/workflows/profile-user.md +453 -0
- package/get-shit-done/workflows/progress.md +699 -0
- package/get-shit-done/workflows/quick.md +1039 -0
- package/get-shit-done/workflows/reapply-patches.md +426 -0
- package/get-shit-done/workflows/remove-phase.md +156 -0
- package/get-shit-done/workflows/remove-workspace.md +108 -0
- package/get-shit-done/workflows/resume-project.md +332 -0
- package/get-shit-done/workflows/review.md +623 -0
- package/get-shit-done/workflows/scan.md +105 -0
- package/get-shit-done/workflows/secure-phase.md +180 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/settings-advanced.md +620 -0
- package/get-shit-done/workflows/settings-integrations.md +312 -0
- package/get-shit-done/workflows/settings.md +552 -0
- package/get-shit-done/workflows/ship.md +356 -0
- package/get-shit-done/workflows/sketch-wrap-up.md +286 -0
- package/get-shit-done/workflows/sketch.md +361 -0
- package/get-shit-done/workflows/spec-phase.md +262 -0
- package/get-shit-done/workflows/spike-wrap-up.md +307 -0
- package/get-shit-done/workflows/spike.md +453 -0
- package/get-shit-done/workflows/stats.md +80 -0
- package/get-shit-done/workflows/sync-skills.md +182 -0
- package/get-shit-done/workflows/thread.md +222 -0
- package/get-shit-done/workflows/transition.md +694 -0
- package/get-shit-done/workflows/ui-phase.md +328 -0
- package/get-shit-done/workflows/ui-review.md +193 -0
- package/get-shit-done/workflows/ultraplan-phase.md +199 -0
- package/get-shit-done/workflows/undo.md +314 -0
- package/get-shit-done/workflows/update.md +443 -0
- package/get-shit-done/workflows/validate-phase.md +179 -0
- package/get-shit-done/workflows/verify-phase.md +544 -0
- package/get-shit-done/workflows/verify-work.md +781 -0
- package/hooks/dist/gsd-check-update-worker.js +95 -0
- package/hooks/dist/gsd-check-update.js +64 -0
- package/hooks/dist/gsd-context-monitor.js +195 -0
- package/hooks/dist/gsd-graphify-update.sh +158 -0
- package/hooks/dist/gsd-phase-boundary.sh +47 -0
- package/hooks/dist/gsd-prompt-guard.js +97 -0
- package/hooks/dist/gsd-read-guard.js +101 -0
- package/hooks/dist/gsd-read-injection-scanner.js +203 -0
- package/hooks/dist/gsd-session-state.sh +59 -0
- package/hooks/dist/gsd-statusline.js +548 -0
- package/hooks/dist/gsd-update-banner.js +134 -0
- package/hooks/dist/gsd-validate-commit.sh +57 -0
- package/hooks/dist/gsd-workflow-guard.js +166 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/gsd-check-update-worker.js +95 -0
- package/hooks/gsd-check-update.js +64 -0
- package/hooks/gsd-context-monitor.js +195 -0
- package/hooks/gsd-graphify-update.sh +158 -0
- package/hooks/gsd-phase-boundary.sh +47 -0
- package/hooks/gsd-prompt-guard.js +97 -0
- package/hooks/gsd-read-guard.js +101 -0
- package/hooks/gsd-read-injection-scanner.js +203 -0
- package/hooks/gsd-session-state.sh +59 -0
- package/hooks/gsd-statusline.js +548 -0
- package/hooks/gsd-update-banner.js +134 -0
- package/hooks/gsd-validate-commit.sh +57 -0
- package/hooks/gsd-workflow-guard.js +166 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/managed-hooks-registry.cjs +34 -0
- package/package.json +102 -0
- package/scripts/affected-tests-lib.cjs +541 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +339 -0
- package/scripts/build-hooks.js +236 -0
- package/scripts/changeset/README.md +129 -0
- package/scripts/changeset/cli.cjs +392 -0
- package/scripts/changeset/github-release-notes.cjs +199 -0
- package/scripts/changeset/lint.cjs +110 -0
- package/scripts/changeset/new.cjs +137 -0
- package/scripts/changeset/parse.cjs +114 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +130 -0
- package/scripts/check-alias-drift.cjs +108 -0
- package/scripts/check-env.cjs +302 -0
- package/scripts/check-npm-integrity.cjs +209 -0
- package/scripts/ci-guard-runner.cjs +16 -0
- package/scripts/ci-prepare-test-scope.cjs +46 -0
- package/scripts/ci-rebase-check.cjs +85 -0
- package/scripts/ci-test-scope.cjs +302 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +147 -0
- package/scripts/fix-slash-commands.cjs +147 -0
- package/scripts/gen-inventory-manifest.cjs +109 -0
- package/scripts/generate-package-identity.cjs +104 -0
- package/scripts/lint-command-contract.cjs +108 -0
- package/scripts/lint-descriptions.cjs +83 -0
- package/scripts/lint-docs-required.cjs +222 -0
- package/scripts/lint-no-source-grep-extras.cjs +81 -0
- package/scripts/lint-no-source-grep.cjs +174 -0
- package/scripts/lint-package-identity-drift.cjs +141 -0
- package/scripts/lint-pr-check-project-dir.cjs +98 -0
- package/scripts/lint-shared-module-handsync.cjs +388 -0
- package/scripts/lint-shell-command-projection-drift.cjs +57 -0
- package/scripts/lint-skill-deps.cjs +180 -0
- package/scripts/lint-test-file-count.allowlist.json +36 -0
- package/scripts/lint-test-file-count.cjs +190 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +203 -0
- package/scripts/release-tarball-smoke.cjs +627 -0
- package/scripts/run-affected-tests.cjs +6 -0
- package/scripts/run-cross-platform-tests.cjs +63 -0
- package/scripts/run-tests.cjs +282 -0
- package/scripts/secret-scan-lint.sh +231 -0
- package/scripts/secret-scan.sh +358 -0
- package/scripts/setup-branch-protection.sh +236 -0
- package/scripts/shared-module-handsync-allowlist.json +183 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/sync-runtime-launcher.cjs +402 -0
- package/scripts/test-failure-reasons.cjs +34 -0
- package/scripts/workflow-policy.cjs +450 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# base64-scan.sh — Detect base64-obfuscated prompt injection in source files
|
|
3
|
+
#
|
|
4
|
+
# Extracts base64 blobs >= 40 chars, decodes them, and checks decoded content
|
|
5
|
+
# against the same injection patterns used by prompt-injection-scan.sh.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# scripts/base64-scan.sh --diff origin/main # CI mode: scan changed files
|
|
9
|
+
# scripts/base64-scan.sh --file path/to/file # Scan a single file
|
|
10
|
+
# scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
|
|
11
|
+
#
|
|
12
|
+
# Exit codes:
|
|
13
|
+
# 0 = clean
|
|
14
|
+
# 1 = findings detected
|
|
15
|
+
# 2 = usage error
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# ── Locale hardening (#116) ───────────────────────────────────────────────────
|
|
19
|
+
# BSD tr (macOS) treats input bytes as multi-byte characters under any UTF-8
|
|
20
|
+
# locale. When the input to `tr -cd '[:print:]'` contains bytes that are not
|
|
21
|
+
# valid UTF-8 start sequences (e.g. lone continuation bytes 0x80–0x9F), BSD tr
|
|
22
|
+
# emits "Illegal byte sequence" to stderr and exits non-zero. Setting LC_ALL=C
|
|
23
|
+
# forces the C locale throughout the script so every byte 0x00–0xFF is a valid
|
|
24
|
+
# character — no multi-byte interpretation, no illegal-byte errors.
|
|
25
|
+
#
|
|
26
|
+
# This is safe for our use-case: all injection patterns are ASCII; base64 -d
|
|
27
|
+
# and grep -E POSIX classes ([:space:], [:print:]) behave correctly in C locale.
|
|
28
|
+
#
|
|
29
|
+
# Source: `man tr` on macOS 26.5 — ENVIRONMENT section states LC_ALL / LC_CTYPE
|
|
30
|
+
# control character interpretation; BSD tr rejects invalid multi-byte sequences.
|
|
31
|
+
# Empirically verified: `printf '\x80\x81hello' | LC_ALL=C tr -cd '[:print:]'`
|
|
32
|
+
# exits 0 and strips the high bytes cleanly.
|
|
33
|
+
export LC_ALL=C
|
|
34
|
+
|
|
35
|
+
MIN_BLOB_LENGTH=40
|
|
36
|
+
# Lines longer than this byte count are skipped with a partial-scan warning.
|
|
37
|
+
# This prevents `grep -oE` from spending unbounded time on e.g. minified JS or
|
|
38
|
+
# single-line binary blobs. 1 MiB is large enough for any realistic text file
|
|
39
|
+
# line but small enough to bound blob-extraction cost.
|
|
40
|
+
MAX_LINE_BYTES=1048576
|
|
41
|
+
|
|
42
|
+
# -- Portable timeout wrapper (#116) ------------------------------------------
|
|
43
|
+
# macOS does not ship GNU coreutils timeout. We probe for it (or gtimeout
|
|
44
|
+
# from homebrew coreutils), falling back to perl alarm(N)+exec.
|
|
45
|
+
# Exit codes: GNU timeout uses 124 on timeout; perl SIGALRM produces 142.
|
|
46
|
+
# Both are treated as timeout exits by is_timeout_exit() below.
|
|
47
|
+
# Usage: run_with_timeout <seconds> <command> [args...]
|
|
48
|
+
_TIMEOUT_CMD=""
|
|
49
|
+
# shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
|
|
50
|
+
_init_timeout_cmd() {
|
|
51
|
+
if [[ -n "$_TIMEOUT_CMD" ]]; then return; fi
|
|
52
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
53
|
+
_TIMEOUT_CMD="timeout"
|
|
54
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
55
|
+
_TIMEOUT_CMD="gtimeout"
|
|
56
|
+
else
|
|
57
|
+
_TIMEOUT_CMD="perl_alarm"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
|
|
62
|
+
run_with_timeout() {
|
|
63
|
+
local secs="$1"; shift
|
|
64
|
+
_init_timeout_cmd
|
|
65
|
+
case "$_TIMEOUT_CMD" in
|
|
66
|
+
timeout|gtimeout)
|
|
67
|
+
"$_TIMEOUT_CMD" "$secs" "$@"
|
|
68
|
+
;;
|
|
69
|
+
perl_alarm)
|
|
70
|
+
# perl sets SIGALRM after N seconds, then exec()s the command.
|
|
71
|
+
# Exit 142 (SIGALRM) when timed out.
|
|
72
|
+
perl -e '
|
|
73
|
+
my $secs = shift @ARGV;
|
|
74
|
+
alarm($secs);
|
|
75
|
+
exec(@ARGV) or die "exec: $!\n";
|
|
76
|
+
' -- "$secs" "$@"
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# is_timeout_exit: returns 0 (true) if rc indicates a timeout kill.
|
|
82
|
+
# shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
|
|
83
|
+
is_timeout_exit() { [[ "$1" -eq 124 || "$1" -eq 142 ]]; }
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ─── Injection Patterns (decoded content) ────────────────────────────────────
|
|
87
|
+
# Subset of patterns — if someone base64-encoded something, check for the
|
|
88
|
+
# most common injection indicators.
|
|
89
|
+
DECODED_PATTERNS=(
|
|
90
|
+
'ignore[[:space:]]+(all[[:space:]]+)?previous[[:space:]]+instructions'
|
|
91
|
+
'you[[:space:]]+are[[:space:]]+now[[:space:]]+'
|
|
92
|
+
'system[[:space:]]+prompt'
|
|
93
|
+
'</?system>'
|
|
94
|
+
'</?assistant>'
|
|
95
|
+
'\[SYSTEM\]'
|
|
96
|
+
'\[INST\]'
|
|
97
|
+
'<<SYS>>'
|
|
98
|
+
'override[[:space:]]+(system|safety|security)'
|
|
99
|
+
'pretend[[:space:]]+(you|to)[[:space:]]'
|
|
100
|
+
'act[[:space:]]+as[[:space:]]+(a|an|if)'
|
|
101
|
+
'jailbreak'
|
|
102
|
+
'bypass[[:space:]]+(safety|content|security)'
|
|
103
|
+
'eval[[:space:]]*\('
|
|
104
|
+
'exec[[:space:]]*\('
|
|
105
|
+
'rm[[:space:]]+-rf'
|
|
106
|
+
'curl[[:space:]].*\|[[:space:]]*sh'
|
|
107
|
+
'wget[[:space:]].*\|[[:space:]]*sh'
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# ─── Ignorelist ──────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
IGNOREFILE=".base64scanignore"
|
|
113
|
+
IGNORED_PATTERNS=()
|
|
114
|
+
|
|
115
|
+
load_ignorelist() {
|
|
116
|
+
if [[ -f "$IGNOREFILE" ]]; then
|
|
117
|
+
while IFS= read -r line; do
|
|
118
|
+
# Skip comments and empty lines
|
|
119
|
+
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
|
120
|
+
[[ -z "${line// }" ]] && continue
|
|
121
|
+
IGNORED_PATTERNS+=("$line")
|
|
122
|
+
done < "$IGNOREFILE"
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
is_ignored() {
|
|
127
|
+
local blob="$1"
|
|
128
|
+
if [[ ${#IGNORED_PATTERNS[@]} -eq 0 ]]; then
|
|
129
|
+
return 1
|
|
130
|
+
fi
|
|
131
|
+
for pattern in "${IGNORED_PATTERNS[@]}"; do
|
|
132
|
+
if [[ "$blob" == "$pattern" ]]; then
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
done
|
|
136
|
+
return 1
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# ─── Skip Rules ──────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
should_skip_file() {
|
|
142
|
+
local file="$1"
|
|
143
|
+
# Skip binary files
|
|
144
|
+
case "$file" in
|
|
145
|
+
*.png|*.jpg|*.jpeg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.otf) return 0 ;;
|
|
146
|
+
*.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0 ;;
|
|
147
|
+
*.pdf|*.doc|*.docx|*.xls|*.xlsx) return 0 ;;
|
|
148
|
+
esac
|
|
149
|
+
# Skip lockfiles and node_modules
|
|
150
|
+
case "$file" in
|
|
151
|
+
*/node_modules/*) return 0 ;;
|
|
152
|
+
*/package-lock.json) return 0 ;;
|
|
153
|
+
*/yarn.lock) return 0 ;;
|
|
154
|
+
*/pnpm-lock.yaml) return 0 ;;
|
|
155
|
+
esac
|
|
156
|
+
# Skip the scan scripts themselves and test files
|
|
157
|
+
case "$file" in
|
|
158
|
+
*/base64-scan.sh) return 0 ;;
|
|
159
|
+
*/security-scan.test.cjs) return 0 ;;
|
|
160
|
+
esac
|
|
161
|
+
# Skip scanner fixture directories — they contain deliberate injection samples
|
|
162
|
+
case "$file" in
|
|
163
|
+
tests/fixtures/*) return 0 ;;
|
|
164
|
+
esac
|
|
165
|
+
return 1
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
is_data_uri() {
|
|
169
|
+
local context="$1"
|
|
170
|
+
# data:image/png;base64,... or data:application/font-woff;base64,...
|
|
171
|
+
echo "$context" | grep -qE 'data:[a-zA-Z]+/[a-zA-Z0-9.+-]+;base64,' 2>/dev/null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# ─── File Collection ─────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
collect_files() {
|
|
177
|
+
local mode="$1"
|
|
178
|
+
shift
|
|
179
|
+
|
|
180
|
+
case "$mode" in
|
|
181
|
+
--diff)
|
|
182
|
+
local base="${1:-origin/main}"
|
|
183
|
+
git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
|
|
184
|
+
| grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
|
|
185
|
+
;;
|
|
186
|
+
--file)
|
|
187
|
+
if [[ -f "$1" ]]; then
|
|
188
|
+
echo "$1"
|
|
189
|
+
else
|
|
190
|
+
echo "Error: file not found: $1" >&2
|
|
191
|
+
exit 2
|
|
192
|
+
fi
|
|
193
|
+
;;
|
|
194
|
+
--dir)
|
|
195
|
+
local dir="$1"
|
|
196
|
+
if [[ ! -d "$dir" ]]; then
|
|
197
|
+
echo "Error: directory not found: $dir" >&2
|
|
198
|
+
exit 2
|
|
199
|
+
fi
|
|
200
|
+
find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
|
|
201
|
+
! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
|
|
202
|
+
;;
|
|
203
|
+
--stdin)
|
|
204
|
+
cat
|
|
205
|
+
;;
|
|
206
|
+
*)
|
|
207
|
+
echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
|
|
208
|
+
exit 2
|
|
209
|
+
;;
|
|
210
|
+
esac
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# ─── Scanner ─────────────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
extract_and_check_blobs() {
|
|
216
|
+
local file="$1"
|
|
217
|
+
local found=0
|
|
218
|
+
local line_num=0
|
|
219
|
+
|
|
220
|
+
while IFS= read -r line; do
|
|
221
|
+
line_num=$((line_num + 1))
|
|
222
|
+
|
|
223
|
+
# Guard: skip lines that exceed MAX_LINE_BYTES. Very long lines (e.g. a
|
|
224
|
+
# minified JS bundle stored as one line, or a binary file with no newlines)
|
|
225
|
+
# would cause `grep -oE` to spend unbounded time. We emit a partial-scan
|
|
226
|
+
# warning to stderr so the caller can see coverage was reduced.
|
|
227
|
+
if [[ ${#line} -gt $MAX_LINE_BYTES ]]; then
|
|
228
|
+
echo "SKIP: $file line $line_num (${#line} bytes > ${MAX_LINE_BYTES} limit — partial scan)" >&2
|
|
229
|
+
continue
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Skip data URIs — legitimate base64 usage
|
|
233
|
+
if is_data_uri "$line"; then
|
|
234
|
+
continue
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# Extract base64-like blobs (alphanumeric + / + = padding, >= MIN_BLOB_LENGTH)
|
|
238
|
+
local blobs
|
|
239
|
+
blobs=$(echo "$line" | grep -oE '[A-Za-z0-9+/]{'"$MIN_BLOB_LENGTH"',}={0,3}' 2>/dev/null || true)
|
|
240
|
+
|
|
241
|
+
if [[ -z "$blobs" ]]; then
|
|
242
|
+
continue
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
while IFS= read -r blob; do
|
|
246
|
+
[[ -z "$blob" ]] && continue
|
|
247
|
+
|
|
248
|
+
# Check ignorelist
|
|
249
|
+
if [[ ${#IGNORED_PATTERNS[@]} -gt 0 ]] && is_ignored "$blob"; then
|
|
250
|
+
continue
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# Try to decode — if it fails, not valid base64
|
|
254
|
+
local decoded
|
|
255
|
+
decoded=$(echo "$blob" | base64 -d 2>/dev/null || echo "")
|
|
256
|
+
|
|
257
|
+
if [[ -z "$decoded" ]]; then
|
|
258
|
+
continue
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# Check if decoded content is mostly printable text (not random binary)
|
|
262
|
+
local total_chars=${#decoded}
|
|
263
|
+
if [[ $total_chars -eq 0 ]]; then
|
|
264
|
+
continue
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
# Count printable ASCII characters
|
|
268
|
+
local printable_count
|
|
269
|
+
printable_count=$(echo -n "$decoded" | tr -cd '[:print:]' | wc -c | tr -d ' ')
|
|
270
|
+
# Skip if less than 70% printable (likely binary data, not obfuscated text)
|
|
271
|
+
if [[ $((printable_count * 100 / total_chars)) -lt 70 ]]; then
|
|
272
|
+
continue
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Scan decoded content against injection patterns
|
|
276
|
+
for pattern in "${DECODED_PATTERNS[@]}"; do
|
|
277
|
+
if echo "$decoded" | grep -iqE "$pattern" 2>/dev/null; then
|
|
278
|
+
if [[ $found -eq 0 ]]; then
|
|
279
|
+
echo "FAIL: $file"
|
|
280
|
+
found=1
|
|
281
|
+
fi
|
|
282
|
+
echo " line $line_num: base64 blob decodes to suspicious content"
|
|
283
|
+
echo " blob: ${blob:0:60}..."
|
|
284
|
+
echo " decoded: ${decoded:0:120}"
|
|
285
|
+
echo " matched: $pattern"
|
|
286
|
+
break
|
|
287
|
+
fi
|
|
288
|
+
done
|
|
289
|
+
done <<< "$blobs"
|
|
290
|
+
done < "$file"
|
|
291
|
+
|
|
292
|
+
return $found
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
# ─── Main ────────────────────────────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
main() {
|
|
298
|
+
if [[ $# -eq 0 ]]; then
|
|
299
|
+
echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
|
|
300
|
+
exit 2
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
load_ignorelist
|
|
304
|
+
|
|
305
|
+
local mode="$1"
|
|
306
|
+
shift
|
|
307
|
+
|
|
308
|
+
local files
|
|
309
|
+
files=$(collect_files "$mode" "$@")
|
|
310
|
+
|
|
311
|
+
if [[ -z "$files" ]]; then
|
|
312
|
+
echo "base64-scan: no files to scan"
|
|
313
|
+
exit 0
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
local total=0
|
|
317
|
+
local failed=0
|
|
318
|
+
|
|
319
|
+
while IFS= read -r file; do
|
|
320
|
+
[[ -z "$file" ]] && continue
|
|
321
|
+
if should_skip_file "$file"; then
|
|
322
|
+
continue
|
|
323
|
+
fi
|
|
324
|
+
total=$((total + 1))
|
|
325
|
+
if ! extract_and_check_blobs "$file"; then
|
|
326
|
+
failed=$((failed + 1))
|
|
327
|
+
fi
|
|
328
|
+
done <<< "$files"
|
|
329
|
+
|
|
330
|
+
echo ""
|
|
331
|
+
echo "base64-scan: scanned $total files, $failed with findings"
|
|
332
|
+
|
|
333
|
+
if [[ $failed -gt 0 ]]; then
|
|
334
|
+
exit 1
|
|
335
|
+
fi
|
|
336
|
+
exit 0
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
main "$@"
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copy GSD hooks to dist for installation.
|
|
4
|
+
* Validates JavaScript syntax before copying to prevent shipping broken hooks.
|
|
5
|
+
* See #1107, #1109, #1125, #1161 — a duplicate const declaration shipped
|
|
6
|
+
* in dist and caused PostToolUse hook errors for all users.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const vm = require('vm');
|
|
12
|
+
|
|
13
|
+
const HOOKS_DIR = path.join(__dirname, '..', 'hooks');
|
|
14
|
+
const DIST_DIR = path.join(HOOKS_DIR, 'dist');
|
|
15
|
+
// Per-process staging directory for atomic writes. Using process.pid in the
|
|
16
|
+
// name eliminates all contention between concurrent builders: each process
|
|
17
|
+
// owns its own staging dir and never races with another builder's cleanup.
|
|
18
|
+
// Lives under hooks/ so it shares a filesystem with DIST_DIR (POSIX
|
|
19
|
+
// rename(2) is only atomic within the same filesystem) but is NOT inside
|
|
20
|
+
// DIST_DIR — so readers that readdirSync(DIST_DIR) (e.g. bin/install.js,
|
|
21
|
+
// install-hooks-copy tests) never observe a transient ".tmp" sibling.
|
|
22
|
+
// The parent pattern hooks/.dist-staging-*/ is gitignored.
|
|
23
|
+
const STAGE_DIR = path.join(HOOKS_DIR, `.dist-staging-${process.pid}`);
|
|
24
|
+
|
|
25
|
+
// Hooks to copy (pure Node.js, no bundling needed)
|
|
26
|
+
const HOOKS_TO_COPY = [
|
|
27
|
+
'gsd-check-update-worker.js',
|
|
28
|
+
'gsd-check-update.js',
|
|
29
|
+
'gsd-context-monitor.js',
|
|
30
|
+
'gsd-prompt-guard.js',
|
|
31
|
+
'gsd-read-guard.js',
|
|
32
|
+
'gsd-read-injection-scanner.js',
|
|
33
|
+
'gsd-statusline.js',
|
|
34
|
+
'gsd-update-banner.js',
|
|
35
|
+
'gsd-workflow-guard.js',
|
|
36
|
+
// Community hooks (bash, opt-in via .planning/config.json hooks.community)
|
|
37
|
+
'gsd-session-state.sh',
|
|
38
|
+
'gsd-validate-commit.sh',
|
|
39
|
+
'gsd-phase-boundary.sh',
|
|
40
|
+
// Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
|
|
41
|
+
// .planning/config.json graphify.auto_update; off by default.
|
|
42
|
+
'gsd-graphify-update.sh'
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
// Subdirectories under hooks/ whose contents must also ship to dist. Each
|
|
46
|
+
// entry is copied as `hooks/<dir>/*` → `hooks/dist/<dir>/*` so detached
|
|
47
|
+
// helpers (e.g. hooks/lib/gsd-graphify-rebuild.sh) resolve from the hook's
|
|
48
|
+
// installed runtime path. See #3579.
|
|
49
|
+
const HOOKS_SUBDIRS_TO_COPY = ['lib'];
|
|
50
|
+
|
|
51
|
+
// Sync millisecond sleep using Atomics.wait on a throwaway SharedArrayBuffer.
|
|
52
|
+
// Used between Windows rename retries; this script is sync end-to-end so
|
|
53
|
+
// setTimeout would not work. Total worst-case backoff across MAX_ATTEMPTS
|
|
54
|
+
// is bounded (~400ms) — acceptable for a one-shot build script.
|
|
55
|
+
function sleepSync(ms) {
|
|
56
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Atomic-replace via fs.renameSync, with Windows-only retry and fallback.
|
|
61
|
+
*
|
|
62
|
+
* POSIX rename(2) atomically replaces dest even when readers hold open
|
|
63
|
+
* handles on it. Windows MoveFileEx (which fs.renameSync uses with
|
|
64
|
+
* MOVEFILE_REPLACE_EXISTING) cannot — it throws EPERM/EBUSY when another
|
|
65
|
+
* process has the destination open. Concurrent install.js readers and
|
|
66
|
+
* antivirus scanners are the realistic triggers; both release handles
|
|
67
|
+
* within milliseconds, so a short backoff resolves the race. After
|
|
68
|
+
* retries are exhausted, fall back to copy-then-unlink (re-introduces
|
|
69
|
+
* the truncate-then-write race for this single file but keeps the build
|
|
70
|
+
* moving rather than crashing). If even copy fails because dest is hard-
|
|
71
|
+
* locked, log a non-fatal warning and leave the prior dest in place — a
|
|
72
|
+
* subsequent build invocation will retry from a fresh state.
|
|
73
|
+
*/
|
|
74
|
+
function renameAtomicWithRetry(stagedDest, dest, hook) {
|
|
75
|
+
if (process.platform !== 'win32') {
|
|
76
|
+
fs.renameSync(stagedDest, dest);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const BACKOFFS_MS = [10, 30, 90, 270];
|
|
80
|
+
for (let attempt = 0; attempt <= BACKOFFS_MS.length; attempt++) {
|
|
81
|
+
try {
|
|
82
|
+
fs.renameSync(stagedDest, dest);
|
|
83
|
+
return;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
const transient = e && (e.code === 'EPERM' || e.code === 'EBUSY');
|
|
86
|
+
if (!transient) throw e;
|
|
87
|
+
if (attempt < BACKOFFS_MS.length) {
|
|
88
|
+
sleepSync(BACKOFFS_MS[attempt]);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// Retries exhausted; fall back to copy-then-unlink.
|
|
92
|
+
try {
|
|
93
|
+
fs.copyFileSync(stagedDest, dest);
|
|
94
|
+
try { fs.unlinkSync(stagedDest); } catch (_) { /* tolerate */ }
|
|
95
|
+
console.warn(`\x1b[33m! ${hook}: rename failed (${e.code}) after ${BACKOFFS_MS.length} retries; used copy-fallback\x1b[0m`);
|
|
96
|
+
return;
|
|
97
|
+
} catch (fallbackErr) {
|
|
98
|
+
try { fs.unlinkSync(stagedDest); } catch (_) { /* tolerate */ }
|
|
99
|
+
console.warn(`\x1b[33m! ${hook}: rename + copy fallback both failed (${e.code} → ${fallbackErr.code || fallbackErr.message}); leaving prior dest in place\x1b[0m`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Validate JavaScript syntax without executing the file.
|
|
108
|
+
* Catches SyntaxError (duplicate const, missing brackets, etc.)
|
|
109
|
+
* before the hook gets shipped to users.
|
|
110
|
+
*/
|
|
111
|
+
function validateSyntax(filePath) {
|
|
112
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
113
|
+
try {
|
|
114
|
+
// Use vm.compileFunction to check syntax without executing
|
|
115
|
+
new vm.Script(content, { filename: path.basename(filePath) });
|
|
116
|
+
return null; // No error
|
|
117
|
+
} catch (e) {
|
|
118
|
+
if (e instanceof SyntaxError) {
|
|
119
|
+
return e.message;
|
|
120
|
+
}
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function build() {
|
|
126
|
+
// Ensure dist and staging directories exist (staging is a sibling of dist
|
|
127
|
+
// used to make writes atomic — see STAGE_DIR comment above).
|
|
128
|
+
if (!fs.existsSync(DIST_DIR)) {
|
|
129
|
+
fs.mkdirSync(DIST_DIR, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
if (!fs.existsSync(STAGE_DIR)) {
|
|
132
|
+
fs.mkdirSync(STAGE_DIR, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let hasErrors = false;
|
|
136
|
+
|
|
137
|
+
// Copy hooks to dist with syntax validation
|
|
138
|
+
for (const hook of HOOKS_TO_COPY) {
|
|
139
|
+
const src = path.join(HOOKS_DIR, hook);
|
|
140
|
+
const dest = path.join(DIST_DIR, hook);
|
|
141
|
+
|
|
142
|
+
if (!fs.existsSync(src)) {
|
|
143
|
+
console.warn(`Warning: ${hook} not found, skipping`);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Validate JS syntax before copying (.sh files skip — not Node.js)
|
|
148
|
+
if (hook.endsWith('.js')) {
|
|
149
|
+
const syntaxError = validateSyntax(src);
|
|
150
|
+
if (syntaxError) {
|
|
151
|
+
console.error(`\x1b[31m✗ ${hook}: SyntaxError — ${syntaxError}\x1b[0m`);
|
|
152
|
+
hasErrors = true;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log(`\x1b[32m✓\x1b[0m Copying ${hook}...`);
|
|
158
|
+
// Atomic write: copy to a per-process staging file in the per-PID sibling
|
|
159
|
+
// STAGE_DIR (same filesystem as DIST_DIR so rename(2) is atomic), then
|
|
160
|
+
// rename into place. Multiple test files invoke this script concurrently
|
|
161
|
+
// from their before() hooks; fs.copyFileSync truncates then writes the
|
|
162
|
+
// destination — readers (install.js subprocesses spawned by parallel
|
|
163
|
+
// install tests) can observe the dest empty or partial mid-write,
|
|
164
|
+
// producing flaky failures such as bug-2136 part 4 where installed .sh
|
|
165
|
+
// hooks lacked their "# gsd-hook-version:" header. POSIX rename(2)
|
|
166
|
+
// makes the swap atomic so readers see either the old file or the new
|
|
167
|
+
// file. The staging file lives outside DIST_DIR so readdirSync(DIST_DIR)
|
|
168
|
+
// (in install.js and tests) never observes a transient ".tmp" sibling.
|
|
169
|
+
// Each process uses its own STAGE_DIR (keyed by PID) so concurrent
|
|
170
|
+
// builders never race on staging-dir creation or cleanup.
|
|
171
|
+
const stagedDest = path.join(STAGE_DIR, `${hook}.${Date.now()}`);
|
|
172
|
+
fs.copyFileSync(src, stagedDest);
|
|
173
|
+
// Preserve executable bit for shell scripts before rename so the
|
|
174
|
+
// installed file is executable from the very first observation.
|
|
175
|
+
if (hook.endsWith('.sh')) {
|
|
176
|
+
try { fs.chmodSync(stagedDest, 0o755); } catch (e) { /* Windows */ }
|
|
177
|
+
}
|
|
178
|
+
renameAtomicWithRetry(stagedDest, dest, hook);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Copy whitelisted hook subdirectories (e.g. hooks/lib/) into dist so the
|
|
182
|
+
// installer's readdir-and-isFile loop in bin/install.js sees them and
|
|
183
|
+
// detached hook helpers resolve from the installed runtime path (#3579).
|
|
184
|
+
for (const subdir of HOOKS_SUBDIRS_TO_COPY) {
|
|
185
|
+
const srcDir = path.join(HOOKS_DIR, subdir);
|
|
186
|
+
if (!fs.existsSync(srcDir)) continue;
|
|
187
|
+
const destDir = path.join(DIST_DIR, subdir);
|
|
188
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
189
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
190
|
+
for (const ent of entries) {
|
|
191
|
+
if (!ent.isFile()) continue;
|
|
192
|
+
const srcFile = path.join(srcDir, ent.name);
|
|
193
|
+
const destFile = path.join(destDir, ent.name);
|
|
194
|
+
if (ent.name.endsWith('.js')) {
|
|
195
|
+
const syntaxError = validateSyntax(srcFile);
|
|
196
|
+
if (syntaxError) {
|
|
197
|
+
console.error(`\x1b[31m✗ ${subdir}/${ent.name}: SyntaxError — ${syntaxError}\x1b[0m`);
|
|
198
|
+
hasErrors = true;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
console.log(`\x1b[32m✓\x1b[0m Copying ${subdir}/${ent.name}...`);
|
|
203
|
+
const stagedDest = path.join(STAGE_DIR, `${subdir}__${ent.name}.${Date.now()}`);
|
|
204
|
+
fs.copyFileSync(srcFile, stagedDest);
|
|
205
|
+
if (ent.name.endsWith('.sh')) {
|
|
206
|
+
try { fs.chmodSync(stagedDest, 0o755); } catch (e) { /* Windows */ }
|
|
207
|
+
}
|
|
208
|
+
renameAtomicWithRetry(stagedDest, destFile, `${subdir}/${ent.name}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Best-effort cleanup of this process's own staging dir. Since STAGE_DIR
|
|
213
|
+
// is per-PID (`.dist-staging-<pid>/`), no other builder touches it — so
|
|
214
|
+
// rmSync with recursive:true is safe and leaves no race window.
|
|
215
|
+
try {
|
|
216
|
+
fs.rmSync(STAGE_DIR, { recursive: true, force: true });
|
|
217
|
+
} catch (e) { /* tolerate ENOENT if the dir was never created (e.g. all hooks skipped) */ }
|
|
218
|
+
|
|
219
|
+
if (hasErrors) {
|
|
220
|
+
console.error('\n\x1b[31mBuild failed: fix syntax errors above before publishing.\x1b[0m');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log('\nBuild complete.');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Export HOOKS_TO_COPY so tests can require() this file and assert against
|
|
228
|
+
// the typed value instead of regex-parsing the source text (retires
|
|
229
|
+
// pending-migration-to-typed-ir for orphaned-hooks.test.cjs, per #455).
|
|
230
|
+
// Guard the build() call so requiring this file as a module does not trigger
|
|
231
|
+
// a full build run (which copies files and writes to disk).
|
|
232
|
+
if (require.main === module) {
|
|
233
|
+
build();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = { HOOKS_TO_COPY };
|