claude-code-superkit 0.0.0-stage → 1.5.3
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.md +374 -2
- package/bin/cli.js +153 -0
- package/bin/inject-tokens.js +224 -0
- package/bin/measure-tokens.js +17 -0
- package/bin/superkit-counts-verify.sh +30 -0
- package/lib/codex.js +246 -0
- package/lib/docs-scaffold.js +147 -0
- package/lib/installer.js +499 -0
- package/lib/prompts.js +51 -0
- package/lib/settings-builder.js +51 -0
- package/lib/superpowers.js +99 -0
- package/lib/utils.js +117 -0
- package/lib/validator.js +84 -0
- package/package.json +49 -4
- package/packages/codex/AGENTS.md +396 -0
- package/packages/codex/INSTALL.md +342 -0
- package/packages/codex/config.toml +22 -0
- package/packages/codex/hooks/superkit.mjs +203 -0
- package/packages/codex/hooks.json +30 -0
- package/packages/codex/legacy-baseline.json +88 -0
- package/packages/codex/native-skills.txt +65 -0
- package/packages/codex/rules/default.rules +565 -0
- package/packages/codex/skills/ai-slop-cleaner/SKILL.md +44 -0
- package/packages/codex/skills/api-contract-sync/SKILL.md +96 -0
- package/packages/codex/skills/architect/SKILL.md +49 -0
- package/packages/codex/skills/audit-backend/SKILL.md +80 -0
- package/packages/codex/skills/audit-frontend/SKILL.md +82 -0
- package/packages/codex/skills/audit-infra/SKILL.md +87 -0
- package/packages/codex/skills/audit-orchestrator/SKILL.md +114 -0
- package/packages/codex/skills/behavioral-nudge-engine/SKILL.md +110 -0
- package/packages/codex/skills/benchmark/SKILL.md +49 -0
- package/packages/codex/skills/bot-reviewer/SKILL.md +94 -0
- package/packages/codex/skills/code-reviewer/SKILL.md +67 -0
- package/packages/codex/skills/codebase-onboarding-engineer/SKILL.md +46 -0
- package/packages/codex/skills/comment-rot-analyzer/SKILL.md +75 -0
- package/packages/codex/skills/commit-helper/SKILL.md +101 -0
- package/packages/codex/skills/critic/SKILL.md +56 -0
- package/packages/codex/skills/database-reviewer/SKILL.md +79 -0
- package/packages/codex/skills/debug-observer/SKILL.md +107 -0
- package/packages/codex/skills/dependency-checker/SKILL.md +69 -0
- package/packages/codex/skills/design-system-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/dev-orchestrator/SKILL.md +304 -0
- package/packages/codex/skills/docs-reviewer/SKILL.md +86 -0
- package/packages/codex/skills/drizzle-orm-expert/SKILL.md +246 -0
- package/packages/codex/skills/e2e-test-generator/SKILL.md +95 -0
- package/packages/codex/skills/evaluator/SKILL.md +63 -0
- package/packages/codex/skills/frontend-perf-reviewer/SKILL.md +95 -0
- package/packages/codex/skills/frontend-ui-color-reviewer/SKILL.md +68 -0
- package/packages/codex/skills/frontend-ui-design-critic/SKILL.md +99 -0
- package/packages/codex/skills/frontend-ui-interaction-reviewer/SKILL.md +102 -0
- package/packages/codex/skills/frontend-ui-motion-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/frontend-ui-reviewer/SKILL.md +81 -0
- package/packages/codex/skills/frontend-ui-typography-reviewer/SKILL.md +62 -0
- package/packages/codex/skills/gltf-debugging/SKILL.md +128 -0
- package/packages/codex/skills/go-benchmark/SKILL.md +217 -0
- package/packages/codex/skills/go-concurrency-reviewer/SKILL.md +113 -0
- package/packages/codex/skills/go-error-reviewer/SKILL.md +107 -0
- package/packages/codex/skills/go-grpc-patterns/SKILL.md +285 -0
- package/packages/codex/skills/go-modernizer/SKILL.md +111 -0
- package/packages/codex/skills/go-observability-reviewer/SKILL.md +121 -0
- package/packages/codex/skills/go-performance-reviewer/SKILL.md +112 -0
- package/packages/codex/skills/go-reviewer/SKILL.md +109 -0
- package/packages/codex/skills/go-samber-do/SKILL.md +158 -0
- package/packages/codex/skills/go-samber-lo/SKILL.md +178 -0
- package/packages/codex/skills/go-samber-oops/SKILL.md +174 -0
- package/packages/codex/skills/goal-verifier/SKILL.md +53 -0
- package/packages/codex/skills/health-checker/SKILL.md +109 -0
- package/packages/codex/skills/html-to-3d-texture/SKILL.md +153 -0
- package/packages/codex/skills/impeccable-craft/SKILL.md +101 -0
- package/packages/codex/skills/lint-runner/SKILL.md +85 -0
- package/packages/codex/skills/migrate/SKILL.md +60 -0
- package/packages/codex/skills/migration-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/minimal-change-engineer/SKILL.md +44 -0
- package/packages/codex/skills/new-migration/SKILL.md +59 -0
- package/packages/codex/skills/nextjs-supabase-auth/SKILL.md +227 -0
- package/packages/codex/skills/output-enforcement/SKILL.md +71 -0
- package/packages/codex/skills/plan-checker/SKILL.md +53 -0
- package/packages/codex/skills/postgresql-optimization/SKILL.md +159 -0
- package/packages/codex/skills/pre-deploy-validator/SKILL.md +111 -0
- package/packages/codex/skills/presentation-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/product-3d-lighting/SKILL.md +137 -0
- package/packages/codex/skills/project-architecture/SKILL.md +52 -0
- package/packages/codex/skills/py-reviewer/SKILL.md +100 -0
- package/packages/codex/skills/r3f-scene-reviewer/SKILL.md +88 -0
- package/packages/codex/skills/r3f-scroll-driven-3d/SKILL.md +162 -0
- package/packages/codex/skills/reality-checker/SKILL.md +61 -0
- package/packages/codex/skills/redis-patterns/SKILL.md +260 -0
- package/packages/codex/skills/review-orchestrator/SKILL.md +151 -0
- package/packages/codex/skills/rs-reviewer/SKILL.md +98 -0
- package/packages/codex/skills/ru-text/SKILL.md +132 -0
- package/packages/codex/skills/scaffold-endpoint/SKILL.md +44 -0
- package/packages/codex/skills/security-scanner/SKILL.md +94 -0
- package/packages/codex/skills/silent-failure-hunter/SKILL.md +176 -0
- package/packages/codex/skills/telegram-bot-builder/SKILL.md +120 -0
- package/packages/codex/skills/test-generator/SKILL.md +79 -0
- package/packages/codex/skills/test-runner/SKILL.md +101 -0
- package/packages/codex/skills/threejs-color-management/SKILL.md +97 -0
- package/packages/codex/skills/tree-generator/SKILL.md +63 -0
- package/packages/codex/skills/ts-reviewer/SKILL.md +90 -0
- package/packages/codex/skills/ui-design-reviewer/SKILL.md +89 -0
- package/packages/codex/skills/ui-reviewer/SKILL.md +103 -0
- package/packages/codex/skills/visual-reviewer/SKILL.md +85 -0
- package/packages/codex/skills/writing-agents/SKILL.md +165 -0
- package/packages/codex/skills/writing-commands/SKILL.md +173 -0
- package/packages/core/CLAUDE.md +118 -0
- package/packages/core/INTERNAL-FILES +16 -0
- package/packages/core/agents/ai-slop-cleaner.md +187 -0
- package/packages/core/agents/api-contract-sync.md +127 -0
- package/packages/core/agents/architect.md +205 -0
- package/packages/core/agents/audit-backend.md +137 -0
- package/packages/core/agents/audit-frontend.md +129 -0
- package/packages/core/agents/audit-infra.md +182 -0
- package/packages/core/agents/behavioral-nudge-engine.md +170 -0
- package/packages/core/agents/code-reviewer.md +195 -0
- package/packages/core/agents/codebase-onboarding-engineer.md +172 -0
- package/packages/core/agents/comment-rot-analyzer.md +115 -0
- package/packages/core/agents/critic.md +172 -0
- package/packages/core/agents/database-reviewer.md +201 -0
- package/packages/core/agents/debug-observer.md +261 -0
- package/packages/core/agents/dependency-checker.md +199 -0
- package/packages/core/agents/docs-reviewer.md +206 -0
- package/packages/core/agents/e2e-test-generator.md +261 -0
- package/packages/core/agents/evaluator.md +141 -0
- package/packages/core/agents/goal-verifier.md +130 -0
- package/packages/core/agents/health-checker.md +194 -0
- package/packages/core/agents/migration-reviewer.md +161 -0
- package/packages/core/agents/minimal-change-engineer.md +219 -0
- package/packages/core/agents/plan-checker.md +139 -0
- package/packages/core/agents/pre-deploy-validator.md +189 -0
- package/packages/core/agents/reality-checker.md +175 -0
- package/packages/core/agents/scaffold-endpoint.md +162 -0
- package/packages/core/agents/security-scanner.md +236 -0
- package/packages/core/agents/silent-failure-hunter.md +276 -0
- package/packages/core/agents/test-generator.md +146 -0
- package/packages/core/agents/tree-generator.md +141 -0
- package/packages/core/agents/ui-reviewer.md +172 -0
- package/packages/core/agents/visual-reviewer.md +211 -0
- package/packages/core/commands/audit.md +166 -0
- package/packages/core/commands/benchmark.md +130 -0
- package/packages/core/commands/commit.md +148 -0
- package/packages/core/commands/dev.md +302 -0
- package/packages/core/commands/docs-init.md +75 -0
- package/packages/core/commands/lint.md +137 -0
- package/packages/core/commands/migrate.md +146 -0
- package/packages/core/commands/new-migration.md +182 -0
- package/packages/core/commands/pair.md +209 -0
- package/packages/core/commands/review.md +195 -0
- package/packages/core/commands/security-scan.md +142 -0
- package/packages/core/commands/superkit-evolve.md +231 -0
- package/packages/core/commands/superkit-init.md +274 -0
- package/packages/core/commands/test.md +108 -0
- package/packages/core/commands/workflow.md +150 -0
- package/packages/core/docs/guide/01-getting-started.md +41 -0
- package/packages/core/docs-templates/README.md +33 -0
- package/packages/core/docs-templates/adr-template.md +62 -0
- package/packages/core/docs-templates/architecture/api-contracts.md +26 -0
- package/packages/core/docs-templates/architecture/api-reference.md +48 -0
- package/packages/core/docs-templates/architecture/auth-and-sessions.md +38 -0
- package/packages/core/docs-templates/architecture/backend-layers.md +42 -0
- package/packages/core/docs-templates/architecture/data-flow.md +26 -0
- package/packages/core/docs-templates/architecture/database-schema.md +51 -0
- package/packages/core/docs-templates/architecture/deployment.md +41 -0
- package/packages/core/docs-templates/architecture/frontend-state.md +46 -0
- package/packages/core/helpers/statusline.cjs +370 -0
- package/packages/core/hooks/CHAIN-SEMANTICS.md +84 -0
- package/packages/core/hooks/audit-settings-source.sh +98 -0
- package/packages/core/hooks/audit-trail.sh +120 -0
- package/packages/core/hooks/block-dangerous-git.sh +189 -0
- package/packages/core/hooks/bundle-import-check.sh +64 -0
- package/packages/core/hooks/compact-state-inject.sh +84 -0
- package/packages/core/hooks/config-protection.sh +68 -0
- package/packages/core/hooks/console-log-warning.sh +28 -0
- package/packages/core/hooks/context-monitor.sh +35 -0
- package/packages/core/hooks/dev-edit-counter.sh +59 -0
- package/packages/core/hooks/dev-marker-set.sh +55 -0
- package/packages/core/hooks/dev-required-on-commit.sh +426 -0
- package/packages/core/hooks/doc-check-on-commit.sh +396 -0
- package/packages/core/hooks/edit-streak-check.sh +63 -0
- package/packages/core/hooks/evolve-check.sh +78 -0
- package/packages/core/hooks/gateguard-pre-edit.sh +154 -0
- package/packages/core/hooks/gateguard-record-facts.sh +37 -0
- package/packages/core/hooks/intake-classifier.py +449 -0
- package/packages/core/hooks/lib/profile.sh +141 -0
- package/packages/core/hooks/loop-guard.sh +75 -0
- package/packages/core/hooks/migration-safety.sh +56 -0
- package/packages/core/hooks/plan-completion-gate.sh +61 -0
- package/packages/core/hooks/pre-compact-save.sh +89 -0
- package/packages/core/hooks/security-patterns.sh +119 -0
- package/packages/core/hooks/session-context-restore.sh +76 -0
- package/packages/core/hooks/subagent-stop-validate.sh +73 -0
- package/packages/core/hooks/superkit-counts-verify.sh +346 -0
- package/packages/core/hooks/superkit-update.sh +367 -0
- package/packages/core/hooks/tests/README.md +42 -0
- package/packages/core/hooks/tests/audit-trail-chain_test.sh +62 -0
- package/packages/core/hooks/tests/block-dangerous-git_test.sh +144 -0
- package/packages/core/hooks/tests/chain-semantics/h1.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/h2.sh +9 -0
- package/packages/core/hooks/tests/chain-semantics/h3.sh +11 -0
- package/packages/core/hooks/tests/chain-semantics/run-experiment.sh +65 -0
- package/packages/core/hooks/tests/chain-semantics/settings-experiment.json +15 -0
- package/packages/core/hooks/tests/counts-verify_test.sh +118 -0
- package/packages/core/hooks/tests/dev-required-on-commit_test.sh +207 -0
- package/packages/core/hooks/tests/dev-required-wip-on-main_test.sh +20 -0
- package/packages/core/hooks/tests/doc-check-on-commit_test.sh +122 -0
- package/packages/core/hooks/tests/edit-streak-check_test.sh +106 -0
- package/packages/core/hooks/tests/internal-manifest_test.sh +130 -0
- package/packages/core/hooks/tests/json-path_test.sh +108 -0
- package/packages/core/hooks/tests/mirror-invariants_test.sh +139 -0
- package/packages/core/hooks/tests/nudge-throttle_test.sh +95 -0
- package/packages/core/hooks/tests/profile-helper_test.sh +21 -0
- package/packages/core/hooks/tests/run-all.sh +47 -0
- package/packages/core/hooks/tests/session-key-helper_test.sh +17 -0
- package/packages/core/hooks/tests/superkit-update_test.sh +311 -0
- package/packages/core/hooks/tests/warn-visibility_test.sh +59 -0
- package/packages/core/hooks/user-intent-detect.sh +44 -0
- package/packages/core/hooks/verify-hooks.sh +69 -0
- package/packages/core/rules/auto-commands.md +124 -0
- package/packages/core/rules/coding-style.md +68 -0
- package/packages/core/rules/dev-workflow.md +77 -0
- package/packages/core/rules/documentation.md +106 -0
- package/packages/core/rules/frontend-aesthetics.md +53 -0
- package/packages/core/rules/git-workflow.md +14 -0
- package/packages/core/rules/security.md +29 -0
- package/packages/core/rules/superkit-integrity.md +114 -0
- package/packages/core/settings.json +229 -0
- package/packages/core/skills/drizzle-orm-expert/SKILL.md +260 -0
- package/packages/core/skills/nextjs-supabase-auth/SKILL.md +259 -0
- package/packages/core/skills/postgresql-optimization/SKILL.md +150 -0
- package/packages/core/skills/project-architecture/SKILL.md +31 -0
- package/packages/core/skills/project-scanner/SKILL.md +132 -0
- package/packages/core/skills/redis-patterns/SKILL.md +261 -0
- package/packages/core/skills/ru-text/SKILL.md +156 -0
- package/packages/core/skills/telegram-bot-builder/SKILL.md +145 -0
- package/packages/core/skills/writing-agents/SKILL.md +220 -0
- package/packages/core/skills/writing-commands/SKILL.md +176 -0
- package/packages/core/skills/writing-hooks/SKILL.md +335 -0
- package/packages/extras/bot-reviewer.md +279 -0
- package/packages/extras/design-system-reviewer.md +234 -0
- package/packages/extras/red-blue-auditor/README.md +117 -0
- package/packages/extras/red-blue-auditor/agent.md +128 -0
- package/packages/extras/red-blue-auditor/patterns/agent-config.txt +4 -0
- package/packages/extras/red-blue-auditor/patterns/hook-injection.txt +8 -0
- package/packages/extras/red-blue-auditor/patterns/mcp-risk.txt +5 -0
- package/packages/extras/red-blue-auditor/patterns/permission-abuse.txt +6 -0
- package/packages/extras/red-blue-auditor/patterns/secrets.txt +17 -0
- package/packages/extras/red-blue-auditor/scan.sh +88 -0
- package/packages/extras/skillsmp-search/SKILL.md +134 -0
- package/packages/frontend-3d/README.md +62 -0
- package/packages/frontend-3d/agents/frontend-perf-reviewer.md +164 -0
- package/packages/frontend-3d/agents/presentation-reviewer.md +146 -0
- package/packages/frontend-3d/agents/r3f-scene-reviewer.md +128 -0
- package/packages/frontend-3d/agents/ui-design-reviewer.md +169 -0
- package/packages/frontend-3d/commands/capture-screen.md +166 -0
- package/packages/frontend-3d/hooks/bundle-size-warn.sh +93 -0
- package/packages/frontend-3d/hooks/gsap-pattern-check.sh +74 -0
- package/packages/frontend-3d/hooks/r3f-color-check.sh +76 -0
- package/packages/frontend-3d/hooks/tailwind-version-guard.sh +98 -0
- package/packages/frontend-3d/rules/frontend-aesthetics-3d.md +81 -0
- package/packages/frontend-3d/rules/gsap-conventions.md +129 -0
- package/packages/frontend-3d/rules/threejs-conventions.md +116 -0
- package/packages/frontend-3d/skills/gltf-debugging/SKILL.md +145 -0
- package/packages/frontend-3d/skills/html-to-3d-texture/SKILL.md +183 -0
- package/packages/frontend-3d/skills/output-enforcement/SKILL.md +79 -0
- package/packages/frontend-3d/skills/product-3d-lighting/SKILL.md +166 -0
- package/packages/frontend-3d/skills/r3f-scroll-driven-3d/SKILL.md +209 -0
- package/packages/frontend-3d/skills/threejs-color-management/SKILL.md +111 -0
- package/packages/frontend-ui/NOTICE.md +99 -0
- package/packages/frontend-ui/README.md +59 -0
- package/packages/frontend-ui/agents/ui-color-reviewer.md +174 -0
- package/packages/frontend-ui/agents/ui-design-critic.md +198 -0
- package/packages/frontend-ui/agents/ui-interaction-reviewer.md +259 -0
- package/packages/frontend-ui/agents/ui-motion-reviewer.md +266 -0
- package/packages/frontend-ui/agents/ui-reviewer.md +251 -0
- package/packages/frontend-ui/agents/ui-typography-reviewer.md +222 -0
- package/packages/frontend-ui/hooks/tests/ui-animation-easing-check_test.sh +78 -0
- package/packages/frontend-ui/hooks/tests/ui-banned-fonts-check_test.sh +77 -0
- package/packages/frontend-ui/hooks/tests/ui-color-check_test.sh +75 -0
- package/packages/frontend-ui/hooks/ui-animation-easing-check.sh +86 -0
- package/packages/frontend-ui/hooks/ui-banned-fonts-check.sh +90 -0
- package/packages/frontend-ui/hooks/ui-color-check.sh +102 -0
- package/packages/frontend-ui/rules/color-and-contrast.md +230 -0
- package/packages/frontend-ui/rules/frontend-design-aesthetics.md +124 -0
- package/packages/frontend-ui/rules/interaction-polish.md +335 -0
- package/packages/frontend-ui/rules/motion-and-animation.md +283 -0
- package/packages/frontend-ui/rules/spatial-and-layout.md +212 -0
- package/packages/frontend-ui/rules/typography-guidelines.md +185 -0
- package/packages/frontend-ui/rules/ui-anti-patterns.md +309 -0
- package/packages/frontend-ui/settings.json +24 -0
- package/packages/frontend-ui/skills/impeccable-craft/SKILL.md +187 -0
- package/packages/gan/README.md +232 -0
- package/packages/gan/agents/gan-evaluator.md +214 -0
- package/packages/gan/agents/gan-generator.md +171 -0
- package/packages/gan/agents/gan-planner.md +206 -0
- package/packages/gan/rubrics/functionality.md +66 -0
- package/packages/gan/rubrics/ui-quality.md +77 -0
- package/packages/gan/skills/gan-evaluator/SKILL.md +91 -0
- package/packages/gan/skills/gan-generator/SKILL.md +72 -0
- package/packages/gan/skills/gan-planner/SKILL.md +93 -0
- package/packages/stack-agents/go/go-concurrency-reviewer.md +210 -0
- package/packages/stack-agents/go/go-error-reviewer.md +187 -0
- package/packages/stack-agents/go/go-modernizer.md +160 -0
- package/packages/stack-agents/go/go-observability-reviewer.md +185 -0
- package/packages/stack-agents/go/go-performance-reviewer.md +160 -0
- package/packages/stack-agents/go/go-reviewer.md +277 -0
- package/packages/stack-agents/go/references/benchmark-methodology.md +280 -0
- package/packages/stack-agents/go/references/channel-patterns.md +384 -0
- package/packages/stack-agents/go/references/cli-cobra-viper.md +190 -0
- package/packages/stack-agents/go/references/code-style.md +262 -0
- package/packages/stack-agents/go/references/context-propagation.md +317 -0
- package/packages/stack-agents/go/references/data-structures.md +315 -0
- package/packages/stack-agents/go/references/database-patterns.md +334 -0
- package/packages/stack-agents/go/references/design-patterns.md +411 -0
- package/packages/stack-agents/go/references/di-frameworks.md +146 -0
- package/packages/stack-agents/go/references/error-creation.md +251 -0
- package/packages/stack-agents/go/references/error-inspection.md +348 -0
- package/packages/stack-agents/go/references/error-wrapping.md +230 -0
- package/packages/stack-agents/go/references/gopls-driving.md +62 -0
- package/packages/stack-agents/go/references/goroutine-lifecycle.md +351 -0
- package/packages/stack-agents/go/references/graphql-patterns.md +221 -0
- package/packages/stack-agents/go/references/grpc-patterns.md +373 -0
- package/packages/stack-agents/go/references/modernize-guide.md +522 -0
- package/packages/stack-agents/go/references/module-management.md +191 -0
- package/packages/stack-agents/go/references/naming-conventions.md +176 -0
- package/packages/stack-agents/go/references/observability-pipeline.md +352 -0
- package/packages/stack-agents/go/references/performance-profiling.md +250 -0
- package/packages/stack-agents/go/references/refactoring-mechanics.md +127 -0
- package/packages/stack-agents/go/references/rest-openapi-patterns.md +160 -0
- package/packages/stack-agents/go/references/samber-do.md +201 -0
- package/packages/stack-agents/go/references/samber-libraries.md +363 -0
- package/packages/stack-agents/go/references/samber-lo.md +260 -0
- package/packages/stack-agents/go/references/samber-oops.md +200 -0
- package/packages/stack-agents/go/references/security-checklist.md +441 -0
- package/packages/stack-agents/go/references/standard-stdlib-now.md +181 -0
- package/packages/stack-agents/go/references/stay-updated.md +147 -0
- package/packages/stack-agents/go/references/structs-interfaces.md +312 -0
- package/packages/stack-agents/go/references/sync-primitives.md +395 -0
- package/packages/stack-agents/go/references/testing-patterns.md +483 -0
- package/packages/stack-agents/go/references/troubleshooting.md +98 -0
- package/packages/stack-agents/python/py-reviewer.md +194 -0
- package/packages/stack-agents/rust/rs-reviewer.md +188 -0
- package/packages/stack-agents/typescript/ts-reviewer.md +135 -0
- package/packages/stack-hooks/go/go-context-check-on-edit.sh +56 -0
- package/packages/stack-hooks/go/go-error-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-format-on-edit.sh +32 -0
- package/packages/stack-hooks/go/go-safety-check-on-edit.sh +54 -0
- package/packages/stack-hooks/go/go-vet-on-edit.sh +61 -0
- package/packages/stack-hooks/go/golangci-lint-on-edit.sh +54 -0
- package/packages/stack-hooks/python/ruff-on-edit.sh +67 -0
- package/packages/stack-hooks/rust/cargo-check-on-edit.sh +64 -0
- package/packages/stack-hooks/typescript/typecheck-on-edit.sh +76 -0
- package/packages/stack-rules/go/go-conventions.md +44 -0
- package/packages/stack-rules/go/go-safety.md +35 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Hook chain execution semantics
|
|
2
|
+
|
|
3
|
+
**Question.** When multiple hooks share a matcher inside `.claude/settings.json`, does Claude Code run them sequentially (in list order, with `exit 2` in one suppressing peers) or in parallel (all three spawn at once, `exit 2` in one does NOT stop the others)?
|
|
4
|
+
|
|
5
|
+
This is load-bearing for every enforcement hook we ship — if we assume sequential but Claude Code runs parallel, hooks like `dev-required-on-commit` can't count on `doc-check-on-commit`'s exit code having already been observed.
|
|
6
|
+
|
|
7
|
+
## 1 · Authoritative answer
|
|
8
|
+
|
|
9
|
+
[Anthropic's hook reference](https://code.claude.com/docs/en/hooks) is explicit:
|
|
10
|
+
|
|
11
|
+
> When multiple hooks match the same event, they all run in parallel.
|
|
12
|
+
|
|
13
|
+
This is reproduced on `docs.anthropic.com/en/docs/claude-code/hooks` (same content, different origin). There is no "sequence" mode and no ordering guarantee beyond "all of them will fire".
|
|
14
|
+
|
|
15
|
+
Practical consequence: `exit 2` from one hook does **not** preempt its peers. Every hook registered under a matcher runs to completion (or times out). The runtime aggregates their verdicts — any single deny wins, but all of them still executed side-effects.
|
|
16
|
+
|
|
17
|
+
## 2 · Local simulation of both modes
|
|
18
|
+
|
|
19
|
+
`run-experiment.sh` in this directory runs the three test hooks `h1` / `h2` / `h3` in both sequential and parallel modes and records a shared log. It isn't real Claude Code, but it demonstrates the mechanics of each mode clearly and verifies the test hooks work.
|
|
20
|
+
|
|
21
|
+
### Sequential mode (what the settings.json list LOOKS like it does)
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
06:52:11.958 h1 start
|
|
25
|
+
06:52:12.327 h1 exit=2
|
|
26
|
+
06:52:12.376 h2 start ← h2 only started after h1 finished
|
|
27
|
+
06:52:12.924 h2 exit=0
|
|
28
|
+
06:52:12.972 h3 start
|
|
29
|
+
06:52:13.740 h3 exit=0
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Total wall-clock: ~1.8s. Each hook's full duration is serialized.
|
|
33
|
+
|
|
34
|
+
### Parallel mode (what Anthropic docs say Claude Code actually does)
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
06:52:13.815 h1 start ← same millisecond
|
|
38
|
+
06:52:13.815 h2 start ← same millisecond
|
|
39
|
+
06:52:13.815 h3 start ← same millisecond
|
|
40
|
+
06:52:14.161 h1 exit=2
|
|
41
|
+
06:52:14.366 h2 exit=0
|
|
42
|
+
06:52:14.583 h3 exit=0 ← h3 ran to completion despite h1 exit=2
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Total wall-clock: ~0.77s (time of the slowest hook). All three start simultaneously; h1's `exit 2` does not suppress h2 or h3; the `h3-ran` marker file exists afterwards.
|
|
46
|
+
|
|
47
|
+
## 3 · Verifying inside real Claude Code
|
|
48
|
+
|
|
49
|
+
The local simulation only checks that hooks work in isolation. To confirm Claude Code's actual scheduler:
|
|
50
|
+
|
|
51
|
+
1. Create a throwaway project directory.
|
|
52
|
+
2. Copy `settings-experiment.json` into it as `.claude/settings.json` (substituting `${CLAUDE_PROJECT_DIR}` with the absolute path to the superkit clone).
|
|
53
|
+
3. Open that directory in Claude Code and run any `Bash` tool call (`ls`, `pwd`, anything).
|
|
54
|
+
4. Inspect:
|
|
55
|
+
- `${TMPDIR:-/tmp}/claude-chain-test.log` — the timestamps show whether hooks started within the same millisecond (parallel) or serially.
|
|
56
|
+
- `${TMPDIR:-/tmp}/claude-chain-test-h3-ran` — presence confirms h3 ran even though h1 exited 2.
|
|
57
|
+
5. Record findings in this file under §5.
|
|
58
|
+
|
|
59
|
+
## 4 · Architectural consequence (applies regardless of runtime version)
|
|
60
|
+
|
|
61
|
+
Because Anthropic can change the runtime at any time and our local simulation can only show "parallel is possible, not mandatory", every hook we ship **MUST** behave correctly under BOTH semantics. The design contract:
|
|
62
|
+
|
|
63
|
+
1. **Hooks are idempotent.** Running the same hook twice with the same payload yields the same decision and the same side-effects. No hook leaves a dangling lock, half-written state file, or incrementable counter that depends on not being called twice.
|
|
64
|
+
2. **Decisions are independent.** A hook's verdict is computed from the payload plus the `${TMPDIR}`-backed state files — NOT from "what an earlier hook in the chain decided". `dev-required-on-commit.sh` reads the counter file that `dev-edit-counter.sh` wrote; it does not assume `doc-check-on-commit.sh` has already decided anything.
|
|
65
|
+
3. **State writes are atomic.** `> file` and `>> file` are both atomic at the shell level. Hooks that need multi-step updates use `mv` after writing to a `.tmp` sibling.
|
|
66
|
+
4. **settings.json order is cosmetic.** We still keep `block-dangerous-git → doc-check-on-commit → dev-required-on-commit → loop-guard` as a list for human readability, but no hook's logic depends on running before or after another hook.
|
|
67
|
+
5. **Timeouts are explicit.** Every hook that does real I/O has `timeout: 60` in settings.json (or the shell-level `timeout 55` inside the script) so a misbehaving peer can't starve the whole chain.
|
|
68
|
+
|
|
69
|
+
## 5 · Empirical findings (to fill in on the next real-runtime test)
|
|
70
|
+
|
|
71
|
+
| Field | Value |
|
|
72
|
+
|-------|-------|
|
|
73
|
+
| Date tested | *(TBD)* |
|
|
74
|
+
| Claude Code version | *(TBD, read from `/help` or `claude --version`)* |
|
|
75
|
+
| OS | *(TBD)* |
|
|
76
|
+
| Concurrency observed | *(parallel / sequential / other)* |
|
|
77
|
+
| h3 marker present after h1 exit 2 | *(yes / no)* |
|
|
78
|
+
| Notes | *(TBD)* |
|
|
79
|
+
|
|
80
|
+
## 6 · References
|
|
81
|
+
|
|
82
|
+
- Anthropic official hooks reference: `code.claude.com/docs/en/hooks`
|
|
83
|
+
- `disler/claude-code-hooks-multi-agent-observability` — similar in-the-wild observations on concurrency
|
|
84
|
+
- The test scaffolding files next to this document: `h1.sh`, `h2.sh`, `h3.sh`, `run-experiment.sh`, `settings-experiment.json`
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# audit-settings-source.sh — CVE-2025-59536 mitigation.
|
|
3
|
+
# Triggers on: SessionStart
|
|
4
|
+
# Profile: standard, strict (skipped on fast for startup speed)
|
|
5
|
+
#
|
|
6
|
+
# A malicious `.claude/settings.json` committed to an untrusted repo can
|
|
7
|
+
# register a PreToolUse hook that runs arbitrary code on every tool call,
|
|
8
|
+
# effectively an RCE on session open. Reference:
|
|
9
|
+
# research.checkpoint.com/2026/rce-and-api-token-exfiltration-through-
|
|
10
|
+
# claude-code-project-files-cve-2025-59536/
|
|
11
|
+
#
|
|
12
|
+
# This hook inspects .claude/settings.json in the current project:
|
|
13
|
+
# - If the file exists AND was last touched by an author who is NOT the
|
|
14
|
+
# current git user OR was modified by ANY author in the last 7 days
|
|
15
|
+
# (likely a recent pull from an untrusted branch), print a loud warning
|
|
16
|
+
# to stderr AND touch ${TMPDIR:-/tmp}/claude-untrusted-settings-${SESSION}
|
|
17
|
+
# so downstream hooks can downgrade decisions.
|
|
18
|
+
# - Fails open (exit 0) in every path — this is a warning, not a block.
|
|
19
|
+
# Opt-out: CLAUDE_DISABLE_SETTINGS_AUDIT=1
|
|
20
|
+
|
|
21
|
+
# Unified profile + disable check
|
|
22
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
23
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
24
|
+
# shellcheck source=/dev/null
|
|
25
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
26
|
+
|
|
27
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
28
|
+
if [ "$PROFILE" = "fast" ] || [ "${CLAUDE_DISABLE_SETTINGS_AUDIT:-}" = "1" ]; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
33
|
+
SETTINGS="$PROJECT_DIR/.claude/settings.json"
|
|
34
|
+
|
|
35
|
+
# Only run inside a git repo with a settings.json to audit
|
|
36
|
+
if [ ! -f "$SETTINGS" ] || ! git -C "$PROJECT_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Identify current git user (trusted author)
|
|
41
|
+
MY_EMAIL=$(git -C "$PROJECT_DIR" config user.email 2>/dev/null)
|
|
42
|
+
if [ -z "$MY_EMAIL" ]; then
|
|
43
|
+
# If we can't identify the user we can't classify foreignness — skip.
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Look at the last 10 commits touching .claude/settings.json
|
|
48
|
+
LAST_COMMITS=$(git -C "$PROJECT_DIR" log --format='%H|%ae|%ct' -n 10 -- .claude/settings.json 2>/dev/null)
|
|
49
|
+
if [ -z "$LAST_COMMITS" ]; then
|
|
50
|
+
# File exists but untracked — treat as trusted (user placed it manually).
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
NOW=$(date +%s)
|
|
55
|
+
SEVEN_DAYS=$((7 * 24 * 3600))
|
|
56
|
+
FOREIGN_RECENT=""
|
|
57
|
+
while IFS='|' read -r sha email ts; do
|
|
58
|
+
[ -z "$sha" ] && continue
|
|
59
|
+
AGE=$((NOW - ts))
|
|
60
|
+
if [ "$email" != "$MY_EMAIL" ] && [ "$AGE" -lt "$SEVEN_DAYS" ]; then
|
|
61
|
+
FOREIGN_RECENT="${FOREIGN_RECENT} - ${sha:0:8} by ${email} ($((AGE / 86400))d ago)\n"
|
|
62
|
+
fi
|
|
63
|
+
done <<< "$LAST_COMMITS"
|
|
64
|
+
|
|
65
|
+
if [ -z "$FOREIGN_RECENT" ]; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Touch marker so downstream hooks can consult it
|
|
70
|
+
INPUT=$(cat 2>/dev/null || echo '')
|
|
71
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
72
|
+
SESSION_KEY="${SESSION_ID:-${CLAUDE_HOOK_PID:-$PPID}}"
|
|
73
|
+
MARKER="${TMPDIR:-/tmp}/claude-untrusted-settings-${SESSION_KEY}"
|
|
74
|
+
touch "$MARKER"
|
|
75
|
+
|
|
76
|
+
cat >&2 <<EOF
|
|
77
|
+
|
|
78
|
+
────────────────────────────────────────────────────────────────────
|
|
79
|
+
⚠ SECURITY WARNING — .claude/settings.json was modified recently by
|
|
80
|
+
someone other than you. This file controls what hooks run on every
|
|
81
|
+
tool call. A malicious settings.json in an untrusted repo can
|
|
82
|
+
execute arbitrary code (CVE-2025-59536).
|
|
83
|
+
|
|
84
|
+
Recent foreign commits touching .claude/settings.json:
|
|
85
|
+
$(echo -e "$FOREIGN_RECENT")
|
|
86
|
+
Actions:
|
|
87
|
+
1. Review the diff: git log -p .claude/settings.json
|
|
88
|
+
2. If anything looks off, open Claude Code with --no-hooks until
|
|
89
|
+
you've vetted the file.
|
|
90
|
+
3. To silence this warning: CLAUDE_DISABLE_SETTINGS_AUDIT=1
|
|
91
|
+
|
|
92
|
+
Reference: https://research.checkpoint.com/2026/
|
|
93
|
+
rce-and-api-token-exfiltration-through-claude-code-project-files-
|
|
94
|
+
cve-2025-59536/
|
|
95
|
+
────────────────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
EOF
|
|
98
|
+
exit 0
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# audit-trail.sh — append-only hash-chained audit log (Task 10).
|
|
3
|
+
# Triggers on: PostToolUse (Bash, Edit, Write, Skill). Profile: standard, strict.
|
|
4
|
+
#
|
|
5
|
+
# Writes one JSON line per tool call to ~/.claude/audit/YYYY-MM-DD.jsonl.
|
|
6
|
+
# Each line contains:
|
|
7
|
+
# ts — ISO 8601 UTC timestamp
|
|
8
|
+
# session_id — Claude Code session_id from payload (falls back to PPID)
|
|
9
|
+
# project — basename of $CLAUDE_PROJECT_DIR
|
|
10
|
+
# event — tool name (Bash|Edit|Write|Skill|…) or semantic tag
|
|
11
|
+
# tool — raw tool name
|
|
12
|
+
# decision — "allow" (exit-0 peer consensus assumed) or "error" on parse fail
|
|
13
|
+
# tag — override tag extracted from a git-commit command, if any
|
|
14
|
+
# files — comma-joined staged/edited file list (short)
|
|
15
|
+
# counter — current /dev edit counter value (reads the state file)
|
|
16
|
+
# marker_exists — whether /dev marker is present (true/false)
|
|
17
|
+
# prev_hash — previous line's hash (genesis = 64 zeros)
|
|
18
|
+
# hash — sha256(prev_hash || canonical_json_of_this_line_without_hash)
|
|
19
|
+
#
|
|
20
|
+
# The log is append-only. `/audit --verify-chain` recomputes the chain
|
|
21
|
+
# and reports the first divergence. `/audit --discipline` aggregates.
|
|
22
|
+
# Rotation: session-context-restore.sh moves logs older than 30 days to
|
|
23
|
+
# ~/.claude/audit/archive/.
|
|
24
|
+
#
|
|
25
|
+
# Opt-out: CLAUDE_DISABLE_AUDIT_TRAIL=1.
|
|
26
|
+
|
|
27
|
+
# Unified profile + disable check
|
|
28
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
29
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
30
|
+
# shellcheck source=/dev/null
|
|
31
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
32
|
+
|
|
33
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
34
|
+
if [ "$PROFILE" = "fast" ] || [ "${CLAUDE_DISABLE_AUDIT_TRAIL:-}" = "1" ]; then
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
39
|
+
|
|
40
|
+
# ── Parse payload ────────────────────────────────────────────────────────
|
|
41
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "Unknown"' 2>/dev/null)
|
|
42
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
43
|
+
SESSION_KEY=$(command -v superkit_session_key >/dev/null 2>&1 && superkit_session_key || printf '%s' "${SESSION_ID:-${CLAUDE_HOOK_PID:-$PPID}}")
|
|
44
|
+
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "PostToolUse"' 2>/dev/null)
|
|
45
|
+
|
|
46
|
+
# Tool-specific snippet
|
|
47
|
+
FIELD=""
|
|
48
|
+
case "$TOOL" in
|
|
49
|
+
Bash)
|
|
50
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // .command // empty' 2>/dev/null)
|
|
51
|
+
# Keep first 120 chars for the log
|
|
52
|
+
FIELD=$(printf '%s' "$CMD" | head -c 120 | tr '\n' ' ')
|
|
53
|
+
;;
|
|
54
|
+
Edit|Write|MultiEdit)
|
|
55
|
+
FIELD=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
|
|
56
|
+
;;
|
|
57
|
+
Skill)
|
|
58
|
+
FIELD=$(echo "$INPUT" | jq -r '.tool_input.skill // empty' 2>/dev/null)
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
|
|
62
|
+
# Override tag if the command is a git commit
|
|
63
|
+
TAG=""
|
|
64
|
+
if [ "$TOOL" = "Bash" ]; then
|
|
65
|
+
TAG=$(printf '%s' "$CMD" | grep -oiE '\[(quick|no-dev|trivial|hotfix|wip)(:[^]]*)?\]' | head -1)
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Counter + marker state
|
|
69
|
+
COUNTER_FILE="${TMPDIR:-/tmp}/claude-edit-count-${SESSION_KEY}"
|
|
70
|
+
MARKER_FILE="${TMPDIR:-/tmp}/claude-dev-marker-${SESSION_KEY}"
|
|
71
|
+
COUNTER=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
72
|
+
[ -f "$MARKER_FILE" ] && MARKER="true" || MARKER="false"
|
|
73
|
+
|
|
74
|
+
PROJECT=$(basename "${CLAUDE_PROJECT_DIR:-$(pwd)}")
|
|
75
|
+
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
76
|
+
|
|
77
|
+
# ── Prepare log directory + chain state ──────────────────────────────────
|
|
78
|
+
AUDIT_DIR="$HOME/.claude/audit"
|
|
79
|
+
LAST_HASH_FILE="$AUDIT_DIR/.last-hash-${SESSION_KEY}"
|
|
80
|
+
mkdir -p "$AUDIT_DIR" 2>/dev/null
|
|
81
|
+
|
|
82
|
+
PREV_HASH=$(cat "$LAST_HASH_FILE" 2>/dev/null)
|
|
83
|
+
if [ -z "$PREV_HASH" ] || [ ${#PREV_HASH} -ne 64 ]; then
|
|
84
|
+
PREV_HASH=$(printf '%064d' 0) # genesis = 64 zeros
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# ── Emit canonical JSON (without hash) and compute hash over it ──────────
|
|
88
|
+
# `jq -c` produces deterministic compact output given ordered keys.
|
|
89
|
+
PAYLOAD=$(jq -cn \
|
|
90
|
+
--arg ts "$TS" \
|
|
91
|
+
--arg sid "$SESSION_KEY" \
|
|
92
|
+
--arg project "$PROJECT" \
|
|
93
|
+
--arg event "$EVENT" \
|
|
94
|
+
--arg tool "$TOOL" \
|
|
95
|
+
--arg field "$FIELD" \
|
|
96
|
+
--arg tag "$TAG" \
|
|
97
|
+
--arg counter "$COUNTER" \
|
|
98
|
+
--arg marker "$MARKER" \
|
|
99
|
+
--arg prev "$PREV_HASH" \
|
|
100
|
+
'{ts:$ts,session_id:$sid,project:$project,event:$event,tool:$tool,field:$field,tag:$tag,counter:($counter|tonumber),marker_exists:($marker=="true"),prev_hash:$prev}' \
|
|
101
|
+
2>/dev/null)
|
|
102
|
+
|
|
103
|
+
if [ -z "$PAYLOAD" ]; then
|
|
104
|
+
exit 0 # jq failure — skip logging rather than corrupt the chain
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
HASH=$(printf '%s' "$PAYLOAD" | shasum -a 256 | cut -d' ' -f1)
|
|
108
|
+
|
|
109
|
+
# ── Append line + update last-hash atomically ────────────────────────────
|
|
110
|
+
LINE=$(printf '%s' "$PAYLOAD" | jq -c --arg h "$HASH" '. + {hash:$h}')
|
|
111
|
+
LOG_FILE="$AUDIT_DIR/$(date -u +%Y-%m-%d).jsonl"
|
|
112
|
+
|
|
113
|
+
# Append is atomic for < PIPE_BUF-byte writes; our lines are always small.
|
|
114
|
+
printf '%s\n' "$LINE" >> "$LOG_FILE"
|
|
115
|
+
|
|
116
|
+
# Update last-hash via mv-rename for atomicity
|
|
117
|
+
TMP_HASH="$AUDIT_DIR/.last-hash-${SESSION_KEY}.$$"
|
|
118
|
+
printf '%s' "$HASH" > "$TMP_HASH" && mv -f "$TMP_HASH" "$LAST_HASH_FILE"
|
|
119
|
+
|
|
120
|
+
exit 0
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# block-dangerous-git.sh — PreToolUse hook for Bash
|
|
3
|
+
# Blocks dangerous git commands: --no-verify, --force, reset --hard, branch -D
|
|
4
|
+
|
|
5
|
+
# Unified profile + disable check
|
|
6
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
7
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
8
|
+
# shellcheck source=/dev/null
|
|
9
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
10
|
+
|
|
11
|
+
INPUT=$(cat)
|
|
12
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
13
|
+
|
|
14
|
+
if [ -z "$COMMAND" ]; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# ── Strip commit-MESSAGE payloads before scanning ────────────────────────────
|
|
19
|
+
# A commit message that merely *describes* --no-verify / a force push must not
|
|
20
|
+
# trip the guards below (this fired twice in prod while writing commit messages
|
|
21
|
+
# that document these very rules). We remove the -m / --message arguments and
|
|
22
|
+
# the body of a `git commit -F -` heredoc, then run every existing regex on the
|
|
23
|
+
# REMAINDER unchanged — so chained segments (&&, ;, |) OUTSIDE the message are
|
|
24
|
+
# still scanned: `git commit -m "x" && git push --force` must still block.
|
|
25
|
+
# Safety bias: when a strip is uncertain we remove too LITTLE (a false block is
|
|
26
|
+
# annoying but safe) rather than too much (which could hide a real command).
|
|
27
|
+
SCAN_COMMAND="$COMMAND"
|
|
28
|
+
|
|
29
|
+
# (a/b) Strip -m / --message arguments. Quoted forms are removed FIRST, so a
|
|
30
|
+
# spurious << or && that lives INSIDE a message is gone before it can
|
|
31
|
+
# matter; unquoted single-token forms are removed last (they stop at the
|
|
32
|
+
# first whitespace or shell metacharacter ; & | < > ( ) so a glued
|
|
33
|
+
# `-m foo;git push --force` keeps the `;git push` for scanning). The
|
|
34
|
+
# attached unquoted `-mfoo` form is left alone deliberately (rare; erring
|
|
35
|
+
# toward strip-less — worst case is a harmless false block).
|
|
36
|
+
SCAN_COMMAND=$(printf '%s' "$SCAN_COMMAND" | sed -E \
|
|
37
|
+
-e 's/--message="[^"]*"//g' \
|
|
38
|
+
-e "s/--message='[^']*'//g" \
|
|
39
|
+
-e 's/--message[[:space:]]+"[^"]*"//g' \
|
|
40
|
+
-e "s/--message[[:space:]]+'[^']*'//g" \
|
|
41
|
+
-e 's/(^|[[:space:]])-m"[^"]*"/\1/g' \
|
|
42
|
+
-e "s/(^|[[:space:]])-m'[^']*'/\1/g" \
|
|
43
|
+
-e 's/(^|[[:space:]])-m[[:space:]]+"[^"]*"/\1/g' \
|
|
44
|
+
-e "s/(^|[[:space:]])-m[[:space:]]+'[^']*'/\1/g" \
|
|
45
|
+
-e 's/--message=[^[:space:];&|<>()]*//g' \
|
|
46
|
+
-e 's/--message[[:space:]]+[^[:space:];&|<>()]*//g' \
|
|
47
|
+
-e 's/(^|[[:space:]])-m[[:space:]]+[^[:space:];&|<>()]*/\1/g')
|
|
48
|
+
|
|
49
|
+
# (c) Strip a `git commit ... <<MARKER ... MARKER` heredoc BODY (message fed via
|
|
50
|
+
# -F -). Runs AFTER the -m strip so that a << appearing only inside an -m
|
|
51
|
+
# message has already been removed and cannot trigger a false heredoc. Only
|
|
52
|
+
# the body lines are dropped — the opener line and anything after the
|
|
53
|
+
# terminator are kept and scanned, so a chained dangerous command survives.
|
|
54
|
+
SCAN_COMMAND=$(printf '%s' "$SCAN_COMMAND" | awk '
|
|
55
|
+
BEGIN { in_h = 0; marker = ""; dash = 0 }
|
|
56
|
+
function get_marker(line, rest) {
|
|
57
|
+
if (match(line, /<<-?/)) {
|
|
58
|
+
rest = substr(line, RSTART + RLENGTH)
|
|
59
|
+
if (match(rest, /[A-Za-z_][A-Za-z0-9_]*/))
|
|
60
|
+
return substr(rest, RSTART, RLENGTH)
|
|
61
|
+
}
|
|
62
|
+
return ""
|
|
63
|
+
}
|
|
64
|
+
{
|
|
65
|
+
if (in_h) {
|
|
66
|
+
t = $0
|
|
67
|
+
if (dash) sub(/^\t+/, "", t)
|
|
68
|
+
if (t == marker) { in_h = 0; print; next }
|
|
69
|
+
next
|
|
70
|
+
}
|
|
71
|
+
if ($0 ~ /git[[:space:]]+commit/) {
|
|
72
|
+
m = get_marker($0)
|
|
73
|
+
if (m != "") { in_h = 1; marker = m; dash = ($0 ~ /<<-/); print; next }
|
|
74
|
+
}
|
|
75
|
+
print
|
|
76
|
+
}')
|
|
77
|
+
|
|
78
|
+
if echo "$SCAN_COMMAND" | grep -qE '\-\-no-verify'; then
|
|
79
|
+
cat >&2 <<'EOF'
|
|
80
|
+
BLOCKED: --no-verify is not allowed.
|
|
81
|
+
Why: pre-commit hooks exist to catch regressions before they land.
|
|
82
|
+
Suggested alternative:
|
|
83
|
+
1) Run the failing hook manually, read its output, fix the issue.
|
|
84
|
+
2) If the hook itself is wrong, amend the hook config with a
|
|
85
|
+
documented reason rather than silencing it at every commit.
|
|
86
|
+
EOF
|
|
87
|
+
exit 2
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if { echo "$SCAN_COMMAND" | grep -qE 'git[[:space:]]+push' \
|
|
91
|
+
&& echo "$SCAN_COMMAND" | grep -qE '(^|[[:space:]])(--force([[:space:]=]|$)|-f([[:space:]]|$))'; } \
|
|
92
|
+
|| echo "$SCAN_COMMAND" | grep -qE 'git[[:space:]]+push[[:space:]]+[^[:space:]]+[[:space:]]+\+'; then
|
|
93
|
+
cat >&2 <<'EOF'
|
|
94
|
+
BLOCKED: Force push is not allowed.
|
|
95
|
+
Why: overwrites remote history; collaborators' clones break silently.
|
|
96
|
+
+refspec pushes (git push origin +main) force non-fast-forward too.
|
|
97
|
+
Suggested alternative:
|
|
98
|
+
git push --force-with-lease # safe — refuses if remote moved
|
|
99
|
+
git push --force-with-lease=<branch> # even safer — scopes to your ref
|
|
100
|
+
EOF
|
|
101
|
+
exit 2
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git\s+reset\s+--hard'; then
|
|
105
|
+
cat >&2 <<'EOF'
|
|
106
|
+
BLOCKED: git reset --hard can destroy work.
|
|
107
|
+
Why: drops uncommitted changes AND moves HEAD — double hit.
|
|
108
|
+
Suggested alternative:
|
|
109
|
+
git stash push -u -m 'before-reset' # preserve WIP first
|
|
110
|
+
git reset --soft <ref> # keep staged + WC
|
|
111
|
+
git reset --mixed <ref> # keep WC only
|
|
112
|
+
git restore --source=<ref> <path> # narrow, per-file restore
|
|
113
|
+
EOF
|
|
114
|
+
exit 2
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git\s+branch\s+-D'; then
|
|
118
|
+
cat >&2 <<'EOF'
|
|
119
|
+
BLOCKED: git branch -D force-deletes.
|
|
120
|
+
Why: drops a branch even if its commits are unmerged / unpushed.
|
|
121
|
+
Suggested alternative:
|
|
122
|
+
git branch -d <name> # safe — refuses if unmerged
|
|
123
|
+
git push origin --delete <name> # delete remote counterpart
|
|
124
|
+
git reflog show <name> # recover if you just -D'd one
|
|
125
|
+
EOF
|
|
126
|
+
exit 2
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git[[:space:]]+checkout[[:space:]]+(\.([[:space:]]|$)|--([[:space:]]|$)|-f([[:space:]]|$)|--force([[:space:]]|$))'; then
|
|
130
|
+
cat >&2 <<'EOF'
|
|
131
|
+
BLOCKED: git checkout discards uncommitted changes.
|
|
132
|
+
Why: checkout of '.', '--', '-f', or '--force' overwrites working-tree
|
|
133
|
+
edits with no undo — the uncommitted work is gone for good.
|
|
134
|
+
Suggested alternative:
|
|
135
|
+
git stash push -u -m before-discard # preserve WIP first
|
|
136
|
+
git restore --source=<ref> <path> # narrow, per-file restore
|
|
137
|
+
EOF
|
|
138
|
+
exit 2
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git[[:space:]]+clean[[:space:]]+-[A-Za-z]*f|git[[:space:]]+clean[[:space:]]+.*--force'; then
|
|
142
|
+
cat >&2 <<'EOF'
|
|
143
|
+
BLOCKED: git clean -f permanently deletes untracked files.
|
|
144
|
+
Why: removes untracked files/dirs outright — git cannot recover them.
|
|
145
|
+
Suggested alternative:
|
|
146
|
+
git clean -n # dry run — list what WOULD be removed
|
|
147
|
+
git stash push -u # stash untracked instead of deleting
|
|
148
|
+
EOF
|
|
149
|
+
exit 2
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git[[:space:]]+switch[[:space:]]+.*(--discard-changes|--force([[:space:]]|$)|-[A-Za-z]*f([[:space:]]|$)|-C([[:space:]]|$))'; then
|
|
153
|
+
cat >&2 <<'EOF'
|
|
154
|
+
BLOCKED: git switch --discard-changes / -C throws away work.
|
|
155
|
+
Why: forces the switch and drops uncommitted changes with no undo.
|
|
156
|
+
Suggested alternative:
|
|
157
|
+
git stash push -u -m before-switch # preserve WIP first
|
|
158
|
+
git switch <branch> # plain switch keeps your changes
|
|
159
|
+
EOF
|
|
160
|
+
exit 2
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# ── Task 18: stash-commit bypass patterns ─────────────────────────────────
|
|
164
|
+
# Real incident from github.com/anthropics/claude-code/issues/40117 — a
|
|
165
|
+
# stash+commit+stash-pop sequence can slip staged changes past hook
|
|
166
|
+
# scrutiny. Also catch -q/--quiet combined with --no-verify and
|
|
167
|
+
# --amend --no-verify which target pre-commit gate bypass.
|
|
168
|
+
|
|
169
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git\s+stash.*&&.*git\s+commit.*stash\s+pop'; then
|
|
170
|
+
echo "BLOCKED: stash → commit → stash pop bypass pattern detected." >&2
|
|
171
|
+
echo " This pattern is a known way to slip staged work past commit-gate hooks." >&2
|
|
172
|
+
echo " Commit the staged work directly, then stash/pop the remainder." >&2
|
|
173
|
+
exit 2
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git\s+commit.*(-q|--quiet).*--no-verify'; then
|
|
177
|
+
echo "BLOCKED: git commit -q/--quiet combined with --no-verify." >&2
|
|
178
|
+
echo " This combination exists solely to silence the hook failure. Fix the" >&2
|
|
179
|
+
echo " underlying pre-commit-hook error instead of muting it." >&2
|
|
180
|
+
exit 2
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if echo "$SCAN_COMMAND" | grep -qE 'git\s+commit.*--amend.*--no-verify'; then
|
|
184
|
+
echo "BLOCKED: git commit --amend --no-verify is a pre-commit-hook bypass." >&2
|
|
185
|
+
echo " Fix the hook error, stage the fix, then --amend without --no-verify." >&2
|
|
186
|
+
exit 2
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
exit 0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook: warn when new imports reference packages not in package.json
|
|
3
|
+
# Profile: skip on fast, run on standard/strict
|
|
4
|
+
|
|
5
|
+
# Unified profile + disable check
|
|
6
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
7
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
8
|
+
# shellcheck source=/dev/null
|
|
9
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
10
|
+
|
|
11
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
12
|
+
if [ "$PROFILE" = "fast" ]; then exit 0; fi
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
16
|
+
|
|
17
|
+
# Only process TypeScript files
|
|
18
|
+
if [ -z "$FILE_PATH" ] || [[ ! "$FILE_PATH" =~ \.(ts|tsx)$ ]]; then exit 0; fi
|
|
19
|
+
|
|
20
|
+
# Get new content from edit or write
|
|
21
|
+
NEW_STRING=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty')
|
|
22
|
+
if [ -z "$NEW_STRING" ]; then exit 0; fi
|
|
23
|
+
|
|
24
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
25
|
+
|
|
26
|
+
# Extract package imports (skip relative imports starting with . or /)
|
|
27
|
+
NEW_IMPORTS=$(echo "$NEW_STRING" | grep -oE "from ['\"]([^./][^'\"]*)['\"]" | sed "s/from ['\"]//;s/['\"]$//" | sort -u)
|
|
28
|
+
if [ -z "$NEW_IMPORTS" ]; then exit 0; fi
|
|
29
|
+
|
|
30
|
+
# Find nearest package.json by walking up from file path
|
|
31
|
+
PKG_JSON=""
|
|
32
|
+
SEARCH_DIR=$(dirname "$FILE_PATH")
|
|
33
|
+
while [ "$SEARCH_DIR" != "/" ] && [ "$SEARCH_DIR" != "." ]; do
|
|
34
|
+
if [ -f "$SEARCH_DIR/package.json" ]; then
|
|
35
|
+
PKG_JSON="$SEARCH_DIR/package.json"
|
|
36
|
+
break
|
|
37
|
+
fi
|
|
38
|
+
SEARCH_DIR=$(dirname "$SEARCH_DIR")
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
if [ -z "$PKG_JSON" ] || [ ! -f "$PKG_JSON" ]; then exit 0; fi
|
|
42
|
+
|
|
43
|
+
WARNINGS=""
|
|
44
|
+
for IMPORT in $NEW_IMPORTS; do
|
|
45
|
+
# Extract base package name (handle scoped packages @org/pkg)
|
|
46
|
+
BASE_PKG=$(echo "$IMPORT" | sed 's|@\([^/]*\)/\([^/]*\).*|@\1/\2|; t; s|/.*||')
|
|
47
|
+
|
|
48
|
+
# Skip known built-in/virtual modules
|
|
49
|
+
case "$BASE_PKG" in
|
|
50
|
+
react|react-dom|react/jsx-runtime|vite/*) continue ;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
if ! grep -q "\"$BASE_PKG\"" "$PKG_JSON" 2>/dev/null; then
|
|
54
|
+
WARNINGS="${WARNINGS} - ${BASE_PKG}\n"
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
if [ -n "$WARNINGS" ]; then
|
|
59
|
+
echo "WARNING: New dependencies not in package.json:" >&2
|
|
60
|
+
echo -e "$WARNINGS" >&2
|
|
61
|
+
echo "Consider: are these intentional? Check bundle size impact." >&2
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
exit 0
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# compact-state-inject.sh — survive context compaction with disciplinary state
|
|
3
|
+
# Triggers on: SessionStart (matcher "compact")
|
|
4
|
+
# Profile: standard, strict
|
|
5
|
+
#
|
|
6
|
+
# After Claude Code compacts the conversation, the counter / marker /
|
|
7
|
+
# override-cycle state we kept in session memory is wiped. We persist
|
|
8
|
+
# the disciplinary snapshot via the audit log, then on SessionStart
|
|
9
|
+
# (compact) we read back the last ~30 min of entries and inject a
|
|
10
|
+
# ≤200 char summary into the fresh post-compact context so Claude
|
|
11
|
+
# "remembers" its budget standing.
|
|
12
|
+
#
|
|
13
|
+
# Output format (Claude Code SessionStart additionalContext):
|
|
14
|
+
# {"hookSpecificOutput":{"hookEventName":"SessionStart",
|
|
15
|
+
# "additionalContext":"Session stats: …"}}
|
|
16
|
+
#
|
|
17
|
+
# Opt-out: CLAUDE_DISABLE_COMPACT_STATE=1
|
|
18
|
+
|
|
19
|
+
# Unified profile + disable check
|
|
20
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
21
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
22
|
+
# shellcheck source=/dev/null
|
|
23
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
24
|
+
|
|
25
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
26
|
+
if [ "$PROFILE" = "fast" ] || [ "${CLAUDE_DISABLE_COMPACT_STATE:-}" = "1" ]; then
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
31
|
+
SOURCE=$(echo "$INPUT" | jq -r '.source // .matcher // empty' 2>/dev/null)
|
|
32
|
+
# Only fire after compaction — skip on fresh / resume sources
|
|
33
|
+
if [ "$SOURCE" != "compact" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
38
|
+
SESSION_KEY="${SESSION_ID:-${CLAUDE_HOOK_PID:-$PPID}}"
|
|
39
|
+
|
|
40
|
+
AUDIT_DIR="$HOME/.claude/audit"
|
|
41
|
+
[ -d "$AUDIT_DIR" ] || exit 0
|
|
42
|
+
|
|
43
|
+
# Count tagged overrides + /dev invocations in the last 60 minutes
|
|
44
|
+
# (across all audit files — today + maybe yesterday at a day boundary).
|
|
45
|
+
# Window widened for Opus 4.8 1M context: sessions last longer, so 60 min
|
|
46
|
+
# keeps the post-compaction summary representative of the active stretch.
|
|
47
|
+
cutoff=$(date -u -v-60M +%s 2>/dev/null || date -u -d '60 minutes ago' +%s 2>/dev/null || echo 0)
|
|
48
|
+
|
|
49
|
+
overrides=0
|
|
50
|
+
dev_invocations=0
|
|
51
|
+
for f in "$AUDIT_DIR"/*.jsonl; do
|
|
52
|
+
[ -f "$f" ] || continue
|
|
53
|
+
while IFS= read -r line; do
|
|
54
|
+
sid=$(printf '%s' "$line" | jq -r '.session_id // empty' 2>/dev/null)
|
|
55
|
+
[ "$sid" != "$SESSION_KEY" ] && continue
|
|
56
|
+
ts=$(printf '%s' "$line" | jq -r '.ts // empty' 2>/dev/null)
|
|
57
|
+
[ -z "$ts" ] && continue
|
|
58
|
+
ts_epoch=$(date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$ts" +%s 2>/dev/null || \
|
|
59
|
+
date -u -d "$ts" +%s 2>/dev/null || echo 0)
|
|
60
|
+
[ "$ts_epoch" -lt "$cutoff" ] && continue
|
|
61
|
+
tag=$(printf '%s' "$line" | jq -r '.tag // empty' 2>/dev/null)
|
|
62
|
+
[ -n "$tag" ] && overrides=$((overrides + 1))
|
|
63
|
+
marker=$(printf '%s' "$line" | jq -r '.marker_exists // false' 2>/dev/null)
|
|
64
|
+
[ "$marker" = "true" ] && dev_invocations=$((dev_invocations + 1))
|
|
65
|
+
done < "$f"
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
# If nothing notable to inject, exit without emitting anything.
|
|
69
|
+
if [ "$overrides" -eq 0 ] && [ "$dev_invocations" -eq 0 ]; then
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Build the summary. Keep under ~200 chars so it doesn't eat budget.
|
|
74
|
+
if [ "$overrides" -ge 2 ]; then
|
|
75
|
+
msg="Disciplinary state after compaction: ${overrides} override tag(s) used in the last 60 min. The next override is rejected by the rolling-budget gate — please use /dev for the next code-changing commit."
|
|
76
|
+
elif [ "$overrides" -eq 1 ]; then
|
|
77
|
+
msg="Disciplinary state after compaction: 1 override tag used in the last 60 min. One more within 60 min will raise strictness (threshold 3 → 2) and the one after that is blocked."
|
|
78
|
+
else
|
|
79
|
+
msg="Disciplinary state after compaction: no overrides in the last 60 min, ${dev_invocations} /dev invocation(s) recorded."
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Emit the SessionStart additionalContext JSON
|
|
83
|
+
jq -cn --arg m "$msg" '{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:$m}}'
|
|
84
|
+
exit 0
|