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,367 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# superkit-update.sh — auto-update superkit files at session start
|
|
3
|
+
# Triggers on: SessionStart
|
|
4
|
+
# Profile: all (fast, standard, strict)
|
|
5
|
+
#
|
|
6
|
+
# Reads .claude/.superkit-meta (created by setup.sh) to find the superkit clone
|
|
7
|
+
# path. Compares install version (meta) vs source version (clone's VERSION file),
|
|
8
|
+
# pulls remote if behind, and re-syncs updated files when install lags source.
|
|
9
|
+
#
|
|
10
|
+
# CONTRACT (rewritten 2026-07-10 — see the D1 defect brief):
|
|
11
|
+
# 1. SELF-BOOTSTRAP first. The consumer runs its OWN copy of this script, so the
|
|
12
|
+
# sync that ships a fix would otherwise be performed by the old, blind copy.
|
|
13
|
+
# Before anything else, if the source clone's copy of this script differs from
|
|
14
|
+
# the installed copy, replace the installed copy and re-exec it (guarded by an
|
|
15
|
+
# env sentinel against re-exec loops).
|
|
16
|
+
# 2. NEVER copy a repo-only internal file (derived from packages/core/INTERNAL-FILES).
|
|
17
|
+
# 3. PRISTINE-PRESERVE. A consumer file is only overwritten when its CONTENT equals
|
|
18
|
+
# the same file's blob at the install-baseline tag (v$INSTALL_VERSION) OR at any
|
|
19
|
+
# of the last ~5 released tags. No tag matches → treat as locally customized →
|
|
20
|
+
# preserve it and report it. Any uncertainty (missing tag, git error, no temp)
|
|
21
|
+
# → PRESERVE (fail-safe). Content-only compare (cmp), never mtime, and NEVER
|
|
22
|
+
# against the clone's HEAD/worktree blob (that is the *new* content, useless as
|
|
23
|
+
# a pristine test).
|
|
24
|
+
# 4. A file absent in the consumer is created (that is not a clobber). Hooks stay +x.
|
|
25
|
+
# 5. Coverage: core agents/commands/hooks(.sh,.py)/rules/skills, hooks/lib/*.sh,
|
|
26
|
+
# stack-agents (+ references), stack-hooks, stack-rules, and helpers/statusline.cjs
|
|
27
|
+
# — all under the same pristine rule (a forked statusline is preserved).
|
|
28
|
+
# 6. Report one compact line at SessionStart, plus the preserved paths when any.
|
|
29
|
+
#
|
|
30
|
+
# Two independent triggers for sync:
|
|
31
|
+
# 1. Install-version lags source-version (someone pulled the clone but did not
|
|
32
|
+
# sync this repo). Cheap local check, no network required.
|
|
33
|
+
# 2. Source clone is behind remote — pull, then re-check version.
|
|
34
|
+
|
|
35
|
+
# ── Read meta file (entry condition) ─────────────────────────────────
|
|
36
|
+
META_FILE="$CLAUDE_PROJECT_DIR/.claude/.superkit-meta"
|
|
37
|
+
if [ ! -f "$META_FILE" ]; then
|
|
38
|
+
exit 0 # superkit source not tracked, skip
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
source "$META_FILE"
|
|
42
|
+
SUPERKIT_STACKS="${SUPERKIT_STACKS:-}"
|
|
43
|
+
|
|
44
|
+
# Verify source exists (safety exits)
|
|
45
|
+
if [ ! -d "$SUPERKIT_SOURCE" ]; then
|
|
46
|
+
exit 0 # clone directory missing, skip silently
|
|
47
|
+
fi
|
|
48
|
+
if [ ! -f "$SUPERKIT_SOURCE/VERSION" ]; then
|
|
49
|
+
exit 0 # not a valid superkit clone
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# ── (1) SELF-BOOTSTRAP ───────────────────────────────────────────────
|
|
53
|
+
# Replace the installed copy of THIS script with the source's copy before doing
|
|
54
|
+
# anything destructive, then re-exec the fresh copy. The env sentinel breaks any
|
|
55
|
+
# re-exec loop. Guarded so a missing source/local copy, or an identical pair,
|
|
56
|
+
# is a no-op. This is the ONE file we always overwrite from source — it is
|
|
57
|
+
# self-managed infrastructure, so it is deliberately excluded from the pristine
|
|
58
|
+
# sync loop below (a consumer never "customizes" the updater).
|
|
59
|
+
SELF_SRC="$SUPERKIT_SOURCE/packages/core/hooks/superkit-update.sh"
|
|
60
|
+
SELF_LOCAL="$CLAUDE_PROJECT_DIR/.claude/scripts/hooks/superkit-update.sh"
|
|
61
|
+
if [ "${SUPERKIT_UPDATE_BOOTSTRAPPED:-}" != "1" ] \
|
|
62
|
+
&& [ -f "$SELF_SRC" ] && [ -f "$SELF_LOCAL" ] \
|
|
63
|
+
&& ! cmp -s "$SELF_SRC" "$SELF_LOCAL"; then
|
|
64
|
+
if cp "$SELF_SRC" "$SELF_LOCAL" 2>/dev/null; then
|
|
65
|
+
chmod +x "$SELF_LOCAL" 2>/dev/null
|
|
66
|
+
export SUPERKIT_UPDATE_BOOTSTRAPPED=1
|
|
67
|
+
exec bash "$SELF_LOCAL" "$@"
|
|
68
|
+
fi
|
|
69
|
+
# If the copy failed we fall through and run the current (old) logic once.
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ── Cheap version check FIRST (no network) ───────────────────────────
|
|
73
|
+
# Detects the case where someone manually pulled the clone (or another project
|
|
74
|
+
# synced first) — install in this repo lags the source clone.
|
|
75
|
+
SOURCE_VERSION=$(cat "$SUPERKIT_SOURCE/VERSION" | tr -d '[:space:]')
|
|
76
|
+
INSTALL_VERSION="$SUPERKIT_VERSION"
|
|
77
|
+
|
|
78
|
+
NEED_SYNC=false
|
|
79
|
+
if [ "$INSTALL_VERSION" != "$SOURCE_VERSION" ]; then
|
|
80
|
+
NEED_SYNC=true
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Rate limit: check remote at most once per 6 hours. Version-mismatch bypasses
|
|
84
|
+
# the rate limit (we already know we need to sync).
|
|
85
|
+
LAST_UPDATE_FILE="$HOME/.claude/.superkit-update-last-check"
|
|
86
|
+
NOW=$(date +%s)
|
|
87
|
+
LAST=$(cat "$LAST_UPDATE_FILE" 2>/dev/null || echo "0")
|
|
88
|
+
DIFF=$((NOW - LAST))
|
|
89
|
+
|
|
90
|
+
SKIP_REMOTE_CHECK=false
|
|
91
|
+
if [ "$DIFF" -lt 21600 ]; then
|
|
92
|
+
SKIP_REMOTE_CHECK=true
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Fully up to date: install matches source AND we checked remote recently
|
|
96
|
+
if [ "$NEED_SYNC" = "false" ] && [ "$SKIP_REMOTE_CHECK" = "true" ]; then
|
|
97
|
+
exit 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# ── Network check ────────────────────────────────────────────────────
|
|
101
|
+
if [ "$SKIP_REMOTE_CHECK" = "false" ]; then
|
|
102
|
+
echo "$NOW" > "$LAST_UPDATE_FILE" 2>/dev/null
|
|
103
|
+
|
|
104
|
+
cd "$SUPERKIT_SOURCE" 2>/dev/null && git fetch --quiet 2>/dev/null
|
|
105
|
+
LOCAL=$(cd "$SUPERKIT_SOURCE" && git rev-parse HEAD 2>/dev/null)
|
|
106
|
+
REMOTE=$(cd "$SUPERKIT_SOURCE" && git rev-parse @{u} 2>/dev/null)
|
|
107
|
+
|
|
108
|
+
if [ -n "$LOCAL" ] && [ -n "$REMOTE" ] && [ "$LOCAL" != "$REMOTE" ]; then
|
|
109
|
+
cd "$SUPERKIT_SOURCE" && git pull --quiet 2>/dev/null || {
|
|
110
|
+
echo ""
|
|
111
|
+
echo "⚠ superkit: git pull failed in $SUPERKIT_SOURCE"
|
|
112
|
+
echo " Run manually: cd $SUPERKIT_SOURCE && git pull"
|
|
113
|
+
echo ""
|
|
114
|
+
# Continue — install may still lag source even without pull
|
|
115
|
+
}
|
|
116
|
+
# Refresh source version after pull attempt
|
|
117
|
+
SOURCE_VERSION=$(cat "$SUPERKIT_SOURCE/VERSION" | tr -d '[:space:]')
|
|
118
|
+
if [ "$INSTALL_VERSION" != "$SOURCE_VERSION" ]; then
|
|
119
|
+
NEED_SYNC=true
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# Nothing to sync after remote check either
|
|
125
|
+
if [ "$NEED_SYNC" = "false" ]; then
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# ── Sync setup ───────────────────────────────────────────────────────
|
|
130
|
+
NEW_VERSION="$SOURCE_VERSION"
|
|
131
|
+
OLD_VERSION="$INSTALL_VERSION"
|
|
132
|
+
PACKAGES="$SUPERKIT_SOURCE/packages"
|
|
133
|
+
CLAUDE_DIR="$CLAUDE_PROJECT_DIR/.claude"
|
|
134
|
+
|
|
135
|
+
# ── Internal-file ship-list (derived from the manifest) ──────────────
|
|
136
|
+
# packages/core/INTERNAL-FILES is the single source of truth for repo-only files
|
|
137
|
+
# that must NEVER be installed into a consumer's .claude/. We DERIVE the per-
|
|
138
|
+
# category basename lists from it so this hook, lib/installer.js, and
|
|
139
|
+
# superkit-counts-verify.sh never drift.
|
|
140
|
+
MANIFEST_FILE="$SUPERKIT_SOURCE/packages/core/INTERNAL-FILES"
|
|
141
|
+
manifest_category() {
|
|
142
|
+
local category="$1" line dir base
|
|
143
|
+
[ -f "$MANIFEST_FILE" ] || return 0
|
|
144
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
145
|
+
line="${line#"${line%%[![:space:]]*}"}" # ltrim
|
|
146
|
+
line="${line%"${line##*[![:space:]]}"}" # rtrim
|
|
147
|
+
[ -z "$line" ] && continue
|
|
148
|
+
case "$line" in \#*) continue ;; esac
|
|
149
|
+
dir="${line%/*}"; base="${line##*/}"
|
|
150
|
+
[ "$dir" = "$category" ] && printf '%s ' "$base"
|
|
151
|
+
done < "$MANIFEST_FILE"
|
|
152
|
+
}
|
|
153
|
+
SUPERKIT_INTERNAL_HOOKS="$(manifest_category hooks)"
|
|
154
|
+
SUPERKIT_INTERNAL_RULES="$(manifest_category rules)"
|
|
155
|
+
# FALLBACK — PER CATEGORY: each list independently falls back to its hard-coded
|
|
156
|
+
# literal when its own manifest lookup yields nothing. (A joint both-empty guard
|
|
157
|
+
# let a manifest that lost just one category's lines silently ship that
|
|
158
|
+
# category's internal files — the exact D2 leak the manifest exists to prevent.)
|
|
159
|
+
# A broken updater is worse than a stale list. Literals live ONLY on the
|
|
160
|
+
# FALLBACK lines. When the manifest EXISTS but a category is empty, we warn on
|
|
161
|
+
# stderr — a silent fallback is how a corrupted manifest stays invisible; a
|
|
162
|
+
# MISSING manifest stays silent (benign: the source clone predates it).
|
|
163
|
+
if [ -z "${SUPERKIT_INTERNAL_HOOKS// }" ]; then
|
|
164
|
+
[ -f "$MANIFEST_FILE" ] && echo "⚠ superkit-update: INTERNAL-FILES manifest has no 'hooks/' entries — using built-in fallback list" >&2
|
|
165
|
+
SUPERKIT_INTERNAL_HOOKS="superkit-counts-verify.sh verify-hooks.sh" # FALLBACK
|
|
166
|
+
fi
|
|
167
|
+
if [ -z "${SUPERKIT_INTERNAL_RULES// }" ]; then
|
|
168
|
+
[ -f "$MANIFEST_FILE" ] && echo "⚠ superkit-update: INTERNAL-FILES manifest has no 'rules/' entries — using built-in fallback list" >&2
|
|
169
|
+
SUPERKIT_INTERNAL_RULES="superkit-integrity.md" # FALLBACK
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
in_list() { # <needle> <space-separated-haystack>
|
|
173
|
+
local needle="$1" hay="$2" x
|
|
174
|
+
for x in $hay; do [ "$x" = "$needle" ] && return 0; done
|
|
175
|
+
return 1
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# ── Tags to compare against (pristine test) ──────────────────────────
|
|
179
|
+
# Baseline = the version the consumer customized FROM (v$INSTALL_VERSION), plus
|
|
180
|
+
# the last ~5 released tags so a file that is merely STALE (never synced, e.g.
|
|
181
|
+
# references/) still matches a released blob and updates rather than freezes.
|
|
182
|
+
RECENT_TAGS=$(git -C "$SUPERKIT_SOURCE" tag --list 'v*' --sort=-v:refname 2>/dev/null | head -5)
|
|
183
|
+
CHECK_TAGS=$(printf 'v%s\n%s\n' "$INSTALL_VERSION" "$RECENT_TAGS" | awk 'NF && !seen[$0]++')
|
|
184
|
+
|
|
185
|
+
# One reusable temp for git-show output; one for the preserved list. If mktemp
|
|
186
|
+
# fails, is_pristine treats every file as unverifiable → preserve (fail-safe).
|
|
187
|
+
GITSHOW_TMP=$(mktemp 2>/dev/null) || GITSHOW_TMP=""
|
|
188
|
+
PRESERVED_TMP=$(mktemp 2>/dev/null) || PRESERVED_TMP=""
|
|
189
|
+
trap 'rm -f "$GITSHOW_TMP" "$PRESERVED_TMP" 2>/dev/null' EXIT
|
|
190
|
+
|
|
191
|
+
# is_pristine <consumer_abs_path> <packages-relative-source-path>
|
|
192
|
+
# Returns 0 (safe to overwrite) iff the consumer's CONTENT matches the file's
|
|
193
|
+
# blob at one of CHECK_TAGS. Returns 1 (preserve) on any non-match/uncertainty.
|
|
194
|
+
is_pristine() {
|
|
195
|
+
local consumer="$1" rel="$2" tag
|
|
196
|
+
[ -n "$GITSHOW_TMP" ] || return 1 # no temp → cannot prove → preserve
|
|
197
|
+
while IFS= read -r tag; do
|
|
198
|
+
[ -z "$tag" ] && continue
|
|
199
|
+
if git -C "$SUPERKIT_SOURCE" show "${tag}:packages/${rel}" > "$GITSHOW_TMP" 2>/dev/null; then
|
|
200
|
+
cmp -s "$consumer" "$GITSHOW_TMP" && return 0
|
|
201
|
+
fi
|
|
202
|
+
done <<EOF
|
|
203
|
+
$CHECK_TAGS
|
|
204
|
+
EOF
|
|
205
|
+
return 1
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
SYNCED=0
|
|
209
|
+
PRESERVED=0
|
|
210
|
+
SKIPPED=0
|
|
211
|
+
|
|
212
|
+
# sync_file <src_abs> <dst_abs> <src_packages_rel> [exec]
|
|
213
|
+
# absent in consumer → create · pristine → overwrite · else → preserve+report
|
|
214
|
+
sync_file() {
|
|
215
|
+
local src="$1" dst="$2" rel="$3" x="${4:-}"
|
|
216
|
+
[ -f "$src" ] || return 0
|
|
217
|
+
if [ ! -e "$dst" ]; then
|
|
218
|
+
mkdir -p "$(dirname "$dst")" 2>/dev/null
|
|
219
|
+
if cp "$src" "$dst" 2>/dev/null; then
|
|
220
|
+
[ "$x" = "exec" ] && chmod +x "$dst" 2>/dev/null
|
|
221
|
+
SYNCED=$((SYNCED + 1))
|
|
222
|
+
fi
|
|
223
|
+
return 0
|
|
224
|
+
fi
|
|
225
|
+
if is_pristine "$dst" "$rel"; then
|
|
226
|
+
if cp "$src" "$dst" 2>/dev/null; then
|
|
227
|
+
[ "$x" = "exec" ] && chmod +x "$dst" 2>/dev/null
|
|
228
|
+
SYNCED=$((SYNCED + 1))
|
|
229
|
+
fi
|
|
230
|
+
else
|
|
231
|
+
PRESERVED=$((PRESERVED + 1))
|
|
232
|
+
[ -n "$PRESERVED_TMP" ] && printf '%s\n' "$dst" >> "$PRESERVED_TMP"
|
|
233
|
+
fi
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# ── Core agents ──────────────────────────────────────────────────────
|
|
237
|
+
for f in "$PACKAGES/core/agents/"*.md; do
|
|
238
|
+
[ -f "$f" ] || continue
|
|
239
|
+
b=$(basename "$f")
|
|
240
|
+
sync_file "$f" "$CLAUDE_DIR/agents/$b" "core/agents/$b"
|
|
241
|
+
done
|
|
242
|
+
|
|
243
|
+
# ── Core commands ────────────────────────────────────────────────────
|
|
244
|
+
for f in "$PACKAGES/core/commands/"*.md; do
|
|
245
|
+
[ -f "$f" ] || continue
|
|
246
|
+
b=$(basename "$f")
|
|
247
|
+
sync_file "$f" "$CLAUDE_DIR/commands/$b" "core/commands/$b"
|
|
248
|
+
done
|
|
249
|
+
|
|
250
|
+
# ── Core hooks (.sh AND .py) — skip internal + the self-managed updater ──
|
|
251
|
+
for f in "$PACKAGES/core/hooks/"*.sh "$PACKAGES/core/hooks/"*.py; do
|
|
252
|
+
[ -f "$f" ] || continue
|
|
253
|
+
b=$(basename "$f")
|
|
254
|
+
[ "$b" = "superkit-update.sh" ] && continue # self-managed via bootstrap
|
|
255
|
+
if in_list "$b" "$SUPERKIT_INTERNAL_HOOKS"; then
|
|
256
|
+
SKIPPED=$((SKIPPED + 1)); continue
|
|
257
|
+
fi
|
|
258
|
+
sync_file "$f" "$CLAUDE_DIR/scripts/hooks/$b" "core/hooks/$b" exec
|
|
259
|
+
done
|
|
260
|
+
|
|
261
|
+
# ── Core hook library (hooks/lib/*.sh — the :108 glob never descended here) ──
|
|
262
|
+
for f in "$PACKAGES/core/hooks/lib/"*.sh; do
|
|
263
|
+
[ -f "$f" ] || continue
|
|
264
|
+
b=$(basename "$f")
|
|
265
|
+
sync_file "$f" "$CLAUDE_DIR/scripts/hooks/lib/$b" "core/hooks/lib/$b" exec
|
|
266
|
+
done
|
|
267
|
+
|
|
268
|
+
# ── Core rules — skip internal ───────────────────────────────────────
|
|
269
|
+
for f in "$PACKAGES/core/rules/"*.md; do
|
|
270
|
+
[ -f "$f" ] || continue
|
|
271
|
+
b=$(basename "$f")
|
|
272
|
+
if in_list "$b" "$SUPERKIT_INTERNAL_RULES"; then
|
|
273
|
+
SKIPPED=$((SKIPPED + 1)); continue
|
|
274
|
+
fi
|
|
275
|
+
sync_file "$f" "$CLAUDE_DIR/rules/$b" "core/rules/$b"
|
|
276
|
+
done
|
|
277
|
+
|
|
278
|
+
# ── Core skills (SKILL.md + any sibling files inside each skill dir) ──
|
|
279
|
+
for skill_dir in "$PACKAGES/core/skills/"*/; do
|
|
280
|
+
[ -d "$skill_dir" ] || continue
|
|
281
|
+
skill_name=$(basename "$skill_dir")
|
|
282
|
+
for sf in "$skill_dir"*; do
|
|
283
|
+
[ -f "$sf" ] || continue
|
|
284
|
+
b=$(basename "$sf")
|
|
285
|
+
sync_file "$sf" "$CLAUDE_DIR/skills/$skill_name/$b" "core/skills/$skill_name/$b"
|
|
286
|
+
done
|
|
287
|
+
done
|
|
288
|
+
|
|
289
|
+
# ── Stack packages (only for installed stacks) ───────────────────────
|
|
290
|
+
for stack in $SUPERKIT_STACKS; do
|
|
291
|
+
# Stack agents
|
|
292
|
+
if [ -d "$PACKAGES/stack-agents/$stack" ]; then
|
|
293
|
+
for f in "$PACKAGES/stack-agents/$stack/"*.md; do
|
|
294
|
+
[ -f "$f" ] || continue
|
|
295
|
+
b=$(basename "$f")
|
|
296
|
+
sync_file "$f" "$CLAUDE_DIR/agents/$b" "stack-agents/$stack/$b"
|
|
297
|
+
done
|
|
298
|
+
# Stack agent references (on-demand knowledge docs — never synced before)
|
|
299
|
+
if [ -d "$PACKAGES/stack-agents/$stack/references" ]; then
|
|
300
|
+
for f in "$PACKAGES/stack-agents/$stack/references/"*.md; do
|
|
301
|
+
[ -f "$f" ] || continue
|
|
302
|
+
b=$(basename "$f")
|
|
303
|
+
sync_file "$f" "$CLAUDE_DIR/agents/references/$b" "stack-agents/$stack/references/$b"
|
|
304
|
+
done
|
|
305
|
+
fi
|
|
306
|
+
fi
|
|
307
|
+
# Stack hooks (.sh AND .py)
|
|
308
|
+
if [ -d "$PACKAGES/stack-hooks/$stack" ]; then
|
|
309
|
+
for f in "$PACKAGES/stack-hooks/$stack/"*.sh "$PACKAGES/stack-hooks/$stack/"*.py; do
|
|
310
|
+
[ -f "$f" ] || continue
|
|
311
|
+
b=$(basename "$f")
|
|
312
|
+
sync_file "$f" "$CLAUDE_DIR/scripts/hooks/$b" "stack-hooks/$stack/$b" exec
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
# Stack rules (never synced before — go-safety.md drifted two releases)
|
|
316
|
+
if [ -d "$PACKAGES/stack-rules/$stack" ]; then
|
|
317
|
+
for f in "$PACKAGES/stack-rules/$stack/"*.md; do
|
|
318
|
+
[ -f "$f" ] || continue
|
|
319
|
+
b=$(basename "$f")
|
|
320
|
+
sync_file "$f" "$CLAUDE_DIR/rules/$b" "stack-rules/$stack/$b"
|
|
321
|
+
done
|
|
322
|
+
fi
|
|
323
|
+
done
|
|
324
|
+
|
|
325
|
+
# ── ADR template (project root, create-if-missing only) ──────────────
|
|
326
|
+
# dev.md Phase 2 + superkit-init.md reference docs-templates/adr-template.md at
|
|
327
|
+
# the PROJECT root (NOT .claude/ — note CLAUDE_DIR points at .claude). Create it
|
|
328
|
+
# when absent so the nudge never dangles (counts as synced). If present, leave it
|
|
329
|
+
# untouched and do NOT report it as preserved: it's a doc template consumers
|
|
330
|
+
# legitimately edit, so silence — not "preserved (locally customized)" — is right.
|
|
331
|
+
ADR_SRC="$PACKAGES/core/docs-templates/adr-template.md"
|
|
332
|
+
ADR_DST="$CLAUDE_PROJECT_DIR/docs-templates/adr-template.md"
|
|
333
|
+
if [ -f "$ADR_SRC" ] && [ ! -e "$ADR_DST" ]; then
|
|
334
|
+
mkdir -p "$(dirname "$ADR_DST")" 2>/dev/null
|
|
335
|
+
if cp "$ADR_SRC" "$ADR_DST" 2>/dev/null; then
|
|
336
|
+
SYNCED=$((SYNCED + 1))
|
|
337
|
+
fi
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
# Settings.json — SKIP (user may have customized it)
|
|
341
|
+
# CLAUDE.md — SKIP (user fills it with project info)
|
|
342
|
+
|
|
343
|
+
# statusline.cjs — synced under the SAME pristine rule as everything else: a
|
|
344
|
+
# consumer who forked it keeps their fork (preserved + reported); an untouched
|
|
345
|
+
# copy gets the upstream improvements; an absent one is created.
|
|
346
|
+
sync_file "$PACKAGES/core/helpers/statusline.cjs" "$CLAUDE_DIR/scripts/statusline.cjs" "core/helpers/statusline.cjs"
|
|
347
|
+
|
|
348
|
+
# Keep hooks executable (matches the installer's +x behavior)
|
|
349
|
+
chmod +x "$CLAUDE_DIR/scripts/hooks/"*.sh 2>/dev/null
|
|
350
|
+
chmod +x "$CLAUDE_DIR/scripts/hooks/"*.py 2>/dev/null
|
|
351
|
+
chmod +x "$CLAUDE_DIR/scripts/hooks/lib/"*.sh 2>/dev/null
|
|
352
|
+
|
|
353
|
+
# ── Update meta version ──────────────────────────────────────────────
|
|
354
|
+
sed -i.bak "s/SUPERKIT_VERSION=.*/SUPERKIT_VERSION=\"$NEW_VERSION\"/" "$META_FILE" 2>/dev/null
|
|
355
|
+
rm -f "$META_FILE.bak"
|
|
356
|
+
|
|
357
|
+
# ── Report (compact — lands in every session start) ──────────────────
|
|
358
|
+
echo "superkit-update: $SYNCED synced · $PRESERVED preserved (locally customized) · $SKIPPED skipped (internal) · $OLD_VERSION → $NEW_VERSION"
|
|
359
|
+
if [ "$PRESERVED" -gt 0 ] && [ -n "$PRESERVED_TMP" ] && [ -f "$PRESERVED_TMP" ]; then
|
|
360
|
+
while IFS= read -r dpath; do
|
|
361
|
+
[ -z "$dpath" ] && continue
|
|
362
|
+
echo " preserved: $dpath"
|
|
363
|
+
done < "$PRESERVED_TMP"
|
|
364
|
+
echo " review with: git -C $SUPERKIT_SOURCE show v$OLD_VERSION:packages/<srcpath> | diff - <consumerpath>"
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
exit 0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Hook regression tests
|
|
2
|
+
|
|
3
|
+
Self-contained bash tests that stand up a temp git repo, pipe a realistic
|
|
4
|
+
Claude Code payload into each hook, and assert on the exit code.
|
|
5
|
+
|
|
6
|
+
## Run
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# All tests
|
|
10
|
+
bash packages/core/hooks/tests/run-all.sh
|
|
11
|
+
|
|
12
|
+
# Individual suites
|
|
13
|
+
bash packages/core/hooks/tests/doc-check-on-commit_test.sh
|
|
14
|
+
bash packages/core/hooks/tests/dev-required-on-commit_test.sh
|
|
15
|
+
bash packages/core/hooks/tests/json-path_test.sh
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Each suite exits 0 when every case passes, non-zero on the first failure.
|
|
19
|
+
|
|
20
|
+
## What's covered
|
|
21
|
+
|
|
22
|
+
**`doc-check-on-commit_test.sh`** — 7 scenarios
|
|
23
|
+
- Code-only commit → exit 2 (BLOCK)
|
|
24
|
+
- Code + `docs/superpowers/plans/` file → still BLOCK (plan does not satisfy)
|
|
25
|
+
- Code + `memory/` markdown → still BLOCK
|
|
26
|
+
- Code + `CHANGELOG.md` → still BLOCK
|
|
27
|
+
- Code + matching architecture doc → exit 0 (allow)
|
|
28
|
+
- New `*/jobs/**` file → BLOCK (backend-layers + tree-docs required)
|
|
29
|
+
- Modified `*/middleware/*.go` → BLOCK on backend-layers only (no tree demand)
|
|
30
|
+
|
|
31
|
+
**`dev-required-on-commit_test.sh`** — 5 scenarios
|
|
32
|
+
- 0 edits + code staged → allow
|
|
33
|
+
- 3 edits + code + no `/dev` marker → BLOCK
|
|
34
|
+
- 3 edits + `[quick]` override → allow + state reset
|
|
35
|
+
- 3 edits + marker present → allow + state reset
|
|
36
|
+
- 5 edits + docs-only staged → allow (docs-only exempt)
|
|
37
|
+
|
|
38
|
+
**`json-path_test.sh`** — 5 hooks × 1 sanity case each
|
|
39
|
+
- Each hook receives a realistic `.tool_input.*` payload and runs its main
|
|
40
|
+
branch instead of the pre-fix silent `exit 0`. We assert that the hook
|
|
41
|
+
either matches (does work / blocks) or falls through — never the silent
|
|
42
|
+
"input was empty" path.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# audit-trail-chain_test.sh — verify that concurrent sessions get separate
|
|
3
|
+
# .last-hash-<session> files rather than fighting over a single .last-hash.
|
|
4
|
+
#
|
|
5
|
+
# AUDIT_DIR is hard-coded as "$HOME/.claude/audit" inside audit-trail.sh,
|
|
6
|
+
# so we override HOME to a temp directory for hermeticity.
|
|
7
|
+
set -u
|
|
8
|
+
|
|
9
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/audit-trail.sh"
|
|
10
|
+
|
|
11
|
+
# Point HOME at a temp dir so audit-trail.sh writes there, not ~/.claude/audit
|
|
12
|
+
FAKE_HOME=$(mktemp -d)
|
|
13
|
+
AUDIT_DIR="$FAKE_HOME/.claude/audit"
|
|
14
|
+
|
|
15
|
+
cleanup() { rm -rf "$FAKE_HOME"; }
|
|
16
|
+
trap cleanup EXIT
|
|
17
|
+
|
|
18
|
+
# Emit one audit event for session $1.
|
|
19
|
+
# audit-trail.sh derives SESSION_KEY from the payload's .session_id field;
|
|
20
|
+
# the SESSION_ID env var is only consulted as a fallback when the payload
|
|
21
|
+
# omits .session_id. Here the payload always carries .session_id, so that
|
|
22
|
+
# value (not any env var) drives the per-session hash file.
|
|
23
|
+
emit() {
|
|
24
|
+
local sid="$1"
|
|
25
|
+
printf '%s' \
|
|
26
|
+
'{"session_id":"'"$sid"'","tool_name":"Edit","tool_input":{"file_path":"x.txt"}}' \
|
|
27
|
+
| HOME="$FAKE_HOME" bash "$HOOK" >/dev/null 2>&1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Interleave two sessions: A, B, A, B
|
|
31
|
+
emit "sessA"
|
|
32
|
+
emit "sessB"
|
|
33
|
+
emit "sessA"
|
|
34
|
+
emit "sessB"
|
|
35
|
+
|
|
36
|
+
# Each session must have its own per-session hash file
|
|
37
|
+
FILE_A="$AUDIT_DIR/.last-hash-sessA"
|
|
38
|
+
FILE_B="$AUDIT_DIR/.last-hash-sessB"
|
|
39
|
+
GLOBAL="$AUDIT_DIR/.last-hash"
|
|
40
|
+
|
|
41
|
+
PASS=1
|
|
42
|
+
|
|
43
|
+
if [ ! -f "$FILE_A" ]; then
|
|
44
|
+
echo "FAIL: missing $FILE_A"
|
|
45
|
+
PASS=0
|
|
46
|
+
fi
|
|
47
|
+
if [ ! -f "$FILE_B" ]; then
|
|
48
|
+
echo "FAIL: missing $FILE_B"
|
|
49
|
+
PASS=0
|
|
50
|
+
fi
|
|
51
|
+
if [ -f "$GLOBAL" ]; then
|
|
52
|
+
echo "FAIL: global .last-hash still exists (should have been replaced by per-session files)"
|
|
53
|
+
PASS=0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ "$PASS" -eq 1 ]; then
|
|
57
|
+
echo "PASS: per-session hash chains (.last-hash-sessA, .last-hash-sessB)"
|
|
58
|
+
exit 0
|
|
59
|
+
else
|
|
60
|
+
echo "Files in audit dir: $(ls -a "$AUDIT_DIR" 2>/dev/null | tr '\n' ' ')"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# block-dangerous-git_test.sh — regression test for block-dangerous-git.sh
|
|
3
|
+
# Feeds the hook JSON on stdin ({"tool_input":{"command":"..."}}) exactly as
|
|
4
|
+
# the PreToolUse wiring does, and asserts exit codes across the full matrix:
|
|
5
|
+
# exit 0 = allowed, exit 2 = blocked.
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/block-dangerous-git.sh"
|
|
9
|
+
[ -x "$HOOK" ] || { echo "FAIL: hook not executable at $HOOK"; exit 1; }
|
|
10
|
+
|
|
11
|
+
PASS_COUNT=0
|
|
12
|
+
FAIL_COUNT=0
|
|
13
|
+
|
|
14
|
+
# assert_exit <expected-code> <command>
|
|
15
|
+
# Note: no matrix command contains a double-quote or backslash, so the command
|
|
16
|
+
# embeds directly into the JSON without escaping.
|
|
17
|
+
assert_exit() {
|
|
18
|
+
local expected="$1"
|
|
19
|
+
local cmd="$2"
|
|
20
|
+
local actual=0
|
|
21
|
+
echo "{\"tool_input\":{\"command\":\"$cmd\"}}" | bash "$HOOK" >/dev/null 2>&1 || actual=$?
|
|
22
|
+
if [ "$actual" -eq "$expected" ]; then
|
|
23
|
+
echo "PASS: exit $actual (want $expected) | $cmd"
|
|
24
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
25
|
+
else
|
|
26
|
+
echo "FAIL: exit $actual, expected $expected | $cmd"
|
|
27
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
28
|
+
fi
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# assert_exit_json <expected-code> <command> [label]
|
|
32
|
+
# Same contract as assert_exit, but JSON-encodes <command> with python3 (jq
|
|
33
|
+
# fallback) so it can carry double-quotes, single-quotes and embedded newlines
|
|
34
|
+
# (needed for -m "quoted" messages and -F - heredoc bodies). Escaping is NEVER
|
|
35
|
+
# hand-rolled — the encoder owns it. <label> keeps multiline commands readable
|
|
36
|
+
# in the output; defaults to the (possibly multiline) command.
|
|
37
|
+
json_encode() {
|
|
38
|
+
# reads command on stdin, prints {"tool_input":{"command": <encoded>}}
|
|
39
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
40
|
+
python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command": sys.stdin.read()}}))'
|
|
41
|
+
else
|
|
42
|
+
jq -Rs '{tool_input: {command: .}}'
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
assert_exit_json() {
|
|
46
|
+
local expected="$1"
|
|
47
|
+
local cmd="$2"
|
|
48
|
+
local label="${3:-$cmd}"
|
|
49
|
+
local actual=0
|
|
50
|
+
printf '%s' "$cmd" | json_encode | bash "$HOOK" >/dev/null 2>&1 || actual=$?
|
|
51
|
+
if [ "$actual" -eq "$expected" ]; then
|
|
52
|
+
echo "PASS: exit $actual (want $expected) | $label"
|
|
53
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
54
|
+
else
|
|
55
|
+
echo "FAIL: exit $actual, expected $expected | $label"
|
|
56
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
echo "── MUST PASS (exit 0) — safe ops, incl. the recommended --force-with-lease escape hatch"
|
|
61
|
+
assert_exit 0 "git push --force-with-lease"
|
|
62
|
+
assert_exit 0 "git push --force-with-lease=main"
|
|
63
|
+
assert_exit 0 "git push --force-if-includes --force-with-lease"
|
|
64
|
+
assert_exit 0 "git push origin main"
|
|
65
|
+
assert_exit 0 "git checkout main"
|
|
66
|
+
assert_exit 0 "git checkout -b feature"
|
|
67
|
+
assert_exit 0 "git checkout feature-branch"
|
|
68
|
+
assert_exit 0 "git switch main"
|
|
69
|
+
assert_exit 0 "git switch -c new-branch"
|
|
70
|
+
assert_exit 0 "git clean -n"
|
|
71
|
+
assert_exit 0 "git restore --source=HEAD~1 file.go"
|
|
72
|
+
assert_exit 0 "git status"
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
echo "── MUST BLOCK (exit 2) — force push (bare / -f / +refspec) + working-tree discard"
|
|
76
|
+
assert_exit 2 "git push --force"
|
|
77
|
+
assert_exit 2 "git push -f origin main"
|
|
78
|
+
assert_exit 2 "git push origin +main"
|
|
79
|
+
assert_exit 2 "git push origin +refs/heads/main:refs/heads/main"
|
|
80
|
+
assert_exit 2 "git checkout ."
|
|
81
|
+
assert_exit 2 "git checkout -- src/foo.go"
|
|
82
|
+
assert_exit 2 "git checkout -f"
|
|
83
|
+
assert_exit 2 "git clean -fd"
|
|
84
|
+
assert_exit 2 "git clean -fdx"
|
|
85
|
+
assert_exit 2 "git switch --discard-changes"
|
|
86
|
+
assert_exit 2 "git switch -C main"
|
|
87
|
+
|
|
88
|
+
echo ""
|
|
89
|
+
echo "── PRE-EXISTING blocks (exit 2) — prove no regression"
|
|
90
|
+
assert_exit 2 "git commit --no-verify"
|
|
91
|
+
assert_exit 2 "git reset --hard HEAD~1"
|
|
92
|
+
assert_exit 2 "git branch -D feature"
|
|
93
|
+
|
|
94
|
+
echo ""
|
|
95
|
+
echo "── D4: commit MESSAGE must NOT self-trip the guards (exit 0) — needs JSON encoding"
|
|
96
|
+
# (1) message that literally names --no-verify while explaining the ban
|
|
97
|
+
assert_exit_json 0 'git commit -m "docs: explain why --no-verify is banned"' \
|
|
98
|
+
'message names --no-verify (explaining the ban)'
|
|
99
|
+
# (2) message that names a force push + the +refspec form
|
|
100
|
+
assert_exit_json 0 'git commit -m "hook: block git push --force and +refspec"' \
|
|
101
|
+
'message names git push --force'
|
|
102
|
+
# (3) -F - heredoc whose body mentions BOTH --no-verify and --force
|
|
103
|
+
HEREDOC_BOTH=$'git commit -F - <<\'EOF\'\ndocs: document the git guardrails\n\nExplain why --no-verify is banned and why git push --force is blocked.\nEOF'
|
|
104
|
+
assert_exit_json 0 "$HEREDOC_BOTH" \
|
|
105
|
+
'heredoc body (-F -) mentions --no-verify AND git push --force'
|
|
106
|
+
# --message= long form naming force-with-lease / --force as prose
|
|
107
|
+
assert_exit_json 0 "git commit --message='explain --force-with-lease vs --force'" \
|
|
108
|
+
'--message= prose naming --force'
|
|
109
|
+
# chained-but-SAFE: message stripped, remainder is the recommended escape hatch
|
|
110
|
+
assert_exit_json 0 'git commit -m "x" && git push --force-with-lease' \
|
|
111
|
+
'message stripped; chained --force-with-lease stays allowed'
|
|
112
|
+
# ADVERSARIAL: a real force-push command TEXT lives entirely inside the quotes
|
|
113
|
+
assert_exit_json 0 'git commit -m "sample; git push --force in a doc"' \
|
|
114
|
+
'ADVERSARIAL: "; git push --force" is TEXT inside the quotes'
|
|
115
|
+
# ADVERSARIAL: heredoc body naming reset --hard / branch -D as documentation
|
|
116
|
+
HEREDOC_DESTRUCT=$'git commit -F - <<EOF\nchangelog: warn about git reset --hard and git branch -D\nEOF'
|
|
117
|
+
assert_exit_json 0 "$HEREDOC_DESTRUCT" \
|
|
118
|
+
'ADVERSARIAL: heredoc body names reset --hard & branch -D'
|
|
119
|
+
|
|
120
|
+
echo ""
|
|
121
|
+
echo "── D4: guard must NOT weaken (exit 2) — danger OUTSIDE the message still blocks"
|
|
122
|
+
# (4) chained real force push after a stripped message
|
|
123
|
+
assert_exit_json 2 'git commit -m "x" && git push --force' \
|
|
124
|
+
'chained: git push --force after a stripped message'
|
|
125
|
+
# (5) a real --no-verify flag sitting OUTSIDE any message
|
|
126
|
+
assert_exit_json 2 'git commit --no-verify -m "x"' \
|
|
127
|
+
'real --no-verify flag outside the -m message'
|
|
128
|
+
# ADVERSARIAL: message stripped, then a real reset --hard via ; separator
|
|
129
|
+
assert_exit_json 2 'git commit -m "safe" ; git reset --hard HEAD~1' \
|
|
130
|
+
'ADVERSARIAL: message stripped; chained git reset --hard blocks'
|
|
131
|
+
# ADVERSARIAL: unquoted -m token then a real chained force push
|
|
132
|
+
assert_exit_json 2 'git commit -m wip && git push --force' \
|
|
133
|
+
'ADVERSARIAL: unquoted -m value; chained git push --force blocks'
|
|
134
|
+
# ADVERSARIAL: message stripped, then a second real --no-verify commit
|
|
135
|
+
assert_exit_json 2 'git commit -m "msg" && git commit --no-verify' \
|
|
136
|
+
'ADVERSARIAL: chained second commit with real --no-verify blocks'
|
|
137
|
+
|
|
138
|
+
echo ""
|
|
139
|
+
TOTAL=$((PASS_COUNT + FAIL_COUNT))
|
|
140
|
+
if [ "$FAIL_COUNT" -gt 0 ]; then
|
|
141
|
+
echo "BLOCK-DANGEROUS-GIT TESTS FAILED ($FAIL_COUNT/$TOTAL failed)"
|
|
142
|
+
exit 1
|
|
143
|
+
fi
|
|
144
|
+
echo "ALL BLOCK-DANGEROUS-GIT TESTS PASSED ($PASS_COUNT/$TOTAL)"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# h1.sh — test hook: exits 2 with a diagnostic line on stderr.
|
|
3
|
+
# Used to probe whether exit 2 in one hook suppresses peer hooks.
|
|
4
|
+
|
|
5
|
+
LOG="${CHAIN_TEST_LOG:-${TMPDIR:-/tmp}/claude-chain-test.log}"
|
|
6
|
+
printf '%s h1 start (pid=%d ppid=%d)\n' "$(date +%H:%M:%S.%N)" "$$" "$PPID" >> "$LOG"
|
|
7
|
+
# Sleep a bit so concurrent peers can be observed in the log
|
|
8
|
+
sleep 0.3
|
|
9
|
+
printf '%s h1 exit=2\n' "$(date +%H:%M:%S.%N)" >> "$LOG"
|
|
10
|
+
echo "h1: this hook intentionally exits 2" >&2
|
|
11
|
+
exit 2
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# h2.sh — test hook: benign, sleeps mid-run, exit 0.
|
|
3
|
+
# Used to observe whether h1's exit 2 interrupts or lets peers finish.
|
|
4
|
+
|
|
5
|
+
LOG="${CHAIN_TEST_LOG:-${TMPDIR:-/tmp}/claude-chain-test.log}"
|
|
6
|
+
printf '%s h2 start (pid=%d ppid=%d)\n' "$(date +%H:%M:%S.%N)" "$$" "$PPID" >> "$LOG"
|
|
7
|
+
sleep 0.5
|
|
8
|
+
printf '%s h2 exit=0\n' "$(date +%H:%M:%S.%N)" >> "$LOG"
|
|
9
|
+
exit 0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# h3.sh — test hook: touches a marker and exits 0.
|
|
3
|
+
# Presence of the marker after the experiment proves h3 ran despite h1's exit 2.
|
|
4
|
+
|
|
5
|
+
LOG="${CHAIN_TEST_LOG:-${TMPDIR:-/tmp}/claude-chain-test.log}"
|
|
6
|
+
MARKER="${CHAIN_TEST_MARKER:-${TMPDIR:-/tmp}/claude-chain-test-h3-ran}"
|
|
7
|
+
printf '%s h3 start (pid=%d ppid=%d)\n' "$(date +%H:%M:%S.%N)" "$$" "$PPID" >> "$LOG"
|
|
8
|
+
sleep 0.7
|
|
9
|
+
touch "$MARKER"
|
|
10
|
+
printf '%s h3 exit=0 (marker=%s)\n' "$(date +%H:%M:%S.%N)" "$MARKER" >> "$LOG"
|
|
11
|
+
exit 0
|