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,154 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gateguard-pre-edit.sh — require facts (Grep/Read) before Edit/Write/Bash
|
|
3
|
+
# Triggers on: PreToolUse(Edit|Write|MultiEdit|Bash)
|
|
4
|
+
# Profile: standard, strict
|
|
5
|
+
#
|
|
6
|
+
# Pattern caught: Claude immediately starts editing files or running shell
|
|
7
|
+
# commands without first looking around (no Grep/Read in the last N tool
|
|
8
|
+
# calls). Forces "establish facts before action" discipline — find the
|
|
9
|
+
# callers, read the schema, understand the surface area before changing it.
|
|
10
|
+
#
|
|
11
|
+
# Advisory only by default (exit 0 with a stderr nudge). Set
|
|
12
|
+
# CLAUDE_GATEGUARD_STRICT=1 to make it blocking (exit 2 with a structured
|
|
13
|
+
# JSON decision).
|
|
14
|
+
#
|
|
15
|
+
# Inspired by GateGuard pattern from affaan-m/everything-claude-code
|
|
16
|
+
# (Anthropic Hackathon Winner).
|
|
17
|
+
#
|
|
18
|
+
# Opt-out: CLAUDE_DISABLE_GATEGUARD=1
|
|
19
|
+
|
|
20
|
+
# Unified profile + disable check
|
|
21
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
22
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
23
|
+
# shellcheck source=/dev/null
|
|
24
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
25
|
+
|
|
26
|
+
[ "${CLAUDE_DISABLE_GATEGUARD:-}" = "1" ] && exit 0
|
|
27
|
+
|
|
28
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
29
|
+
if [ "$PROFILE" = "fast" ]; then
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
34
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
35
|
+
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // empty' 2>/dev/null)
|
|
36
|
+
|
|
37
|
+
[ "$EVENT" = "PreToolUse" ] || exit 0
|
|
38
|
+
case "$TOOL_NAME" in
|
|
39
|
+
Edit|Write|MultiEdit|Bash) ;;
|
|
40
|
+
*) exit 0 ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
# Read-only Bash commands don't require a facts pass — exempt them.
|
|
44
|
+
if [ "$TOOL_NAME" = "Bash" ]; then
|
|
45
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
46
|
+
case "$CMD" in
|
|
47
|
+
"ls "*|"ls"|"pwd"|"cat "*|"head "*|"tail "*|"less "*|"more "*|"wc "*|"file "*)
|
|
48
|
+
exit 0 ;;
|
|
49
|
+
"grep "*|"rg "*|"find "*|"fd "*|"which "*|"type "*|"command "*|"locate "*)
|
|
50
|
+
exit 0 ;;
|
|
51
|
+
"git status"*|"git diff"*|"git log"*|"git show"*|"git blame"*|"git branch"*)
|
|
52
|
+
exit 0 ;;
|
|
53
|
+
"git rev-parse"*|"git config "*|"git remote "*|"git ls-files"*|"git grep"*)
|
|
54
|
+
exit 0 ;;
|
|
55
|
+
"git stash list"*|"git reflog"*|"git describe"*|"git tag --list"*|"git tag -l"*)
|
|
56
|
+
exit 0 ;;
|
|
57
|
+
"echo "*|"printf "*|"date"*|"whoami"*|"hostname"*|"env"|"uname"*|"id"|"id "*)
|
|
58
|
+
exit 0 ;;
|
|
59
|
+
"ps "*|"top"|"htop"|"df "*|"du "*|"free"*|"jq "*|"yq "*)
|
|
60
|
+
exit 0 ;;
|
|
61
|
+
"sed -n "*|"awk "*|"nl "*|"tree "*|"tree"|"cut "*|"sort "*|"uniq "*)
|
|
62
|
+
exit 0 ;;
|
|
63
|
+
"stat "*|"realpath "*|"readlink "*|"basename "*|"dirname "*)
|
|
64
|
+
exit 0 ;;
|
|
65
|
+
"node --version"*|"python3 --version"*|"npm view"*|"npm list"*|"npm ls"*)
|
|
66
|
+
exit 0 ;;
|
|
67
|
+
esac
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# State file tracks the last Grep/Read timestamp + tool call counter.
|
|
71
|
+
SESSION_KEY=$(command -v superkit_session_key >/dev/null 2>&1 && superkit_session_key || printf '%s' "${SESSION_ID:-${CLAUDE_HOOK_PID:-$PPID}}")
|
|
72
|
+
STATE_FILE="${CLAUDE_GATEGUARD_STATE:-${TMPDIR:-/tmp}/superkit-gateguard-state-${SESSION_KEY}}"
|
|
73
|
+
WINDOW_SECONDS="${CLAUDE_GATEGUARD_WINDOW:-600}"
|
|
74
|
+
|
|
75
|
+
now=$(date +%s)
|
|
76
|
+
last_facts=0
|
|
77
|
+
counter_since_facts=999
|
|
78
|
+
|
|
79
|
+
if [ -f "$STATE_FILE" ]; then
|
|
80
|
+
# State format: "last_facts_ts counter_since_facts"
|
|
81
|
+
read -r last_facts counter_since_facts < "$STATE_FILE" 2>/dev/null
|
|
82
|
+
last_facts=${last_facts:-0}
|
|
83
|
+
counter_since_facts=${counter_since_facts:-999}
|
|
84
|
+
|
|
85
|
+
# Defensive — non-numeric values reset to safe defaults.
|
|
86
|
+
case "$last_facts" in (*[!0-9]*|"") last_facts=0 ;; esac
|
|
87
|
+
case "$counter_since_facts" in (*[!0-9]*|"") counter_since_facts=999 ;; esac
|
|
88
|
+
|
|
89
|
+
# Clock skew / future timestamps — treat as fresh-start, not as "facts
|
|
90
|
+
# are valid forever".
|
|
91
|
+
if [ "$last_facts" -gt "$now" ]; then
|
|
92
|
+
last_facts=0
|
|
93
|
+
counter_since_facts=999
|
|
94
|
+
# Expire stale state — if last Grep/Read was too long ago, treat as never.
|
|
95
|
+
elif [ "$((now - last_facts))" -gt "$WINDOW_SECONDS" ]; then
|
|
96
|
+
last_facts=0
|
|
97
|
+
counter_since_facts=999
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Increment counter for this attempted action (we're in PreToolUse for
|
|
102
|
+
# Edit/Write/Bash). If a recent Grep/Read exists, we're good.
|
|
103
|
+
counter_since_facts=$((counter_since_facts + 1))
|
|
104
|
+
|
|
105
|
+
# Atomic write: build temp then mv (single rename = atomic on POSIX).
|
|
106
|
+
# Prevents concurrent hook invocations from interleaving bytes.
|
|
107
|
+
TMP_STATE="${STATE_FILE}.$$.$now"
|
|
108
|
+
echo "$last_facts $counter_since_facts" > "$TMP_STATE" 2>/dev/null && mv -f "$TMP_STATE" "$STATE_FILE" 2>/dev/null
|
|
109
|
+
# If state write failed (full disk, permissions) — proceed anyway.
|
|
110
|
+
# Hook is fail-open by design.
|
|
111
|
+
|
|
112
|
+
# Threshold: if no Grep/Read recorded at all, or counter is too high,
|
|
113
|
+
# emit a warning.
|
|
114
|
+
if [ "$last_facts" = "0" ] || [ "$counter_since_facts" -gt 3 ]; then
|
|
115
|
+
if [ "${CLAUDE_GATEGUARD_STRICT:-}" = "1" ]; then
|
|
116
|
+
# Strict mode: emit structured JSON to block the tool call
|
|
117
|
+
cat <<'EOF'
|
|
118
|
+
{
|
|
119
|
+
"decision": "block",
|
|
120
|
+
"reason": "GateGuard: no recent Grep/Read in the last 10 minutes. Before editing or running commands, look at the surface area: find callers (Grep), read the schema/types (Read), confirm the file/function exists. Edit blindly = silent breakage. Run a discovery step first, then retry."
|
|
121
|
+
}
|
|
122
|
+
EOF
|
|
123
|
+
exit 2
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Advisory mode: stderr nudge (human-readable transcript) PLUS a stdout
|
|
127
|
+
# additionalContext emission — Claude Code can swallow non-blocking
|
|
128
|
+
# PreToolUse stderr, so the JSON channel is what reliably reaches the model.
|
|
129
|
+
#
|
|
130
|
+
# Per-session throttle (Opus 4.8 self-flags): suppress an identical nudge
|
|
131
|
+
# repeated within CLAUDE_NUDGE_WINDOW_MIN (default 10m; 0 disables). This
|
|
132
|
+
# gates the advisory EMISSION only — the strict (exit 2) block above is
|
|
133
|
+
# never throttled.
|
|
134
|
+
EMIT_ADVISORY=1
|
|
135
|
+
if command -v superkit_should_emit_advisory >/dev/null 2>&1; then
|
|
136
|
+
superkit_should_emit_advisory "gateguard-pre-edit: no recent Grep/Read before edit" || EMIT_ADVISORY=0
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [ "$EMIT_ADVISORY" = "1" ]; then
|
|
140
|
+
echo "" >&2
|
|
141
|
+
echo "🔍 GATEGUARD: no Grep/Read in recent history. Consider establishing facts first:" >&2
|
|
142
|
+
echo " - Grep for callers / importers before changing a function" >&2
|
|
143
|
+
echo " - Read the schema / types before changing data shape" >&2
|
|
144
|
+
echo " - Confirm the file exists and you're editing the right one" >&2
|
|
145
|
+
echo " (CLAUDE_GATEGUARD_STRICT=1 to enforce; CLAUDE_DISABLE_GATEGUARD=1 to silence)" >&2
|
|
146
|
+
echo "" >&2
|
|
147
|
+
|
|
148
|
+
if command -v superkit_advise >/dev/null 2>&1; then
|
|
149
|
+
superkit_advise "GateGuard: no recent Grep/Read before this edit/command. Establish facts first — Grep for callers/importers, Read the schema/types, confirm the file exists — before changing it. (CLAUDE_GATEGUARD_STRICT=1 to enforce; CLAUDE_DISABLE_GATEGUARD=1 to silence)"
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
exit 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gateguard-record-facts.sh — record successful Grep/Read calls for GateGuard
|
|
3
|
+
# Triggers on: PostToolUse(Grep|Read|Glob)
|
|
4
|
+
# Profile: standard, strict
|
|
5
|
+
#
|
|
6
|
+
# Companion to gateguard-pre-edit.sh — every time Claude actually looks at
|
|
7
|
+
# the code (Grep/Read/Glob), reset the GateGuard counter so the next edit
|
|
8
|
+
# is allowed.
|
|
9
|
+
#
|
|
10
|
+
# Opt-out: CLAUDE_DISABLE_GATEGUARD=1
|
|
11
|
+
|
|
12
|
+
SUPERKIT_PROFILE_LIB="$(dirname "$0")/lib/profile.sh"
|
|
13
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] || SUPERKIT_PROFILE_LIB="$(dirname "$0")/../lib/profile.sh"
|
|
14
|
+
# shellcheck source=/dev/null
|
|
15
|
+
[ -f "$SUPERKIT_PROFILE_LIB" ] && source "$SUPERKIT_PROFILE_LIB" && should_skip_hook "$(basename "$0" .sh)" && exit 0
|
|
16
|
+
|
|
17
|
+
[ "${CLAUDE_DISABLE_GATEGUARD:-}" = "1" ] && exit 0
|
|
18
|
+
|
|
19
|
+
PROFILE="${CLAUDE_HOOK_PROFILE:-standard}"
|
|
20
|
+
[ "$PROFILE" = "fast" ] && exit 0
|
|
21
|
+
|
|
22
|
+
INPUT=$(cat 2>/dev/null || echo '{}')
|
|
23
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
24
|
+
|
|
25
|
+
case "$TOOL_NAME" in
|
|
26
|
+
Grep|Read|Glob) ;;
|
|
27
|
+
*) exit 0 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
SESSION_KEY=$(command -v superkit_session_key >/dev/null 2>&1 && superkit_session_key || printf '%s' "${SESSION_ID:-${CLAUDE_HOOK_PID:-$PPID}}")
|
|
31
|
+
STATE_FILE="${CLAUDE_GATEGUARD_STATE:-${TMPDIR:-/tmp}/superkit-gateguard-state-${SESSION_KEY}}"
|
|
32
|
+
now=$(date +%s)
|
|
33
|
+
|
|
34
|
+
# Reset counter on facts-gathering tool use — atomic write via temp + mv.
|
|
35
|
+
TMP_STATE="${STATE_FILE}.$$.$now"
|
|
36
|
+
echo "$now 0" > "$TMP_STATE" 2>/dev/null && mv -f "$TMP_STATE" "$STATE_FILE" 2>/dev/null
|
|
37
|
+
exit 0
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""intake-classifier.py — classify user prompt intent on UserPromptSubmit.
|
|
3
|
+
|
|
4
|
+
Adapts the VKirill/codex-starter-kit handoff-intake-classifier pattern to
|
|
5
|
+
the superkit. Two-layer classification:
|
|
6
|
+
|
|
7
|
+
Layer 1: deterministic scorer (0-15) on RU+EN keywords. Always runs.
|
|
8
|
+
Layer 2: optional LLM fallback (gpt-5.5-nano via OpenAI Responses API)
|
|
9
|
+
when confidence < 0.78 AND OPENAI_API_KEY is set.
|
|
10
|
+
|
|
11
|
+
Output: hookSpecificOutput.additionalContext with guidance for the
|
|
12
|
+
assistant — intent, flags, and one-sentence rationale.
|
|
13
|
+
|
|
14
|
+
Fail-open: any parsing / network / model error is silently dropped so
|
|
15
|
+
the user's prompt is never blocked.
|
|
16
|
+
|
|
17
|
+
Profile: standard, strict (skipped on fast).
|
|
18
|
+
Opt-out: CLAUDE_DISABLE_INTAKE_CLASSIFIER=1
|
|
19
|
+
|
|
20
|
+
This hook coexists with user-intent-detect.sh (which handles the narrow
|
|
21
|
+
"[quick]" override signal). The two answer different questions.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import re
|
|
29
|
+
import sys
|
|
30
|
+
import time
|
|
31
|
+
from typing import Any
|
|
32
|
+
from urllib import error, request
|
|
33
|
+
|
|
34
|
+
API_URL = "https://api.openai.com/v1/responses"
|
|
35
|
+
DEFAULT_MODEL = "gpt-5.5-nano"
|
|
36
|
+
MAX_PROMPT_CHARS = 1800
|
|
37
|
+
MAX_LOG_LINES = 12
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def setting(name: str, default: str = "") -> str:
|
|
41
|
+
return os.environ.get(name, default)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def disabled() -> bool:
|
|
45
|
+
if setting("CLAUDE_DISABLE_INTAKE_CLASSIFIER") == "1":
|
|
46
|
+
return True
|
|
47
|
+
if setting("CLAUDE_HOOK_PROFILE", "standard") == "fast":
|
|
48
|
+
return True
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def emit_context(context: str) -> None:
|
|
53
|
+
print(
|
|
54
|
+
json.dumps(
|
|
55
|
+
{
|
|
56
|
+
"hookSpecificOutput": {
|
|
57
|
+
"hookEventName": "UserPromptSubmit",
|
|
58
|
+
"additionalContext": context,
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
ensure_ascii=False,
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def normalize_prompt(prompt: str) -> str:
|
|
67
|
+
lines = prompt.strip().splitlines()
|
|
68
|
+
compacted: list[str] = []
|
|
69
|
+
log_lines = 0
|
|
70
|
+
for line in lines:
|
|
71
|
+
stripped = line.strip()
|
|
72
|
+
looks_like_log = bool(
|
|
73
|
+
re.search(r"\b(GET|POST|PUT|PATCH|DELETE)\s+https?://", stripped)
|
|
74
|
+
or re.search(r"\b(4\d\d|5\d\d)\b", stripped)
|
|
75
|
+
or re.search(r"\bat\s+[\w.$-]+", stripped)
|
|
76
|
+
or ".js:" in stripped
|
|
77
|
+
)
|
|
78
|
+
if looks_like_log:
|
|
79
|
+
log_lines += 1
|
|
80
|
+
if log_lines > MAX_LOG_LINES:
|
|
81
|
+
continue
|
|
82
|
+
compacted.append(stripped)
|
|
83
|
+
text = "\n".join(compacted)
|
|
84
|
+
if len(text) > MAX_PROMPT_CHARS:
|
|
85
|
+
return text[:MAX_PROMPT_CHARS] + "\n[truncated for intake classification]"
|
|
86
|
+
return text
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def has_any(text: str, needles: tuple[str, ...]) -> bool:
|
|
90
|
+
return any(needle in text for needle in needles)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def has_action_request(text: str) -> bool:
|
|
94
|
+
patterns = (
|
|
95
|
+
r"\bсделай\b",
|
|
96
|
+
r"\bсделать\s+надо\b",
|
|
97
|
+
r"\bисправ(ь|ляй|ить)\b",
|
|
98
|
+
r"\bдобав(ь|ить)\b",
|
|
99
|
+
r"\bобнов(и|ить)\b",
|
|
100
|
+
r"\bперезапуст(и|ить)\b",
|
|
101
|
+
r"\bпочини\b",
|
|
102
|
+
r"\bпочинить\b",
|
|
103
|
+
r"\bпередел(ай|ать)\b",
|
|
104
|
+
r"\bдоработай\b",
|
|
105
|
+
r"\bвнеси\b",
|
|
106
|
+
r"\bзапушь\b",
|
|
107
|
+
r"\bзакоммит(ь|ьте)?\b",
|
|
108
|
+
r"\bреализ(уй|овать)\b",
|
|
109
|
+
r"\bприступай\s+к\s+реализации\b",
|
|
110
|
+
r"\bbuild\b",
|
|
111
|
+
r"\bfix\b",
|
|
112
|
+
r"\badd\b",
|
|
113
|
+
r"\bcreate\b",
|
|
114
|
+
r"\bimplement\b",
|
|
115
|
+
r"\bcommit\b",
|
|
116
|
+
r"\bpush\b",
|
|
117
|
+
)
|
|
118
|
+
return any(re.search(pattern, text, re.IGNORECASE) for pattern in patterns)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def deterministic_classify(prompt: str) -> dict[str, Any]:
|
|
122
|
+
lower = prompt.lower()
|
|
123
|
+
score = 0
|
|
124
|
+
reasons: list[str] = []
|
|
125
|
+
|
|
126
|
+
question_mark = "?" in prompt
|
|
127
|
+
action_requested = has_action_request(lower)
|
|
128
|
+
planning_words = (
|
|
129
|
+
"план",
|
|
130
|
+
"сплан",
|
|
131
|
+
"архитектур",
|
|
132
|
+
"исслед",
|
|
133
|
+
"проанализ",
|
|
134
|
+
"посмотри как",
|
|
135
|
+
"как лучше",
|
|
136
|
+
"миграц",
|
|
137
|
+
"plan",
|
|
138
|
+
"architecture",
|
|
139
|
+
"design",
|
|
140
|
+
"analyze",
|
|
141
|
+
"research",
|
|
142
|
+
)
|
|
143
|
+
question_words = (
|
|
144
|
+
"почему",
|
|
145
|
+
"зачем",
|
|
146
|
+
"разве",
|
|
147
|
+
"есть ли",
|
|
148
|
+
"можно ли",
|
|
149
|
+
"как понять",
|
|
150
|
+
"вопрос",
|
|
151
|
+
"what is",
|
|
152
|
+
"why does",
|
|
153
|
+
"how come",
|
|
154
|
+
"is it possible",
|
|
155
|
+
)
|
|
156
|
+
continuation_words = ("продолжай", "лимиты обновились", "continue", "resume")
|
|
157
|
+
multi_surface_words = (
|
|
158
|
+
"дашборд",
|
|
159
|
+
"crm",
|
|
160
|
+
"бот",
|
|
161
|
+
"api",
|
|
162
|
+
"бд",
|
|
163
|
+
"база",
|
|
164
|
+
"платеж",
|
|
165
|
+
"модел",
|
|
166
|
+
"статист",
|
|
167
|
+
"метрик",
|
|
168
|
+
"dashboard",
|
|
169
|
+
"database",
|
|
170
|
+
"payment",
|
|
171
|
+
"metrics",
|
|
172
|
+
"endpoint",
|
|
173
|
+
)
|
|
174
|
+
bug_words = (
|
|
175
|
+
"не работает",
|
|
176
|
+
"не работают",
|
|
177
|
+
"пусто",
|
|
178
|
+
"по нулям",
|
|
179
|
+
"ошибк",
|
|
180
|
+
"500",
|
|
181
|
+
"не меняется",
|
|
182
|
+
"отсутств",
|
|
183
|
+
"broken",
|
|
184
|
+
"doesn't work",
|
|
185
|
+
"fails",
|
|
186
|
+
"crash",
|
|
187
|
+
"error",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if has_any(lower, continuation_words):
|
|
191
|
+
return {
|
|
192
|
+
"intent": "continue",
|
|
193
|
+
"score": 3,
|
|
194
|
+
"confidence": 0.9,
|
|
195
|
+
"should_edit": False,
|
|
196
|
+
"should_plan": False,
|
|
197
|
+
"should_use_task_ledger": False,
|
|
198
|
+
"subagents_authorized": False,
|
|
199
|
+
"reason": "User asks to continue an existing task.",
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if action_requested:
|
|
203
|
+
score += 5
|
|
204
|
+
reasons.append("action verb")
|
|
205
|
+
if has_any(lower, planning_words):
|
|
206
|
+
score += 4
|
|
207
|
+
reasons.append("planning/discovery language")
|
|
208
|
+
if has_any(lower, multi_surface_words):
|
|
209
|
+
score += 4
|
|
210
|
+
reasons.append("multi-surface system terms")
|
|
211
|
+
if has_any(lower, bug_words):
|
|
212
|
+
score += 3
|
|
213
|
+
reasons.append("bug/regression symptoms")
|
|
214
|
+
if question_mark or has_any(lower, question_words):
|
|
215
|
+
score += 2
|
|
216
|
+
reasons.append("question language")
|
|
217
|
+
if prompt.count("\n\n") >= 2 or len(prompt) > 900:
|
|
218
|
+
score += 2
|
|
219
|
+
reasons.append("multi-item prompt")
|
|
220
|
+
|
|
221
|
+
score = min(score, 15)
|
|
222
|
+
subagents_authorized = has_any(
|
|
223
|
+
lower, ("сабагент", "subagent", "делег", "параллел", "parallel", "delegate")
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if has_any(lower, ("начинай", "пиши план", "write a plan")) and has_any(
|
|
227
|
+
lower, planning_words
|
|
228
|
+
):
|
|
229
|
+
intent = "planning"
|
|
230
|
+
should_edit = False
|
|
231
|
+
should_plan = True
|
|
232
|
+
elif action_requested:
|
|
233
|
+
intent = "implementation"
|
|
234
|
+
should_edit = True
|
|
235
|
+
should_plan = score >= 10
|
|
236
|
+
elif question_mark or has_any(lower, question_words):
|
|
237
|
+
if score >= 8 and not has_any(lower, ("пока вопрос", "просто вопрос", "just curious")):
|
|
238
|
+
intent = "analysis"
|
|
239
|
+
should_edit = False
|
|
240
|
+
should_plan = True
|
|
241
|
+
else:
|
|
242
|
+
intent = "question_only"
|
|
243
|
+
should_edit = False
|
|
244
|
+
should_plan = False
|
|
245
|
+
elif has_any(lower, planning_words):
|
|
246
|
+
intent = "planning"
|
|
247
|
+
should_edit = False
|
|
248
|
+
should_plan = True
|
|
249
|
+
else:
|
|
250
|
+
intent = "analysis"
|
|
251
|
+
should_edit = False
|
|
252
|
+
should_plan = score >= 7
|
|
253
|
+
|
|
254
|
+
should_use_task_ledger = score >= 7 and intent not in {"question_only", "continue"}
|
|
255
|
+
confidence = 0.86
|
|
256
|
+
if question_mark and action_requested:
|
|
257
|
+
confidence = 0.68
|
|
258
|
+
elif intent == "analysis" and 6 <= score <= 8:
|
|
259
|
+
confidence = 0.7
|
|
260
|
+
elif len(prompt.strip()) < 20:
|
|
261
|
+
confidence = 0.72
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
"intent": intent,
|
|
265
|
+
"score": score,
|
|
266
|
+
"confidence": confidence,
|
|
267
|
+
"should_edit": should_edit,
|
|
268
|
+
"should_plan": should_plan,
|
|
269
|
+
"should_use_task_ledger": should_use_task_ledger,
|
|
270
|
+
"subagents_authorized": subagents_authorized,
|
|
271
|
+
"reason": ", ".join(reasons[:3]) or "simple prompt",
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def should_call_llm(classification: dict[str, Any], prompt: str) -> bool:
|
|
276
|
+
mode = setting("CLAUDE_INTAKE_LLM", "auto").lower()
|
|
277
|
+
if mode in {"0", "false", "off", "none", "no"}:
|
|
278
|
+
return False
|
|
279
|
+
if mode in {"1", "true", "on", "always", "yes"}:
|
|
280
|
+
return bool(setting("OPENAI_API_KEY"))
|
|
281
|
+
if not setting("OPENAI_API_KEY"):
|
|
282
|
+
return False
|
|
283
|
+
if float(classification.get("confidence", 1.0)) < 0.78:
|
|
284
|
+
return True
|
|
285
|
+
lower = prompt.lower()
|
|
286
|
+
return "?" in prompt and has_any(
|
|
287
|
+
lower, ("сделай", "исправ", "надо", "план", "реализ", "fix", "add", "build")
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def extract_output_text(payload: dict[str, Any]) -> str:
|
|
292
|
+
if isinstance(payload.get("output_text"), str):
|
|
293
|
+
return payload["output_text"]
|
|
294
|
+
chunks: list[str] = []
|
|
295
|
+
for item in payload.get("output", []) or []:
|
|
296
|
+
if not isinstance(item, dict):
|
|
297
|
+
continue
|
|
298
|
+
for content in item.get("content", []) or []:
|
|
299
|
+
if isinstance(content, dict) and content.get("type") == "output_text":
|
|
300
|
+
text = content.get("text")
|
|
301
|
+
if isinstance(text, str):
|
|
302
|
+
chunks.append(text)
|
|
303
|
+
return "".join(chunks)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def llm_classify(prompt: str) -> dict[str, Any] | None:
|
|
307
|
+
api_key = setting("OPENAI_API_KEY")
|
|
308
|
+
if not api_key:
|
|
309
|
+
return None
|
|
310
|
+
model = setting("CLAUDE_INTAKE_MODEL", DEFAULT_MODEL)
|
|
311
|
+
try:
|
|
312
|
+
timeout = float(setting("CLAUDE_INTAKE_TIMEOUT", "2.0"))
|
|
313
|
+
except ValueError:
|
|
314
|
+
timeout = 2.0
|
|
315
|
+
|
|
316
|
+
instructions = (
|
|
317
|
+
"Classify a user prompt for an intake hook (Russian + English). "
|
|
318
|
+
"Return only compact JSON with keys: intent, should_edit, should_plan, "
|
|
319
|
+
"should_use_task_ledger, score_0_15, subagents_authorized, "
|
|
320
|
+
"confidence_0_1, one_sentence_reason. "
|
|
321
|
+
"Intent must be one of: question_only, analysis, planning, "
|
|
322
|
+
"implementation, continue, execute_approved_plan. "
|
|
323
|
+
"Subagents are authorized only when the user explicitly mentions "
|
|
324
|
+
"subagents, delegation, or parallel work."
|
|
325
|
+
)
|
|
326
|
+
payload = {
|
|
327
|
+
"model": model,
|
|
328
|
+
"instructions": instructions,
|
|
329
|
+
"input": normalize_prompt(prompt),
|
|
330
|
+
"max_output_tokens": 220,
|
|
331
|
+
"store": False,
|
|
332
|
+
}
|
|
333
|
+
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
|
|
334
|
+
req = request.Request(
|
|
335
|
+
API_URL,
|
|
336
|
+
data=data,
|
|
337
|
+
headers={
|
|
338
|
+
"Authorization": f"Bearer {api_key}",
|
|
339
|
+
"Content-Type": "application/json",
|
|
340
|
+
},
|
|
341
|
+
method="POST",
|
|
342
|
+
)
|
|
343
|
+
try:
|
|
344
|
+
with request.urlopen(req, timeout=timeout) as response:
|
|
345
|
+
body = response.read().decode("utf-8", errors="replace")
|
|
346
|
+
except (OSError, error.HTTPError, error.URLError, TimeoutError):
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
parsed = json.loads(body)
|
|
351
|
+
text = extract_output_text(parsed).strip()
|
|
352
|
+
if text.startswith("```"):
|
|
353
|
+
text = re.sub(r"^```(?:json)?\s*", "", text)
|
|
354
|
+
text = re.sub(r"\s*```$", "", text)
|
|
355
|
+
result = json.loads(text)
|
|
356
|
+
except (json.JSONDecodeError, TypeError, ValueError):
|
|
357
|
+
return None
|
|
358
|
+
if not isinstance(result, dict):
|
|
359
|
+
return None
|
|
360
|
+
return result
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def merged_classification(prompt: str) -> tuple[dict[str, Any], str]:
|
|
364
|
+
base = deterministic_classify(prompt)
|
|
365
|
+
if should_call_llm(base, prompt):
|
|
366
|
+
start = time.monotonic()
|
|
367
|
+
llm = llm_classify(prompt)
|
|
368
|
+
if llm:
|
|
369
|
+
elapsed_ms = int((time.monotonic() - start) * 1000)
|
|
370
|
+
return llm, f"llm:{elapsed_ms}ms"
|
|
371
|
+
return base, "deterministic"
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def context_from_classification(c: dict[str, Any], source: str) -> str:
|
|
375
|
+
intent = str(c.get("intent", "analysis"))
|
|
376
|
+
score = c.get("score_0_15", c.get("score", 0))
|
|
377
|
+
confidence = c.get("confidence_0_1", c.get("confidence", 0))
|
|
378
|
+
should_edit = bool(c.get("should_edit", False))
|
|
379
|
+
should_plan = bool(c.get("should_plan", False))
|
|
380
|
+
should_ledger = bool(c.get("should_use_task_ledger", False))
|
|
381
|
+
subagents = bool(c.get("subagents_authorized", False))
|
|
382
|
+
reason = str(c.get("one_sentence_reason", c.get("reason", "")))
|
|
383
|
+
|
|
384
|
+
guidance = [
|
|
385
|
+
f"Intake classifier ({source}): intent={intent}, score={score}, confidence={confidence}.",
|
|
386
|
+
f"Flags: should_edit={str(should_edit).lower()}, should_plan={str(should_plan).lower()}, task_ledger={str(should_ledger).lower()}, subagents_authorized={str(subagents).lower()}.",
|
|
387
|
+
]
|
|
388
|
+
if reason:
|
|
389
|
+
guidance.append(f"Reason: {reason}")
|
|
390
|
+
if intent == "question_only":
|
|
391
|
+
guidance.append(
|
|
392
|
+
"Answer the question directly. Do not edit files or run "
|
|
393
|
+
"implementation commands unless the user explicitly asks for work."
|
|
394
|
+
)
|
|
395
|
+
elif intent == "planning":
|
|
396
|
+
guidance.append(
|
|
397
|
+
"Do discovery and produce a plan. Do not implement yet unless "
|
|
398
|
+
"the user explicitly asks for implementation."
|
|
399
|
+
)
|
|
400
|
+
elif intent == "analysis":
|
|
401
|
+
guidance.append(
|
|
402
|
+
"Investigate enough to answer or plan. Avoid edits unless the "
|
|
403
|
+
"prompt clearly asks for a fix."
|
|
404
|
+
)
|
|
405
|
+
elif intent in {"implementation", "execute_approved_plan"}:
|
|
406
|
+
guidance.append(
|
|
407
|
+
"Proceed with implementation. Build a task ledger for multi-issue "
|
|
408
|
+
"work and verify before completion."
|
|
409
|
+
)
|
|
410
|
+
elif intent == "continue":
|
|
411
|
+
guidance.append(
|
|
412
|
+
"Continue the prior task using the current context; recover the "
|
|
413
|
+
"last known state first if needed."
|
|
414
|
+
)
|
|
415
|
+
if not subagents:
|
|
416
|
+
guidance.append(
|
|
417
|
+
"Do not spawn subagents unless the user explicitly authorizes "
|
|
418
|
+
"delegation, parallel work, or subagents."
|
|
419
|
+
)
|
|
420
|
+
return " ".join(guidance)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def main() -> int:
|
|
424
|
+
if disabled():
|
|
425
|
+
return 0
|
|
426
|
+
|
|
427
|
+
try:
|
|
428
|
+
payload = json.load(sys.stdin)
|
|
429
|
+
except json.JSONDecodeError:
|
|
430
|
+
return 0
|
|
431
|
+
|
|
432
|
+
event = payload.get("hook_event_name")
|
|
433
|
+
if event and event != "UserPromptSubmit":
|
|
434
|
+
return 0
|
|
435
|
+
|
|
436
|
+
prompt = payload.get("prompt")
|
|
437
|
+
if not isinstance(prompt, str) or not prompt.strip():
|
|
438
|
+
return 0
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
classification, source = merged_classification(prompt)
|
|
442
|
+
emit_context(context_from_classification(classification, source))
|
|
443
|
+
except Exception:
|
|
444
|
+
return 0
|
|
445
|
+
return 0
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
if __name__ == "__main__":
|
|
449
|
+
raise SystemExit(main())
|