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,260 @@
|
|
|
1
|
+
# samber/lo — Generic Collection Utilities
|
|
2
|
+
|
|
3
|
+
> Reference document for go-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
> Upstream: https://github.com/samber/lo
|
|
5
|
+
|
|
6
|
+
## What It Is
|
|
7
|
+
|
|
8
|
+
500+ generic helpers for slices, maps, channels, strings, and tuples. Shaped like Lodash / Ramda, but generics-native (Go 1.18+). Not a replacement for the stdlib `slices` (Go 1.21+) and `maps` packages — it layers on top where stdlib is missing something or where readability wins.
|
|
9
|
+
|
|
10
|
+
## High-Frequency Functions
|
|
11
|
+
|
|
12
|
+
### Transform
|
|
13
|
+
|
|
14
|
+
```go
|
|
15
|
+
import "github.com/samber/lo"
|
|
16
|
+
|
|
17
|
+
// Map: T -> U
|
|
18
|
+
names := lo.Map(users, func(u User, _ int) string {
|
|
19
|
+
return u.Name
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Filter: keep elements matching predicate
|
|
23
|
+
active := lo.Filter(users, func(u User, _ int) bool {
|
|
24
|
+
return u.Status == StatusActive
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// FilterMap: filter + map in one pass (no intermediate slice)
|
|
28
|
+
emails := lo.FilterMap(users, func(u User, _ int) (string, bool) {
|
|
29
|
+
if u.Email == "" {
|
|
30
|
+
return "", false
|
|
31
|
+
}
|
|
32
|
+
return strings.ToLower(u.Email), true
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// FlatMap: T -> []U, then flatten
|
|
36
|
+
tagPairs := lo.FlatMap(posts, func(p Post, _ int) []string {
|
|
37
|
+
return p.Tags
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// ForEach: side-effect iteration (no return)
|
|
41
|
+
lo.ForEach(users, func(u User, _ int) {
|
|
42
|
+
log.Printf("user: %s", u.Name)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Reduce
|
|
46
|
+
total := lo.Reduce(prices, func(acc float64, p Price, _ int) float64 {
|
|
47
|
+
return acc + p.Amount
|
|
48
|
+
}, 0.0)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Dedup / Partition
|
|
52
|
+
|
|
53
|
+
```go
|
|
54
|
+
// Uniq — preserves order
|
|
55
|
+
ids := lo.Uniq([]int{1, 2, 2, 3, 1}) // [1 2 3]
|
|
56
|
+
|
|
57
|
+
// UniqBy — dedupe by key
|
|
58
|
+
users := lo.UniqBy(allUsers, func(u User) int64 { return u.ID })
|
|
59
|
+
|
|
60
|
+
// Chunk — split into fixed-size batches (last chunk may be shorter)
|
|
61
|
+
batches := lo.Chunk(items, 100) // [][]Item
|
|
62
|
+
|
|
63
|
+
// Partition — split by predicate into (matched, rest)
|
|
64
|
+
admins, others := lo.FilterReject(users, func(u User, _ int) bool {
|
|
65
|
+
return u.Role == RoleAdmin
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Index / Group
|
|
70
|
+
|
|
71
|
+
```go
|
|
72
|
+
// GroupBy — map[K][]V
|
|
73
|
+
byStatus := lo.GroupBy(users, func(u User) Status { return u.Status })
|
|
74
|
+
|
|
75
|
+
// KeyBy — map[K]V (later wins on collision; use UniqBy first if that matters)
|
|
76
|
+
byID := lo.KeyBy(users, func(u User) int64 { return u.ID })
|
|
77
|
+
|
|
78
|
+
// Associate — map[K]V via (key, value) callback
|
|
79
|
+
nameToEmail := lo.Associate(users, func(u User) (string, string) {
|
|
80
|
+
return u.Name, u.Email
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Control Flow
|
|
85
|
+
|
|
86
|
+
```go
|
|
87
|
+
// Ternary — expression-form conditional
|
|
88
|
+
status := lo.Ternary(isActive, "active", "inactive")
|
|
89
|
+
|
|
90
|
+
// If / ElseIf / Else — chainable conditional
|
|
91
|
+
label := lo.If(age < 13, "child").
|
|
92
|
+
ElseIf(age < 20, "teen").
|
|
93
|
+
ElseIf(age < 65, "adult").
|
|
94
|
+
Else("senior")
|
|
95
|
+
|
|
96
|
+
// Coalesce — first non-zero value
|
|
97
|
+
name := lo.Coalesce(u.DisplayName, u.Username, "anonymous")
|
|
98
|
+
|
|
99
|
+
// CoalesceOrEmpty — same but returns zero value instead of bool
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Error Ergonomics
|
|
103
|
+
|
|
104
|
+
```go
|
|
105
|
+
// Must — panic on error. init/main only; never in request handlers.
|
|
106
|
+
cfg := lo.Must(loadConfig())
|
|
107
|
+
|
|
108
|
+
// Must0 — for funcs returning only error: `lo.Must0(db.Ping())`
|
|
109
|
+
|
|
110
|
+
// Try — run a fn, catch panic, return (value, error)
|
|
111
|
+
value, err := lo.Try(func() error {
|
|
112
|
+
doSomethingThatMightPanic()
|
|
113
|
+
return nil
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Attempt — retry up to N times or until fn returns nil error
|
|
117
|
+
iter, err := lo.Attempt(3, func(i int) error {
|
|
118
|
+
return callFlakeyAPI()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// AttemptWithDelay / AttemptWhile for backoff + predicate-controlled loops
|
|
122
|
+
|
|
123
|
+
// TryWithErrorValue — recover panic payload as `any` (not just error)
|
|
124
|
+
value, errOrAny := lo.TryWithErrorValue(func() error { ... })
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## When to Prefer Stdlib
|
|
128
|
+
|
|
129
|
+
Go 1.21 shipped `slices` and `maps` packages covering the common cases. Prefer stdlib when:
|
|
130
|
+
|
|
131
|
+
| Task | Stdlib | samber/lo |
|
|
132
|
+
|------|--------|-----------|
|
|
133
|
+
| Find index | `slices.Index(s, v)` | `lo.IndexOf(s, v)` |
|
|
134
|
+
| Contains | `slices.Contains(s, v)` | `lo.Contains(s, v)` |
|
|
135
|
+
| Reverse (in-place) | `slices.Reverse(s)` | `lo.Reverse(s)` (copies) |
|
|
136
|
+
| Sort | `slices.Sort(s)` / `SortFunc` | `lo.Sort` — don't use |
|
|
137
|
+
| Delete at index | `slices.Delete(s, i, j)` | not equivalent |
|
|
138
|
+
| Binary search | `slices.BinarySearch` | no equivalent |
|
|
139
|
+
| Get map keys | `maps.Keys(m)` (iter.Seq) | `lo.Keys(m)` (slice) |
|
|
140
|
+
| Get map values | `maps.Values(m)` (iter.Seq) | `lo.Values(m)` (slice) |
|
|
141
|
+
|
|
142
|
+
Rule of thumb: if stdlib has it, use stdlib. Use lo for transforms (`Map`/`Filter`/`Reduce`), grouping (`GroupBy`/`KeyBy`), and control flow helpers that stdlib doesn't have.
|
|
143
|
+
|
|
144
|
+
## Performance Caveats
|
|
145
|
+
|
|
146
|
+
`lo.Map` allocates a new slice sized to `len(input)` — fine. But chained calls allocate an intermediate slice at every step:
|
|
147
|
+
|
|
148
|
+
```go
|
|
149
|
+
// 3 allocations: filtered, mapped, uniqued
|
|
150
|
+
result := lo.Uniq(lo.Map(lo.Filter(xs, keep), transform))
|
|
151
|
+
|
|
152
|
+
// 1 allocation with FilterMap
|
|
153
|
+
result := lo.FilterMap(xs, func(x T, _ int) (U, bool) {
|
|
154
|
+
if !keep(x, 0) { return *new(U), false }
|
|
155
|
+
return transform(x, 0), true
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`lo.Map` takes `func(T, int) U` — that closure can escape to the heap if it captures a variable. For hot paths, benchmark against a manual loop:
|
|
160
|
+
|
|
161
|
+
```go
|
|
162
|
+
names := make([]string, 0, len(users))
|
|
163
|
+
for _, u := range users {
|
|
164
|
+
names = append(names, u.Name)
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For 3-element slices, the loop is faster and clearer than `lo.Map`. For 10k+ elements, both perform comparably — readability wins.
|
|
169
|
+
|
|
170
|
+
`lo.Contains` is O(n) — if you do it in a loop, convert the slice to a map once: `seen := lo.Associate(xs, func(x T) (T, struct{}) { return x, struct{}{} })`.
|
|
171
|
+
|
|
172
|
+
## Parallel Variants (lo/parallel subpackage)
|
|
173
|
+
|
|
174
|
+
```go
|
|
175
|
+
import "github.com/samber/lo/parallel"
|
|
176
|
+
|
|
177
|
+
// Fan out Map across all workers (runtime.NumCPU by default)
|
|
178
|
+
results := parallel.Map(urls, func(url string, _ int) *Response {
|
|
179
|
+
return fetch(url) // blocking I/O
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use when the callback is I/O-bound (HTTP, DB). For CPU-bound work, the overhead usually isn't worth it unless `len(input) > 1000`. `parallel.Map` does not preserve order of execution but preserves order of the output slice. It does NOT bound the goroutine count — for 100k elements, build your own worker pool.
|
|
184
|
+
|
|
185
|
+
## Anti-Patterns
|
|
186
|
+
|
|
187
|
+
### Using lo.Must in Request Handlers
|
|
188
|
+
|
|
189
|
+
```go
|
|
190
|
+
// WRONG — panics the whole server process
|
|
191
|
+
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
|
192
|
+
user := lo.Must(h.repo.Find(r.Context(), id))
|
|
193
|
+
// ...
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// RIGHT
|
|
197
|
+
user, err := h.repo.Find(r.Context(), id)
|
|
198
|
+
if err != nil {
|
|
199
|
+
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`lo.Must` is for `init()`, `main()`, and test helpers. Nowhere else.
|
|
205
|
+
|
|
206
|
+
### Using lo for Trivial Cases
|
|
207
|
+
|
|
208
|
+
```go
|
|
209
|
+
// WRONG — less clear than a 3-line loop
|
|
210
|
+
isUnique := lo.Uniq([]int{a, b, c})
|
|
211
|
+
if len(isUnique) == 3 { ... }
|
|
212
|
+
|
|
213
|
+
// RIGHT
|
|
214
|
+
if a != b && b != c && a != c { ... }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### lo.ForEach When You Don't Need It
|
|
218
|
+
|
|
219
|
+
```go
|
|
220
|
+
// Noise — for _, x := range is clearer
|
|
221
|
+
lo.ForEach(users, func(u User, _ int) {
|
|
222
|
+
log.Printf("%s", u.Name)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// Prefer
|
|
226
|
+
for _, u := range users {
|
|
227
|
+
log.Printf("%s", u.Name)
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`ForEach` is useful in expression position (inside a builder) — not as a replacement for `for range`.
|
|
232
|
+
|
|
233
|
+
### Chaining With Shared Closure State
|
|
234
|
+
|
|
235
|
+
```go
|
|
236
|
+
// Risky — closure captures `total` which is shared across goroutines if parallel variant
|
|
237
|
+
var total int
|
|
238
|
+
lo.ForEach(xs, func(x int, _ int) { total += x })
|
|
239
|
+
|
|
240
|
+
// Prefer reduce for clarity and purity
|
|
241
|
+
total := lo.Reduce(xs, func(a, x, _ int) int { return a + x }, 0)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Review Checklist
|
|
245
|
+
|
|
246
|
+
When reviewing code using samber/lo, flag:
|
|
247
|
+
|
|
248
|
+
- **CRITICAL** — `lo.Must` / `lo.Must0` in request handlers, middleware, or goroutines that don't own the process (will panic the server)
|
|
249
|
+
- **WARNING** — Chained `Filter` → `Map` → `Uniq` on large inputs (replace with `FilterMap` + `Uniq`, or a single loop)
|
|
250
|
+
- **WARNING** — `parallel.Map` over unbounded input (goroutine explosion — use a bounded worker pool)
|
|
251
|
+
- **WARNING** — `lo.Contains` in a loop (O(n*m); convert outer set to map once)
|
|
252
|
+
- **SUGGESTION** — Using lo where stdlib `slices` / `maps` covers it (slices.Contains, slices.Index, etc.)
|
|
253
|
+
- **SUGGESTION** — 3-line loop rewritten as lo chain for no readability gain
|
|
254
|
+
|
|
255
|
+
## Further Reading
|
|
256
|
+
|
|
257
|
+
- Upstream docs: https://github.com/samber/lo (README lists all 500+ helpers)
|
|
258
|
+
- stdlib `slices`: https://pkg.go.dev/slices
|
|
259
|
+
- stdlib `maps`: https://pkg.go.dev/maps
|
|
260
|
+
- Parallel subpackage: https://github.com/samber/lo/tree/master/parallel
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# samber/oops — Structured Errors with Stack Traces
|
|
2
|
+
|
|
3
|
+
> Reference document for go-reviewer, go-error-reviewer. Loaded on demand via Read tool.
|
|
4
|
+
> Upstream: https://github.com/samber/oops
|
|
5
|
+
|
|
6
|
+
## What Stdlib Errors Don't Give You
|
|
7
|
+
|
|
8
|
+
`errors.New` + `fmt.Errorf("ctx: %w", err)` get you:
|
|
9
|
+
|
|
10
|
+
- An error message
|
|
11
|
+
- An unwrap chain (`errors.Is` / `errors.As`)
|
|
12
|
+
|
|
13
|
+
That's it. You don't get: stack traces, domain codes, request IDs, user-facing hints, owner/team routing, or structured key-value context. Every observability backend (Sentry, Datadog, New Relic) wants those. `samber/oops` adds them via a builder API that composes with `errors.Is` and `errors.As` so you don't lose stdlib interop.
|
|
14
|
+
|
|
15
|
+
## Core API
|
|
16
|
+
|
|
17
|
+
```go
|
|
18
|
+
import "github.com/samber/oops"
|
|
19
|
+
|
|
20
|
+
// Create
|
|
21
|
+
err := oops.
|
|
22
|
+
Code("USER_NOT_FOUND").
|
|
23
|
+
In("UserService.FindByID").
|
|
24
|
+
With("user_id", userID).
|
|
25
|
+
Errorf("user %d not found", userID)
|
|
26
|
+
|
|
27
|
+
// Wrap — adds attributes to an existing error
|
|
28
|
+
err := oops.
|
|
29
|
+
In("OrderService.Create").
|
|
30
|
+
With("order_id", orderID).
|
|
31
|
+
User(currentUser.ID, "email", currentUser.Email).
|
|
32
|
+
Wrap(repoErr)
|
|
33
|
+
|
|
34
|
+
// Wrap with a fresh message
|
|
35
|
+
err := oops.
|
|
36
|
+
Trace(traceID).
|
|
37
|
+
Wrapf(repoErr, "could not persist order %d", orderID)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
All builder methods return an `oops.OopsErrorBuilder` — chain freely, call `Errorf`/`Wrap`/`Wrapf`/`New` last.
|
|
41
|
+
|
|
42
|
+
## Attribute Methods (highlights)
|
|
43
|
+
|
|
44
|
+
| Method | Purpose | APM field it usually maps to |
|
|
45
|
+
|--------|---------|-----------------------------|
|
|
46
|
+
| `.Code("USER_NOT_FOUND")` | Stable machine-readable error key | `error.code` |
|
|
47
|
+
| `.In("pkg.Service.Method")` | Domain / subsystem the error came from | `error.domain` |
|
|
48
|
+
| `.Tags("auth", "critical")` | Free-form labels | `error.tags` |
|
|
49
|
+
| `.With("key", value)` | Arbitrary structured context | `error.context.*` |
|
|
50
|
+
| `.User(id, "email", x)` | User identity attached to error | `user.id`, `user.email` |
|
|
51
|
+
| `.Tenant(id, "name", x)` | Tenant / org identity | `tenant.id` |
|
|
52
|
+
| `.Request(req)` | Capture `*http.Request` (method, URL, headers) | `http.*` |
|
|
53
|
+
| `.Trace(traceID)` | Distributed tracing correlation | `trace.id` |
|
|
54
|
+
| `.Hint("rotate API key")` | Operator-facing remediation hint | `error.hint` |
|
|
55
|
+
| `.Public("Account not found")` | User-safe message for HTTP body | `error.public_message` |
|
|
56
|
+
| `.Owner("payments-team")` | Team/owner routing | `error.owner` |
|
|
57
|
+
| `.Duration(elapsed)` | How long the failing op ran | `error.duration_ms` |
|
|
58
|
+
| `.Time(t)` | Override capture timestamp | `error.timestamp` |
|
|
59
|
+
|
|
60
|
+
## Stack Traces
|
|
61
|
+
|
|
62
|
+
Stack is captured automatically at the *first* `Errorf`/`Wrap`/`Wrapf`/`New` in a chain — not at every wrap. Depth is capped by `oops.StackTraceMaxDepth` (default 10 frames); set to 0 to disable:
|
|
63
|
+
|
|
64
|
+
```go
|
|
65
|
+
oops.StackTraceMaxDepth = 0 // disable for hot paths if profiling shows cost
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Recover frames via `oopsErr.Stacktrace()` (returns `oops.OopsStackTrace`, which implements `fmt.Formatter`).
|
|
69
|
+
|
|
70
|
+
## Extraction for Logging / APM
|
|
71
|
+
|
|
72
|
+
```go
|
|
73
|
+
import (
|
|
74
|
+
"errors"
|
|
75
|
+
"log/slog"
|
|
76
|
+
"github.com/samber/oops"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
func logError(err error) {
|
|
80
|
+
var oe oops.OopsError
|
|
81
|
+
if errors.As(err, &oe) {
|
|
82
|
+
slog.Error("operation failed",
|
|
83
|
+
"code", oe.Code(),
|
|
84
|
+
"domain", oe.Domain(),
|
|
85
|
+
"tags", oe.Tags(),
|
|
86
|
+
"context", oe.Context(),
|
|
87
|
+
"hint", oe.Hint(),
|
|
88
|
+
"owner", oe.Owner(),
|
|
89
|
+
"user", oe.User(),
|
|
90
|
+
"trace_id", oe.Trace(),
|
|
91
|
+
"stacktrace", oe.Stacktrace(),
|
|
92
|
+
"error", err,
|
|
93
|
+
)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
slog.Error("operation failed", "error", err)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Or use the built-in slog attrs helper:
|
|
100
|
+
slog.Error("operation failed", slog.Any("error", oops.ToMap(err)))
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`oops.ToMap(err)` walks the full chain and merges attributes (innermost wins on key conflict — but make keys unique anyway).
|
|
104
|
+
|
|
105
|
+
## HTTP Boundary: Public vs Private
|
|
106
|
+
|
|
107
|
+
Never expose internal error messages over HTTP. Use `.Public(msg)` to attach a user-facing string, keep the verbose message private:
|
|
108
|
+
|
|
109
|
+
```go
|
|
110
|
+
err := oops.
|
|
111
|
+
Code("RATE_LIMIT").
|
|
112
|
+
Public("Too many requests. Try again in a minute.").
|
|
113
|
+
With("ip", clientIP).
|
|
114
|
+
With("window", "60s").
|
|
115
|
+
New("rate limit hit: %d req/min from %s", reqCount, clientIP)
|
|
116
|
+
|
|
117
|
+
// In middleware:
|
|
118
|
+
var oe oops.OopsError
|
|
119
|
+
if errors.As(err, &oe) {
|
|
120
|
+
http.Error(w, oe.Public(), http.StatusTooManyRequests) // safe message
|
|
121
|
+
logError(err) // internal detail
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## errors.Is / errors.As Compatibility
|
|
128
|
+
|
|
129
|
+
`oops.Wrap` preserves the chain; stdlib inspection keeps working.
|
|
130
|
+
|
|
131
|
+
```go
|
|
132
|
+
sentinel := errors.New("not found")
|
|
133
|
+
err := oops.Code("USER_NOT_FOUND").Wrap(sentinel)
|
|
134
|
+
|
|
135
|
+
errors.Is(err, sentinel) // true
|
|
136
|
+
errors.As(err, &oops.OopsError{}) // true — extract attributes
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Match on `Code` for domain logic, `errors.Is` for sentinel identity. Don't match on message text — that's fragile across wraps.
|
|
140
|
+
|
|
141
|
+
```go
|
|
142
|
+
// Match on code — stable
|
|
143
|
+
if oops.GetCode(err) == "USER_NOT_FOUND" {
|
|
144
|
+
return http.StatusNotFound
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Match on sentinel — stable
|
|
148
|
+
if errors.Is(err, domain.ErrNotFound) {
|
|
149
|
+
return http.StatusNotFound
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Comparison: stdlib vs pkg/errors vs samber/oops
|
|
154
|
+
|
|
155
|
+
| Feature | stdlib `errors` | `pkg/errors` | `samber/oops` |
|
|
156
|
+
|---------|-----------------|--------------|---------------|
|
|
157
|
+
| Wrap + message | `fmt.Errorf %w` | `errors.Wrap` | `.Wrapf` / `.Wrap` |
|
|
158
|
+
| Stack trace | No | Yes (always-on) | Yes (configurable) |
|
|
159
|
+
| Structured context (`key=value`) | No | No | Yes (`.With`) |
|
|
160
|
+
| Domain code | Sentinels only | Sentinels only | Yes (`.Code`) |
|
|
161
|
+
| User-safe message | Manual | Manual | Yes (`.Public`) |
|
|
162
|
+
| User / tenant attribution | No | No | Yes (`.User`, `.Tenant`) |
|
|
163
|
+
| APM-shape serialization | Manual | Manual | `oops.ToMap` |
|
|
164
|
+
| Maintenance status | Stdlib | Archived upstream | Active |
|
|
165
|
+
|
|
166
|
+
`pkg/errors` is archived; new code should use stdlib or oops, not pkg/errors.
|
|
167
|
+
|
|
168
|
+
## When NOT to Use oops
|
|
169
|
+
|
|
170
|
+
- **Library code with a public API** — don't force the oops dependency on consumers. Return stdlib errors; let callers add context.
|
|
171
|
+
- **Small internal tools** — `fmt.Errorf("ctx: %w", err)` is enough.
|
|
172
|
+
- **Inside a hot loop** — builder allocates; stack capture is non-trivial. Measure first.
|
|
173
|
+
- **When a sentinel + `errors.Is` is cleaner** — oops complements sentinels, doesn't replace them.
|
|
174
|
+
|
|
175
|
+
## Common Pitfalls
|
|
176
|
+
|
|
177
|
+
| Pitfall | Symptom | Fix |
|
|
178
|
+
|---------|---------|-----|
|
|
179
|
+
| Wrapping an oops error with oops again | Duplicate stack traces, attribute shadowing | Wrap once at the deepest point; add attributes elsewhere |
|
|
180
|
+
| Returning `nil` from a builder by accident | Chain like `.Code("X").With(...)` with no terminator leaks an `*OopsErrorBuilder` into the return | Always end with `.Errorf` / `.Wrap` / `.New` |
|
|
181
|
+
| Sending `.Public()` absent | HTTP response leaks internal detail | Default-deny at the HTTP boundary — if `.Public()` is empty, use a generic string |
|
|
182
|
+
| Expecting `errors.Is` to match on `Code` | `Code` is a separate attribute, not a sentinel | Use `oops.GetCode(err)` |
|
|
183
|
+
| Relying on stack trace after serializing over the wire | Stacks don't survive JSON → new process | Log at the origin; pass only structured context over the wire |
|
|
184
|
+
|
|
185
|
+
## Review Checklist
|
|
186
|
+
|
|
187
|
+
When reviewing code using samber/oops, flag:
|
|
188
|
+
|
|
189
|
+
- **CRITICAL** — Internal error returned directly to HTTP response without `.Public()` (info leak)
|
|
190
|
+
- **CRITICAL** — `fmt.Sprintf` with user input used as `.With(key, userInput)` where `key` is dynamic (cardinality explosion in APM)
|
|
191
|
+
- **WARNING** — `oops` used in library code exported to other modules (forces dep on consumers)
|
|
192
|
+
- **WARNING** — Double-wrapping: `oops.Wrap` applied to an already-oops error at every layer (noise + duplicate stacks)
|
|
193
|
+
- **WARNING** — `.Code` values not stable / not centralized (string drift across files)
|
|
194
|
+
- **SUGGESTION** — Consider `.Owner("team")` for multi-team projects so APM routes alerts correctly
|
|
195
|
+
|
|
196
|
+
## Further Reading
|
|
197
|
+
|
|
198
|
+
- Upstream docs: https://github.com/samber/oops
|
|
199
|
+
- `errors` stdlib deep dive: `packages/stack-agents/go/references/error-inspection.md`
|
|
200
|
+
- Wrapping conventions: `packages/stack-agents/go/references/error-wrapping.md`
|