exonous 0.0.0-stage → 2.10.4
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/README.md +100 -2
- package/assets/THIRD-PARTY-NOTICES.md +29 -0
- package/assets/adapters/opencode/exonous.js +54 -0
- package/assets/agents/exo-backend.md +22 -0
- package/assets/agents/exo-cto.md +22 -0
- package/assets/agents/exo-data-ai.md +22 -0
- package/assets/agents/exo-designer.md +22 -0
- package/assets/agents/exo-devops.md +22 -0
- package/assets/agents/exo-frontend.md +22 -0
- package/assets/agents/exo-infra.md +22 -0
- package/assets/agents/exo-mobile.md +22 -0
- package/assets/agents/exo-pm.md +22 -0
- package/assets/agents/exo-qa.md +22 -0
- package/assets/agents/exo-researcher.md +22 -0
- package/assets/agents/exo-security.md +22 -0
- package/assets/agents/roster.json +209 -0
- package/assets/agents/taxis.js +189 -0
- package/assets/editors/cursor/frontend-skills.mdc +27 -0
- package/assets/editors/hooks/cursor/hooks.json +11 -0
- package/assets/machine/PR-TEMPLATE.md +21 -0
- package/assets/machine/hooks/anamnesis.cjs +61 -0
- package/assets/machine/hooks/ethos.cjs +48 -0
- package/assets/machine/hooks/kairos.cjs +46 -0
- package/assets/machine/hooks/lib/cli-options.cjs +133 -0
- package/assets/machine/hooks/lib/memory.cjs +488 -0
- package/assets/machine/hooks/lib/metron.cjs +52 -0
- package/assets/machine/hooks/lib/paths.cjs +99 -0
- package/assets/machine/hooks/lib/payload.cjs +81 -0
- package/assets/machine/hooks/lib/pr-template-validate.cjs +103 -0
- package/assets/machine/hooks/lib/release.cjs +58 -0
- package/assets/machine/hooks/lib/repo.cjs +138 -0
- package/assets/machine/hooks/lib/sanitize.cjs +37 -0
- package/assets/machine/hooks/lib/selfupdate.cjs +354 -0
- package/assets/machine/hooks/lib/shell.cjs +722 -0
- package/assets/machine/hooks/lib/template.cjs +70 -0
- package/assets/machine/hooks/lib/token.cjs +110 -0
- package/assets/machine/hooks/lib/validate-pr-body.cjs +21 -0
- package/assets/machine/hooks/pyle.cjs +302 -0
- package/assets/rules/nomos.md +59 -0
- package/assets/rules/praxis.md +38 -0
- package/assets/skills/code-review/SKILL.md +124 -0
- package/assets/skills/commit-changes/SKILL.md +49 -0
- package/assets/skills/deliver/SKILL.md +114 -0
- package/assets/skills/deliver/references/parallel.md +37 -0
- package/assets/skills/doc/SKILL.md +140 -0
- package/assets/skills/doc/doc.mjs +277 -0
- package/assets/skills/issue-board/SKILL.md +84 -0
- package/assets/skills/issue-board/board.mjs +389 -0
- package/assets/skills/issue-board/config.example.json +9 -0
- package/assets/skills/issue-board/render.py +189 -0
- package/assets/skills/make-pr/SKILL.md +24 -0
- package/assets/skills/native-ui/SKILL.md +67 -0
- package/assets/skills/native-ui/reference/apple.md +180 -0
- package/assets/skills/native-ui/reference/mobile-feel.md +221 -0
- package/assets/skills/native-ui/reference/swift.md +289 -0
- package/assets/skills/project-memory/SKILL.md +59 -0
- package/assets/skills/project-memory/config.example.json +4 -0
- package/assets/skills/project-memory/project-memory.mjs +478 -0
- package/assets/skills/security-review/SKILL.md +102 -0
- package/assets/skills/session-handoff/SKILL.md +23 -0
- package/assets/skills/session-handoff/config.example.json +3 -0
- package/assets/skills/ui-design/SKILL.md +75 -0
- package/assets/skills/ui-design/reference/adapt.md +151 -0
- package/assets/skills/ui-design/reference/adapt.native.md +35 -0
- package/assets/skills/ui-design/reference/android.md +50 -0
- package/assets/skills/ui-design/reference/animate.md +87 -0
- package/assets/skills/ui-design/reference/audit.md +76 -0
- package/assets/skills/ui-design/reference/audit.native.md +126 -0
- package/assets/skills/ui-design/reference/bolder.md +33 -0
- package/assets/skills/ui-design/reference/clarify.md +84 -0
- package/assets/skills/ui-design/reference/colorize.md +86 -0
- package/assets/skills/ui-design/reference/craft-floor.md +25 -0
- package/assets/skills/ui-design/reference/critique.md +345 -0
- package/assets/skills/ui-design/reference/degraded/asset-producer.md +35 -0
- package/assets/skills/ui-design/reference/degraded/documenter.md +23 -0
- package/assets/skills/ui-design/reference/degraded/finish-reviewer.md +43 -0
- package/assets/skills/ui-design/reference/degraded/manual-edit-applier.md +56 -0
- package/assets/skills/ui-design/reference/delight.md +63 -0
- package/assets/skills/ui-design/reference/distill.md +98 -0
- package/assets/skills/ui-design/reference/doctor.md +49 -0
- package/assets/skills/ui-design/reference/document.md +273 -0
- package/assets/skills/ui-design/reference/extract.md +52 -0
- package/assets/skills/ui-design/reference/harden.md +122 -0
- package/assets/skills/ui-design/reference/hooks.md +114 -0
- package/assets/skills/ui-design/reference/init.md +47 -0
- package/assets/skills/ui-design/reference/ios.md +55 -0
- package/assets/skills/ui-design/reference/layout.md +81 -0
- package/assets/skills/ui-design/reference/live-setup.md +75 -0
- package/assets/skills/ui-design/reference/live.md +326 -0
- package/assets/skills/ui-design/reference/new-work.md +350 -0
- package/assets/skills/ui-design/reference/onboard.md +85 -0
- package/assets/skills/ui-design/reference/operate.md +29 -0
- package/assets/skills/ui-design/reference/optimize.md +101 -0
- package/assets/skills/ui-design/reference/overdrive.md +83 -0
- package/assets/skills/ui-design/reference/polish.md +77 -0
- package/assets/skills/ui-design/reference/quieter.md +95 -0
- package/assets/skills/ui-design/reference/routing.md +37 -0
- package/assets/skills/ui-design/reference/shape.md +43 -0
- package/assets/skills/ui-design/reference/styles/brutalist.md +45 -0
- package/assets/skills/ui-design/reference/styles/design-md.md +70 -0
- package/assets/skills/ui-design/reference/styles/direction.md +95 -0
- package/assets/skills/ui-design/reference/styles/full-output.md +42 -0
- package/assets/skills/ui-design/reference/styles/minimalist.md +64 -0
- package/assets/skills/ui-design/reference/styles/premium.md +65 -0
- package/assets/skills/ui-design/reference/styles/redesign.md +59 -0
- package/assets/skills/ui-design/reference/typeset.md +73 -0
- package/assets/skills/ui-design/reference/visualize.md +156 -0
- package/assets/skills/ui-design/scripts/ENGINE-LICENSE +191 -0
- package/assets/skills/ui-design/scripts/VERSION +1 -0
- package/assets/skills/ui-design/scripts/bin/darwin-arm64/impeccable +0 -0
- package/assets/skills/ui-design/scripts/command-metadata.json +94 -0
- package/assets/skills/ui-design/scripts/data/font-index-failures.json +121 -0
- package/assets/skills/ui-design/scripts/data/font-index.json +1 -0
- package/assets/skills/ui-design/scripts/impeccable +206 -0
- package/assets/skills/ui-design/scripts/impeccable.cmd +214 -0
- package/assets/skills/ui-design/scripts/live-browser-dom.js +167 -0
- package/assets/skills/ui-design/scripts/live-browser-ignores.js +242 -0
- package/assets/skills/ui-design/scripts/live-browser-session.js +144 -0
- package/assets/skills/ui-design/scripts/live-browser.js +12990 -0
- package/assets/skills/ui-design/scripts/modern-screenshot.umd.js +14 -0
- package/assets/skills/ui-design/scripts/ui-design +6 -0
- package/assets/skills/ui-design/scripts/ui-design.cmd +3 -0
- package/assets/skills/ui-design/scripts/ui-design.mjs +124 -0
- package/assets/skills/ui-mockup/SKILL.md +68 -0
- package/assets/skills/ui-mockup/reference/brandkit.md +101 -0
- package/assets/skills/ui-mockup/reference/image-to-code.md +83 -0
- package/assets/skills/ui-mockup/reference/mobile-comps.md +94 -0
- package/assets/skills/ui-mockup/reference/web-comps.md +83 -0
- package/assets/skills/ui-motion/SKILL.md +112 -0
- package/assets/skills/ui-motion/reference/expo.md +224 -0
- package/assets/skills/ui-motion/reference/opportunities.md +81 -0
- package/assets/skills/ui-motion/reference/prototype.md +238 -0
- package/assets/skills/ui-motion/reference/review.md +163 -0
- package/assets/skills/ui-motion/reference/toasts.md +144 -0
- package/assets/skills/ui-motion/reference/ui-libraries.md +71 -0
- package/assets/skills/ui-motion/reference/vocabulary.md +130 -0
- package/assets/skills/ui-motion/reference/web.md +294 -0
- package/assets/skills/ui-patterns/SKILL.md +105 -0
- package/assets/skills/ui-patterns/references/stacks/html-tailwind.md +40 -0
- package/assets/skills/ui-patterns/references/stacks/nextjs.md +52 -0
- package/assets/skills/ui-patterns/references/stacks/nuxt.md +100 -0
- package/assets/skills/ui-patterns/references/stacks/react-native.md +48 -0
- package/assets/skills/ui-patterns/references/stacks/react.md +49 -0
- package/assets/skills/ui-patterns/references/stacks/shadcn.md +57 -0
- package/assets/skills/ui-patterns/references/stacks/vue.md +37 -0
- package/assets/techne/.claude-plugin/marketplace.json +31 -0
- package/assets/techne/.claude-plugin/plugin.json +24 -0
- package/assets/techne/.mcp.json +8 -0
- package/assets/techne/commands/build-fix.md +48 -0
- package/assets/techne/commands/refactor-clean.md +46 -0
- package/assets/techne/commands/test-coverage.md +42 -0
- package/assets/techne/hooks/hooks.json +49 -0
- package/assets/techne/rules/angular/coding-style.md +49 -0
- package/assets/techne/rules/angular/hooks.md +23 -0
- package/assets/techne/rules/angular/patterns.md +48 -0
- package/assets/techne/rules/angular/security.md +26 -0
- package/assets/techne/rules/angular/testing.md +46 -0
- package/assets/techne/rules/arkts/coding-style.md +63 -0
- package/assets/techne/rules/arkts/hooks.md +38 -0
- package/assets/techne/rules/arkts/patterns.md +42 -0
- package/assets/techne/rules/arkts/security.md +35 -0
- package/assets/techne/rules/arkts/testing.md +39 -0
- package/assets/techne/rules/common/agents.md +6 -0
- package/assets/techne/rules/common/code-review.md +15 -0
- package/assets/techne/rules/common/coding-style.md +13 -0
- package/assets/techne/rules/common/development-workflow.md +5 -0
- package/assets/techne/rules/common/git-workflow.md +30 -0
- package/assets/techne/rules/common/hooks.md +7 -0
- package/assets/techne/rules/common/patterns.md +3 -0
- package/assets/techne/rules/common/performance.md +3 -0
- package/assets/techne/rules/common/security.md +5 -0
- package/assets/techne/rules/common/testing.md +3 -0
- package/assets/techne/rules/cpp/coding-style.md +31 -0
- package/assets/techne/rules/cpp/hooks.md +26 -0
- package/assets/techne/rules/cpp/patterns.md +31 -0
- package/assets/techne/rules/cpp/security.md +37 -0
- package/assets/techne/rules/cpp/testing.md +31 -0
- package/assets/techne/rules/csharp/coding-style.md +30 -0
- package/assets/techne/rules/csharp/hooks.md +24 -0
- package/assets/techne/rules/csharp/patterns.md +28 -0
- package/assets/techne/rules/csharp/security.md +32 -0
- package/assets/techne/rules/csharp/testing.md +27 -0
- package/assets/techne/rules/dart/coding-style.md +47 -0
- package/assets/techne/rules/dart/hooks.md +26 -0
- package/assets/techne/rules/dart/patterns.md +38 -0
- package/assets/techne/rules/dart/security.md +44 -0
- package/assets/techne/rules/dart/testing.md +39 -0
- package/assets/techne/rules/fsharp/coding-style.md +34 -0
- package/assets/techne/rules/fsharp/hooks.md +25 -0
- package/assets/techne/rules/fsharp/patterns.md +43 -0
- package/assets/techne/rules/fsharp/security.md +32 -0
- package/assets/techne/rules/fsharp/testing.md +27 -0
- package/assets/techne/rules/golang/coding-style.md +30 -0
- package/assets/techne/rules/golang/hooks.md +17 -0
- package/assets/techne/rules/golang/patterns.md +43 -0
- package/assets/techne/rules/golang/security.md +33 -0
- package/assets/techne/rules/golang/testing.md +20 -0
- package/assets/techne/rules/java/coding-style.md +51 -0
- package/assets/techne/rules/java/hooks.md +18 -0
- package/assets/techne/rules/java/patterns.md +55 -0
- package/assets/techne/rules/java/security.md +33 -0
- package/assets/techne/rules/java/testing.md +33 -0
- package/assets/techne/rules/kotlin/coding-style.md +50 -0
- package/assets/techne/rules/kotlin/hooks.md +17 -0
- package/assets/techne/rules/kotlin/patterns.md +44 -0
- package/assets/techne/rules/kotlin/security.md +34 -0
- package/assets/techne/rules/kotlin/testing.md +38 -0
- package/assets/techne/rules/nuxt/coding-style.md +20 -0
- package/assets/techne/rules/nuxt/hooks.md +16 -0
- package/assets/techne/rules/nuxt/patterns.md +46 -0
- package/assets/techne/rules/nuxt/security.md +19 -0
- package/assets/techne/rules/nuxt/testing.md +15 -0
- package/assets/techne/rules/perl/coding-style.md +33 -0
- package/assets/techne/rules/perl/hooks.md +22 -0
- package/assets/techne/rules/perl/patterns.md +43 -0
- package/assets/techne/rules/perl/security.md +39 -0
- package/assets/techne/rules/perl/testing.md +42 -0
- package/assets/techne/rules/php/coding-style.md +30 -0
- package/assets/techne/rules/php/hooks.md +24 -0
- package/assets/techne/rules/php/patterns.md +26 -0
- package/assets/techne/rules/php/security.md +27 -0
- package/assets/techne/rules/php/testing.md +28 -0
- package/assets/techne/rules/python/coding-style.md +38 -0
- package/assets/techne/rules/python/fastapi.md +57 -0
- package/assets/techne/rules/python/hooks.md +19 -0
- package/assets/techne/rules/python/patterns.md +37 -0
- package/assets/techne/rules/python/security.md +26 -0
- package/assets/techne/rules/python/testing.md +32 -0
- package/assets/techne/rules/react/coding-style.md +24 -0
- package/assets/techne/rules/react/hooks.md +62 -0
- package/assets/techne/rules/react/patterns.md +48 -0
- package/assets/techne/rules/react/security.md +45 -0
- package/assets/techne/rules/react/testing.md +42 -0
- package/assets/techne/rules/react-native/accessibility.md +14 -0
- package/assets/techne/rules/react-native/coding-style.md +16 -0
- package/assets/techne/rules/react-native/hooks.md +16 -0
- package/assets/techne/rules/react-native/patterns.md +18 -0
- package/assets/techne/rules/react-native/performance.md +14 -0
- package/assets/techne/rules/react-native/production-readiness.md +16 -0
- package/assets/techne/rules/react-native/security.md +16 -0
- package/assets/techne/rules/react-native/testing.md +14 -0
- package/assets/techne/rules/ruby/coding-style.md +40 -0
- package/assets/techne/rules/ruby/hooks.md +33 -0
- package/assets/techne/rules/ruby/patterns.md +33 -0
- package/assets/techne/rules/ruby/security.md +39 -0
- package/assets/techne/rules/ruby/testing.md +35 -0
- package/assets/techne/rules/rust/coding-style.md +41 -0
- package/assets/techne/rules/rust/hooks.md +16 -0
- package/assets/techne/rules/rust/patterns.md +42 -0
- package/assets/techne/rules/rust/security.md +33 -0
- package/assets/techne/rules/rust/testing.md +30 -0
- package/assets/techne/rules/swift/coding-style.md +30 -0
- package/assets/techne/rules/swift/hooks.md +20 -0
- package/assets/techne/rules/swift/patterns.md +30 -0
- package/assets/techne/rules/swift/security.md +22 -0
- package/assets/techne/rules/swift/testing.md +28 -0
- package/assets/techne/rules/typescript/coding-style.md +38 -0
- package/assets/techne/rules/typescript/hooks.md +14 -0
- package/assets/techne/rules/typescript/patterns.md +12 -0
- package/assets/techne/rules/typescript/security.md +14 -0
- package/assets/techne/rules/typescript/testing.md +12 -0
- package/assets/techne/rules/vue/coding-style.md +19 -0
- package/assets/techne/rules/vue/hooks.md +13 -0
- package/assets/techne/rules/vue/patterns.md +29 -0
- package/assets/techne/rules/vue/security.md +11 -0
- package/assets/techne/rules/vue/testing.md +11 -0
- package/assets/techne/rules/web/coding-style.md +25 -0
- package/assets/techne/rules/web/design-quality.md +21 -0
- package/assets/techne/rules/web/hooks.md +39 -0
- package/assets/techne/rules/web/patterns.md +23 -0
- package/assets/techne/rules/web/performance.md +25 -0
- package/assets/techne/rules/web/security.md +23 -0
- package/assets/techne/rules/web/testing.md +21 -0
- package/assets/techne/scripts/hooks/config-guard.js +50 -0
- package/assets/techne/scripts/hooks/edit-tracker.js +26 -0
- package/assets/techne/scripts/hooks/lib/shell.js +204 -0
- package/assets/techne/scripts/hooks/no-verify-guard.js +205 -0
- package/assets/techne/scripts/hooks/run.js +92 -0
- package/assets/techne/scripts/hooks/stop-checks.js +136 -0
- package/assets/techne/skills/angular/SKILL.md +34 -0
- package/assets/techne/skills/angular/reference/components-and-templates.md +63 -0
- package/assets/techne/skills/angular/reference/dependency-injection.md +44 -0
- package/assets/techne/skills/angular/reference/forms.md +97 -0
- package/assets/techne/skills/angular/reference/routing.md +50 -0
- package/assets/techne/skills/angular/reference/signals-and-state.md +59 -0
- package/assets/techne/skills/angular/reference/styling-motion-a11y.md +54 -0
- package/assets/techne/skills/angular/reference/testing.md +52 -0
- package/assets/techne/skills/angular/reference/tooling.md +31 -0
- package/assets/techne/skills/cpp/SKILL.md +27 -0
- package/assets/techne/skills/cpp/reference/coding-standards.md +160 -0
- package/assets/techne/skills/databases/SKILL.md +35 -0
- package/assets/techne/skills/databases/reference/clickhouse.md +102 -0
- package/assets/techne/skills/databases/reference/migrations.md +94 -0
- package/assets/techne/skills/databases/reference/mysql.md +101 -0
- package/assets/techne/skills/databases/reference/postgres.md +72 -0
- package/assets/techne/skills/databases/reference/prisma.md +105 -0
- package/assets/techne/skills/databases/reference/redis.md +108 -0
- package/assets/techne/skills/delivery/SKILL.md +35 -0
- package/assets/techne/skills/delivery/reference/deployment-patterns.md +58 -0
- package/assets/techne/skills/delivery/reference/docker-patterns.md +77 -0
- package/assets/techne/skills/delivery/reference/git-workflow.md +64 -0
- package/assets/techne/skills/delivery/reference/github-ops.md +67 -0
- package/assets/techne/skills/delivery/reference/jira-integration.md +53 -0
- package/assets/techne/skills/delivery/reference/kubernetes-patterns.md +67 -0
- package/assets/techne/skills/dotnet/SKILL.md +27 -0
- package/assets/techne/skills/dotnet/reference/dotnet-core.md +131 -0
- package/assets/techne/skills/flutter/SKILL.md +29 -0
- package/assets/techne/skills/flutter/reference/code-review.md +76 -0
- package/assets/techne/skills/flutter/reference/patterns.md +201 -0
- package/assets/techne/skills/go/SKILL.md +28 -0
- package/assets/techne/skills/go/reference/patterns.md +87 -0
- package/assets/techne/skills/go/reference/testing.md +142 -0
- package/assets/techne/skills/java/SKILL.md +29 -0
- package/assets/techne/skills/java/reference/java-standards.md +117 -0
- package/assets/techne/skills/java/reference/springboot.md +108 -0
- package/assets/techne/skills/kotlin/SKILL.md +35 -0
- package/assets/techne/skills/kotlin/reference/android-clean-architecture.md +115 -0
- package/assets/techne/skills/kotlin/reference/compose-multiplatform-patterns.md +113 -0
- package/assets/techne/skills/kotlin/reference/kotlin-coroutines-flows.md +93 -0
- package/assets/techne/skills/kotlin/reference/kotlin-patterns.md +74 -0
- package/assets/techne/skills/kotlin/reference/kotlin-testing.md +115 -0
- package/assets/techne/skills/ml/SKILL.md +34 -0
- package/assets/techne/skills/ml/reference/ai-regression-testing.md +99 -0
- package/assets/techne/skills/ml/reference/cost-aware-llm-pipeline.md +133 -0
- package/assets/techne/skills/ml/reference/eval-harness.md +94 -0
- package/assets/techne/skills/ml/reference/mle-workflow.md +177 -0
- package/assets/techne/skills/ml/reference/prompt-optimizer.md +107 -0
- package/assets/techne/skills/ml/reference/pytorch-patterns.md +164 -0
- package/assets/techne/skills/php/SKILL.md +27 -0
- package/assets/techne/skills/php/reference/laravel.md +142 -0
- package/assets/techne/skills/python/SKILL.md +33 -0
- package/assets/techne/skills/python/reference/django.md +104 -0
- package/assets/techne/skills/python/reference/fastapi.md +118 -0
- package/assets/techne/skills/python/reference/python-core.md +88 -0
- package/assets/techne/skills/python/reference/python-testing.md +105 -0
- package/assets/techne/skills/quality/SKILL.md +34 -0
- package/assets/techne/skills/quality/reference/benchmark.md +55 -0
- package/assets/techne/skills/quality/reference/browser-qa.md +60 -0
- package/assets/techne/skills/quality/reference/coding-standards.md +88 -0
- package/assets/techne/skills/quality/reference/e2e-testing.md +99 -0
- package/assets/techne/skills/quality/reference/tdd-workflow.md +89 -0
- package/assets/techne/skills/quality/reference/verification-loop.md +80 -0
- package/assets/techne/skills/react/SKILL.md +30 -0
- package/assets/techne/skills/react/reference/nextjs-turbopack.md +42 -0
- package/assets/techne/skills/react/reference/react-patterns.md +139 -0
- package/assets/techne/skills/react/reference/react-performance.md +101 -0
- package/assets/techne/skills/react/reference/react-testing.md +139 -0
- package/assets/techne/skills/react-native/SKILL.md +28 -0
- package/assets/techne/skills/react-native/reference/expo-patterns.md +196 -0
- package/assets/techne/skills/research/SKILL.md +35 -0
- package/assets/techne/skills/research/reference/architecture-decision-records.md +65 -0
- package/assets/techne/skills/research/reference/codebase-onboarding.md +44 -0
- package/assets/techne/skills/research/reference/deep-research.md +50 -0
- package/assets/techne/skills/research/reference/iterative-retrieval.md +62 -0
- package/assets/techne/skills/research/reference/product-lens.md +38 -0
- package/assets/techne/skills/research/reference/search-first.md +50 -0
- package/assets/techne/skills/rust/SKILL.md +28 -0
- package/assets/techne/skills/rust/reference/patterns.md +108 -0
- package/assets/techne/skills/rust/reference/testing.md +142 -0
- package/assets/techne/skills/services/SKILL.md +32 -0
- package/assets/techne/skills/services/reference/api-contracts.md +139 -0
- package/assets/techne/skills/services/reference/backend-layers.md +95 -0
- package/assets/techne/skills/services/reference/errors-and-resilience.md +146 -0
- package/assets/techne/skills/services/reference/hexagonal-boundaries.md +93 -0
- package/assets/techne/skills/services/reference/mcp-servers.md +50 -0
- package/assets/techne/skills/services/reference/nestjs.md +96 -0
- package/assets/techne/skills/swift/SKILL.md +29 -0
- package/assets/techne/skills/swift/reference/concurrency.md +83 -0
- package/assets/techne/skills/swift/reference/swiftui.md +139 -0
- package/assets/techne/skills/vue/SKILL.md +29 -0
- package/assets/techne/skills/vue/reference/nuxt4-patterns.md +90 -0
- package/assets/techne/skills/vue/reference/ui-to-vue.md +90 -0
- package/assets/techne/skills/vue/reference/vue-patterns.md +151 -0
- package/assets/techne/skills/web/SKILL.md +30 -0
- package/assets/techne/skills/web/reference/bun-runtime.md +55 -0
- package/assets/techne/skills/web/reference/frontend-patterns.md +54 -0
- package/assets/techne/skills/web/reference/seo.md +77 -0
- package/assets/techne/skills/web/reference/vite-patterns.md +96 -0
- package/bin/native.cjs +30 -0
- package/native/aarch64-apple-darwin/exonous +0 -0
- package/native/aarch64-unknown-linux-gnu/exonous +0 -0
- package/native/x86_64-apple-darwin/exonous +0 -0
- package/native/x86_64-pc-windows-msvc/exonous.exe +0 -0
- package/native/x86_64-unknown-linux-gnu/exonous +0 -0
- package/package.json +1 -6
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# .NET / C# core
|
|
2
|
+
|
|
3
|
+
## Immutability and nullability
|
|
4
|
+
|
|
5
|
+
```csharp
|
|
6
|
+
public sealed record Money(decimal Amount, string Currency);
|
|
7
|
+
|
|
8
|
+
public sealed class CreateOrderRequest
|
|
9
|
+
{
|
|
10
|
+
public required string CustomerId { get; init; }
|
|
11
|
+
public required IReadOnlyList<OrderLine> Lines { get; init; }
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Enable nullable reference types; express optional returns as `T?` or `Result<T>`.
|
|
16
|
+
|
|
17
|
+
## Dependency injection
|
|
18
|
+
|
|
19
|
+
```csharp
|
|
20
|
+
public interface IOrderRepository
|
|
21
|
+
{
|
|
22
|
+
Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken);
|
|
23
|
+
Task AddAsync(Order order, CancellationToken cancellationToken);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
|
|
27
|
+
builder.Services.AddScoped<IOrderService, OrderService>();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Primary constructors (C# 12+) are fine when all dependencies are injected interfaces.
|
|
31
|
+
|
|
32
|
+
## Async
|
|
33
|
+
|
|
34
|
+
```csharp
|
|
35
|
+
public async Task<OrderSummary> BuildSummaryAsync(Guid id, CancellationToken cancellationToken)
|
|
36
|
+
{
|
|
37
|
+
var order = await _orders.FindByIdAsync(id, cancellationToken)
|
|
38
|
+
?? throw new NotFoundException(id);
|
|
39
|
+
|
|
40
|
+
var buyer = await _customers.GetAsync(order.CustomerId, cancellationToken);
|
|
41
|
+
return OrderSummary.From(order, buyer);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Parallel independent IO:
|
|
46
|
+
|
|
47
|
+
```csharp
|
|
48
|
+
await Task.WhenAll(fetchOrders, fetchMetrics);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Never block on `Task` in ASP.NET request threads.
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
```csharp
|
|
56
|
+
public sealed class EmailOptions
|
|
57
|
+
{
|
|
58
|
+
public const string SectionName = "Email";
|
|
59
|
+
public required string Host { get; init; }
|
|
60
|
+
public int Port { get; init; } = 587;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
builder.Services.Configure<EmailOptions>(
|
|
64
|
+
builder.Configuration.GetSection(EmailOptions.SectionName));
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Inject `IOptions<EmailOptions>` or `IOptionsMonitor<EmailOptions>` for reload-safe settings.
|
|
68
|
+
|
|
69
|
+
## Result instead of exceptions (expected failures)
|
|
70
|
+
|
|
71
|
+
```csharp
|
|
72
|
+
public readonly record struct Result<T>(bool Ok, T? Value, string? Error)
|
|
73
|
+
{
|
|
74
|
+
public static Result<T> Success(T value) => new(true, value, null);
|
|
75
|
+
public static Result<T> Fail(string message) => new(false, default, message);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use for validation/business rejections; reserve exceptions for truly exceptional faults.
|
|
80
|
+
|
|
81
|
+
## EF Core repository sketch
|
|
82
|
+
|
|
83
|
+
```csharp
|
|
84
|
+
public async Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken)
|
|
85
|
+
{
|
|
86
|
+
return await _db.Orders
|
|
87
|
+
.Include(o => o.Lines)
|
|
88
|
+
.AsNoTracking()
|
|
89
|
+
.FirstOrDefaultAsync(o => o.Id == id, cancellationToken);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Track entities only inside command handlers that mutate state.
|
|
94
|
+
|
|
95
|
+
## Middleware
|
|
96
|
+
|
|
97
|
+
Register custom middleware with `app.UseMiddleware<RequestTimingMiddleware>()`; log elapsed ms and status code; avoid swallowing exceptions.
|
|
98
|
+
|
|
99
|
+
## Minimal APIs
|
|
100
|
+
|
|
101
|
+
```csharp
|
|
102
|
+
var orders = app.MapGroup("/api/orders").RequireAuthorization().WithTags("Orders");
|
|
103
|
+
|
|
104
|
+
orders.MapGet("/{id:guid}", async (Guid id, IOrderRepository repo, CancellationToken ct) =>
|
|
105
|
+
{
|
|
106
|
+
var order = await repo.FindByIdAsync(id, ct);
|
|
107
|
+
return order is null ? Results.NotFound() : Results.Ok(order);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Use `TypedResults` for OpenAPI metadata; validate bodies with `[AsParameters]` or explicit records + `Results.ValidationProblem`.
|
|
112
|
+
|
|
113
|
+
## Guard clauses
|
|
114
|
+
|
|
115
|
+
Validate arguments first (`ThrowIfNull`, range checks) so happy path stays flat.
|
|
116
|
+
|
|
117
|
+
## Anti-patterns
|
|
118
|
+
|
|
119
|
+
| Issue | Remedy |
|
|
120
|
+
|-------|--------|
|
|
121
|
+
| `async void` (non-events) | return `Task` |
|
|
122
|
+
| `.Result` / `.Wait()` | `await` |
|
|
123
|
+
| empty `catch` | log + throw or map to problem detail |
|
|
124
|
+
| `new SqlConnection` in controller | injected factory/repository |
|
|
125
|
+
| `dynamic` in domain | strong types/generics |
|
|
126
|
+
| mutable static fields | scoped/singleton services |
|
|
127
|
+
|
|
128
|
+
## Tooling
|
|
129
|
+
|
|
130
|
+
- `dotnet format` in CI; analyzers (nullable, CA rules) as errors where team agrees.
|
|
131
|
+
- Integration tests: `WebApplicationFactory` with test containers or SQLite provider per **techne:databases** guidance.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter
|
|
3
|
+
description: Flutter and Dart application engineering — null-safe idioms, immutable state (sealed/freezed), widget architecture, BLoC/Riverpod/Provider patterns, GoRouter guards, Dio networking, errors, testing, and review checklists. Use when implementing or reviewing Flutter features, navigation, async UI, or Dart architecture.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Dart and Flutter patterns for production apps: data flow, widgets, routing, HTTP, and quality gates. Platform chrome, adaptive UI, and native feel → **native-ui**; deep accessibility audits → **ui-design** (`audit`, `harden`).
|
|
7
|
+
|
|
8
|
+
## Non-negotiable rules
|
|
9
|
+
|
|
10
|
+
1. **Null-safe idioms** — Avoid `!`; use `?.`, `??`, early returns, and Dart 3 patterns; reserve `late` for guaranteed init (e.g. `AnimationController` in `initState`).
|
|
11
|
+
2. **Explicit async state** — Sealed hierarchies or `freezed` unions for loading/success/failure; no overlapping `isLoading` + `hasError` booleans.
|
|
12
|
+
3. **Widget classes, not builders** — Extract `_buildX()` helpers into `StatelessWidget`/`ConsumerWidget` subclasses; push `const` through the tree.
|
|
13
|
+
4. **Narrow rebuilds** — Watch providers/blocs only in the smallest subtree; keep static chrome `const`.
|
|
14
|
+
5. **Context safety** — After every `await` in widgets, guard with `mounted` / `context.mounted` before navigation, snackbars, or `setState`.
|
|
15
|
+
6. **Layer separation** — Widgets render; state containers (Cubit, Notifier, etc.) orchestrate; repositories talk to APIs/DB — inject dependencies, no god objects.
|
|
16
|
+
7. **Routing** — One declarative router (typically GoRouter); auth redirects and deep links centralized; typed route args, not raw maps.
|
|
17
|
+
8. **HTTP** — Timeouts on the client; auth header interceptor; at most **one** token-refresh retry per request (flag in `extra`).
|
|
18
|
+
9. **Lists** — `ListView.builder` / `GridView.builder` (or pagination) for large datasets; no unbounded `children:` lists.
|
|
19
|
+
10. **Theming** — Colors and text from `Theme.of(context)`; no scattered magic hex in widgets.
|
|
20
|
+
11. **Analysis** — Root `analysis_options.yaml` with strict casts/inference/raw-types; `flutter analyze` clean in CI.
|
|
21
|
+
12. **Secrets** — Tokens in platform secure storage; config via `--dart-define` or env excluded from VCS; never log credentials.
|
|
22
|
+
13. **Review routing** — Systematic review → **exo-qa** / code-review skill; security pass → **exo-security**; library APIs → **biblos** MCP.
|
|
23
|
+
|
|
24
|
+
## References
|
|
25
|
+
|
|
26
|
+
| File | Load when |
|
|
27
|
+
|------|-----------|
|
|
28
|
+
| [reference/patterns.md](reference/patterns.md) | Writing features — state, widgets, GoRouter, Dio, errors, tests |
|
|
29
|
+
| [reference/code-review.md](reference/code-review.md) | Reviewing a PR or auditing project health across Dart/Flutter layers |
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Flutter / Dart review checklist
|
|
2
|
+
|
|
3
|
+
Library-agnostic gates for PR review. Adapt rows to the project’s state library (BLoC, Riverpod, Provider, signals, etc.).
|
|
4
|
+
|
|
5
|
+
## Project and Dart hygiene
|
|
6
|
+
|
|
7
|
+
- Feature-first or layer-first layout; UI free of business rules.
|
|
8
|
+
- `analysis_options.yaml`: strict casts/inference/raw-types; shared in monorepos.
|
|
9
|
+
- No `print` in production — structured logging.
|
|
10
|
+
- Generated `*.g.dart` / `*.freezed.dart` committed or gitignored consistently.
|
|
11
|
+
- `package:` imports; `final`/`const` by default; typed catches (`on SpecificException`).
|
|
12
|
+
- No ignored futures without `unawaited`; no broad `catch (e)` without rethrow policy.
|
|
13
|
+
|
|
14
|
+
## Widgets
|
|
15
|
+
|
|
16
|
+
- `build` under ~100 lines; hot paths split into widget classes.
|
|
17
|
+
- `const` constructors and literals where fields are final.
|
|
18
|
+
- Keys: `ValueKey`/`ObjectKey` in reorderable lists; never `UniqueKey()` inside `build`.
|
|
19
|
+
- Theme-driven color/text; responsive overflow handled (`Flexible`, `FittedBox`).
|
|
20
|
+
- No network/IO/list sorting inside `build`; subscriptions not created per frame.
|
|
21
|
+
|
|
22
|
+
## State management
|
|
23
|
+
|
|
24
|
+
- Dependencies injected into cubits/notifiers/controllers — not constructed internally.
|
|
25
|
+
- Immutable snapshots (`copyWith`, freezed, records) for BLoC/Riverpod-style flows.
|
|
26
|
+
- Reactive stores mutate only through their API (`@action`, signal setters, etc.).
|
|
27
|
+
- Async flows expose distinct loading/success/error variants; UI handles all branches.
|
|
28
|
+
- Consumers scoped narrowly; selectors/`buildWhen`/`select` to limit rebuilds.
|
|
29
|
+
- Streams/`listen` canceled in `dispose`/`close`; timers cleared.
|
|
30
|
+
- Ephemeral UI stays local; shared state lifted minimally.
|
|
31
|
+
|
|
32
|
+
## Performance
|
|
33
|
+
|
|
34
|
+
- Builder constructors for long lists; pagination when datasets grow.
|
|
35
|
+
- Images: cache + decode size (`cacheWidth`/`cacheHeight`); placeholders on failure.
|
|
36
|
+
- `RepaintBoundary` around independently animating subtrees.
|
|
37
|
+
- Avoid `Opacity` in animations — prefer fade transitions.
|
|
38
|
+
|
|
39
|
+
## Navigation
|
|
40
|
+
|
|
41
|
+
- Single routing approach; typed parameters; guards not copy-pasted per screen.
|
|
42
|
+
- Deep links validated before navigation; back behavior verified on Android/iOS.
|
|
43
|
+
|
|
44
|
+
## Security and data
|
|
45
|
+
|
|
46
|
+
- Secrets off-device or in secure storage; API keys via compile-time env, not source.
|
|
47
|
+
- HTTPS only; sanitize deep links; validate forms before API calls.
|
|
48
|
+
- No sensitive values in logs.
|
|
49
|
+
|
|
50
|
+
## Testing and CI
|
|
51
|
+
|
|
52
|
+
- Unit coverage on domain/state; widget tests for interactions; integration for critical flows.
|
|
53
|
+
- State transition tests include error/retry paths.
|
|
54
|
+
- `flutter analyze` and tests block merge.
|
|
55
|
+
|
|
56
|
+
## Accessibility and i18n (spot-check)
|
|
57
|
+
|
|
58
|
+
- Semantic labels on custom controls; 48×48 minimum taps; contrast ≥ 4.5:1 for body text.
|
|
59
|
+
- User strings through l10n; locale-aware dates/numbers.
|
|
60
|
+
- Full visual/a11y pass → **ui-design**; platform-native patterns → **native-ui**.
|
|
61
|
+
|
|
62
|
+
## Dependencies
|
|
63
|
+
|
|
64
|
+
- Prefer maintained pub packages (pub points, recent releases, verified publisher).
|
|
65
|
+
- Caret constraints unless pin required; avoid production `dependency_overrides` without ticket.
|
|
66
|
+
- Monorepo: no `src/` imports across packages; workspace resolution for internals.
|
|
67
|
+
|
|
68
|
+
## State library cheat sheet
|
|
69
|
+
|
|
70
|
+
| Concern | BLoC | Riverpod | Provider |
|
|
71
|
+
|---------|------|----------|----------|
|
|
72
|
+
| Container | Cubit/Bloc | Notifier | ChangeNotifier |
|
|
73
|
+
| UI hook | BlocBuilder | ConsumerWidget | Consumer |
|
|
74
|
+
| Narrow rebuild | BlocSelector | `.select` | Selector |
|
|
75
|
+
| Side effect | BlocListener | `ref.listen` | listener callback |
|
|
76
|
+
| Test | `blocTest` | `ProviderContainer` | direct instance |
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Flutter implementation patterns
|
|
2
|
+
|
|
3
|
+
## Null safety and records
|
|
4
|
+
|
|
5
|
+
```dart
|
|
6
|
+
String label(Account? account) => switch (account) {
|
|
7
|
+
Account(:final name, :final email) => '$name <$email>',
|
|
8
|
+
null => 'Guest',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
Future<SummaryBundle> loadSummary(UserRepo users, OrderRepo orders) async {
|
|
12
|
+
final (people, purchases) = await (users.all(), orders.recent()).wait;
|
|
13
|
+
return SummaryBundle(users: people, orders: purchases);
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Immutable state
|
|
18
|
+
|
|
19
|
+
```dart
|
|
20
|
+
sealed class LoadState<T> {}
|
|
21
|
+
final class Idle<T> extends LoadState<T> {}
|
|
22
|
+
final class Pending<T> extends LoadState<T> {}
|
|
23
|
+
final class Ready<T> extends LoadState<T> { const Ready(this.value); final T value; }
|
|
24
|
+
final class Failed<T> extends LoadState<T> { const Failed(this.reason); final Object reason; }
|
|
25
|
+
|
|
26
|
+
Widget body(LoadState<User> state) => switch (state) {
|
|
27
|
+
Idle() => const SizedBox.shrink(),
|
|
28
|
+
Pending() => const CircularProgressIndicator(),
|
|
29
|
+
Ready(:final value) => ProfileCard(user: value),
|
|
30
|
+
Failed(:final reason) => ErrorBanner(reason.toString()),
|
|
31
|
+
};
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`freezed` + json_serializable when you need `copyWith`/serialization without boilerplate.
|
|
35
|
+
|
|
36
|
+
## Widget architecture
|
|
37
|
+
|
|
38
|
+
Isolate reactive subtrees:
|
|
39
|
+
|
|
40
|
+
```dart
|
|
41
|
+
class DashboardPage extends StatelessWidget {
|
|
42
|
+
const DashboardPage({super.key});
|
|
43
|
+
@override
|
|
44
|
+
Widget build(BuildContext context) {
|
|
45
|
+
return const Scaffold(
|
|
46
|
+
body: Column(children: [
|
|
47
|
+
StaticHeader(),
|
|
48
|
+
_LiveCounter(),
|
|
49
|
+
StaticFooter(),
|
|
50
|
+
]),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class _LiveCounter extends ConsumerWidget {
|
|
56
|
+
const _LiveCounter();
|
|
57
|
+
@override
|
|
58
|
+
Widget build(BuildContext context, WidgetRef ref) {
|
|
59
|
+
final n = ref.watch(counterProvider);
|
|
60
|
+
return Text('$n');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## BLoC sketch
|
|
66
|
+
|
|
67
|
+
```dart
|
|
68
|
+
class SignInCubit extends Cubit<SignInState> {
|
|
69
|
+
SignInCubit(this._auth) : super(const SignInState.idle());
|
|
70
|
+
final AuthGateway _auth;
|
|
71
|
+
|
|
72
|
+
Future<void> submit(String email, String password) async {
|
|
73
|
+
emit(const SignInState.busy());
|
|
74
|
+
try {
|
|
75
|
+
final profile = await _auth.signIn(email, password);
|
|
76
|
+
emit(SignInState.done(profile));
|
|
77
|
+
} on AuthFailure catch (err) {
|
|
78
|
+
emit(SignInState.problem(err.message));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Riverpod sketch
|
|
85
|
+
|
|
86
|
+
```dart
|
|
87
|
+
@riverpod
|
|
88
|
+
Future<List<CatalogItem>> catalog(CatalogRef ref) async {
|
|
89
|
+
return ref.watch(catalogGatewayProvider).fetch();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@riverpod
|
|
93
|
+
class Basket extends _$Basket {
|
|
94
|
+
@override
|
|
95
|
+
List<BasketLine> build() => [];
|
|
96
|
+
|
|
97
|
+
void increment(CatalogItem item) {
|
|
98
|
+
final hit = state.where((l) => l.sku == item.id).firstOrNull;
|
|
99
|
+
state = hit == null
|
|
100
|
+
? [...state, BasketLine(sku: item.id, qty: 1)]
|
|
101
|
+
: [for (final l in state) if (l.sku == item.id) l.copyWith(qty: l.qty + 1) else l];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Derive totals with a separate provider; use `firstWhereOrNull` when joining optional catalogs.
|
|
107
|
+
|
|
108
|
+
## GoRouter + auth
|
|
109
|
+
|
|
110
|
+
```dart
|
|
111
|
+
final appRouter = GoRouter(
|
|
112
|
+
refreshListenable: GoRouterRefreshStream(signInCubit.stream),
|
|
113
|
+
redirect: (ctx, loc) {
|
|
114
|
+
final signedIn = ctx.read<SignInCubit>().state is SignInDone;
|
|
115
|
+
final onLogin = loc.matchedLocation == '/login';
|
|
116
|
+
if (!signedIn && !onLogin) return '/login';
|
|
117
|
+
if (signedIn && onLogin) return '/';
|
|
118
|
+
return null;
|
|
119
|
+
},
|
|
120
|
+
routes: [
|
|
121
|
+
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
|
|
122
|
+
ShellRoute(
|
|
123
|
+
builder: (_, __, child) => AppScaffold(child: child),
|
|
124
|
+
routes: [
|
|
125
|
+
GoRoute(path: '/', builder: (_, __) => const HomeScreen()),
|
|
126
|
+
GoRoute(
|
|
127
|
+
path: '/item/:sku',
|
|
128
|
+
builder: (ctx, state) => ItemScreen(sku: state.pathParameters['sku']!),
|
|
129
|
+
),
|
|
130
|
+
],
|
|
131
|
+
),
|
|
132
|
+
],
|
|
133
|
+
);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Dio client
|
|
137
|
+
|
|
138
|
+
```dart
|
|
139
|
+
final client = Dio(BaseOptions(
|
|
140
|
+
baseUrl: const String.fromEnvironment('API_BASE'),
|
|
141
|
+
connectTimeout: const Duration(seconds: 10),
|
|
142
|
+
receiveTimeout: const Duration(seconds: 30),
|
|
143
|
+
));
|
|
144
|
+
|
|
145
|
+
client.interceptors.add(InterceptorsWrapper(
|
|
146
|
+
onRequest: (opts, next) async {
|
|
147
|
+
final token = await vault.read('access');
|
|
148
|
+
if (token != null) opts.headers['Authorization'] = 'Bearer $token';
|
|
149
|
+
next.next(opts);
|
|
150
|
+
},
|
|
151
|
+
onError: (err, next) async {
|
|
152
|
+
final retried = err.requestOptions.extra['_retried'] == true;
|
|
153
|
+
if (!retried && err.response?.statusCode == 401 && await refreshTokens()) {
|
|
154
|
+
err.requestOptions.extra['_retried'] = true;
|
|
155
|
+
return next.resolve(await client.fetch(err.requestOptions));
|
|
156
|
+
}
|
|
157
|
+
next.next(err);
|
|
158
|
+
},
|
|
159
|
+
));
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Errors in `main`
|
|
163
|
+
|
|
164
|
+
```dart
|
|
165
|
+
void main() {
|
|
166
|
+
FlutterError.onError = (details) {
|
|
167
|
+
FlutterError.presentError(details);
|
|
168
|
+
reporter.recordFlutterFatalError(details);
|
|
169
|
+
};
|
|
170
|
+
PlatformDispatcher.instance.onError = (error, stack) {
|
|
171
|
+
reporter.recordError(error, stack, fatal: true);
|
|
172
|
+
return true;
|
|
173
|
+
};
|
|
174
|
+
runApp(const RootApp());
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Set `ErrorWidget.builder` in release for user-safe fallbacks.
|
|
179
|
+
|
|
180
|
+
## Testing snippets
|
|
181
|
+
|
|
182
|
+
```dart
|
|
183
|
+
blocTest<SignInCubit, SignInState>(
|
|
184
|
+
'busy then problem on bad password',
|
|
185
|
+
build: () => SignInCubit(FakeAuth(reject: true)),
|
|
186
|
+
act: (c) => c.submit('a@b.c', 'bad'),
|
|
187
|
+
expect: () => [const SignInState.busy(), isA<SignInProblem>()],
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
testWidgets('badge count', (tester) async {
|
|
191
|
+
await tester.pumpWidget(
|
|
192
|
+
ProviderScope(
|
|
193
|
+
overrides: [basketProvider.overrideWith(() => FakeBasket(qty: 2))],
|
|
194
|
+
child: const MaterialApp(home: BasketBadge()),
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
expect(find.text('2'), findsOneWidget);
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Use fakes over heavy mocks; test state transitions, not private fields.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: go
|
|
3
|
+
description: Idiomatic Go structure, errors, concurrency, and testing—table-driven tests, race detector, fuzzing, benchmarks. Use when writing or reviewing Go services, CLIs, packages, or `go test` / module layout.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Go favors boring, obvious code: explicit errors, small interfaces at call sites, and concurrency tied to `context`. Pair with **atlas** for repo layout, **biblos** for stdlib and third-party APIs, **techne:services** for HTTP/API design, **techne:quality** for broader TDD gates, and **code-review** / **exo-qa** before merge.
|
|
7
|
+
|
|
8
|
+
## Non-negotiable rules
|
|
9
|
+
|
|
10
|
+
1. **Readability beats cleverness** — straight-line control flow; return early on errors so the happy path stays shallow.
|
|
11
|
+
2. **Every error gets a decision** — wrap with `%w` and operation context; classify with `errors.Is` / `errors.As`; never assign to `_` unless documented (e.g. best-effort close).
|
|
12
|
+
3. **Take interfaces, return structs** — define narrow interfaces where they are consumed, not in provider packages.
|
|
13
|
+
4. **`context.Context` first** on RPC, DB, and HTTP paths — derive timeouts; propagate cancel; shut down with `Shutdown` and a bounded wait, not abrupt `Close` on servers.
|
|
14
|
+
5. **Goroutines must finish** — tie work to context or wait groups; use buffered sends or `select` on `ctx.Done()` so cancelled callers cannot leak workers.
|
|
15
|
+
6. **Zero values should work** — lazy-init maps/slices inside methods; avoid mutable package-level singletons; inject `*sql.DB`, loggers, and clients via constructors.
|
|
16
|
+
7. **Preallocate when size is known** — `make([]T, 0, n)`; build strings with `strings.Builder` or `strings.Join` in hot loops.
|
|
17
|
+
8. **Table-driven tests** — one `TestX` with `[]struct{ name, … }` and `t.Run`; copy loop variables before `t.Parallel()` subtests.
|
|
18
|
+
9. **Helpers call `t.Helper()`** — register cleanup with `t.Cleanup`; use `t.TempDir`, `httptest`, and interface fakes instead of testing unexported symbols.
|
|
19
|
+
10. **CI runs `go test -race ./...`** — cover critical paths toward 100%, public packages ≥90%, rest ≥80%; fuzz parsers and decoders where inputs are untrusted.
|
|
20
|
+
11. **Gate merges with vet, staticcheck, golangci-lint** — `gofmt` / `goimports` on every change; `go mod tidy` before commit.
|
|
21
|
+
12. **Functional options for optional config** — keep constructors stable; one receiver style (pointer or value) per type.
|
|
22
|
+
|
|
23
|
+
## References
|
|
24
|
+
|
|
25
|
+
| File | Load when |
|
|
26
|
+
|------|-----------|
|
|
27
|
+
| [reference/patterns.md](reference/patterns.md) | Packages, errors, interfaces, concurrency, memory, tooling, anti-patterns |
|
|
28
|
+
| [reference/testing.md](reference/testing.md) | TDD loop, tables, mocks, golden files, HTTP tests, bench/fuzz, coverage |
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Go patterns
|
|
2
|
+
|
|
3
|
+
## Layout and packages
|
|
4
|
+
|
|
5
|
+
Typical service repo: `cmd/<app>/main.go`, `internal/` for private app code, `pkg/` only for libraries meant for external importers, `api/` for OpenAPI/proto, `testdata/` for fixtures. Package names: short, lowercase, no `util` dumping grounds.
|
|
6
|
+
|
|
7
|
+
Inject dependencies through structs and constructors—do not open DB pools in `init()` from environment variables.
|
|
8
|
+
|
|
9
|
+
## Errors
|
|
10
|
+
|
|
11
|
+
Sentinel vars for stable domain cases (`ErrNotFound`). Typed errors when callers need fields. Wrap at each layer with a short verb phrase:
|
|
12
|
+
|
|
13
|
+
```go
|
|
14
|
+
if err != nil {
|
|
15
|
+
return nil, fmt.Errorf("load profile %q: %w", id, err)
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Prefer early return over nested `if err != nil`.
|
|
20
|
+
|
|
21
|
+
## Interfaces
|
|
22
|
+
|
|
23
|
+
Keep interfaces tiny (often one method). Consumer packages declare what they need:
|
|
24
|
+
|
|
25
|
+
```go
|
|
26
|
+
type Ledger interface {
|
|
27
|
+
Post(ctx context.Context, entry Entry) error
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Optional behavior: type assert to a secondary interface (e.g. `Flusher`) instead of widening the base API.
|
|
32
|
+
|
|
33
|
+
## Concurrency
|
|
34
|
+
|
|
35
|
+
**Worker pool:** fixed goroutines reading `jobs` until closed; `WaitGroup` for drain.
|
|
36
|
+
|
|
37
|
+
**Fan-out:** `errgroup.WithContext` cancels siblings on first error; capture loop indices before `g.Go`.
|
|
38
|
+
|
|
39
|
+
**HTTP client:** `NewRequestWithContext`, always `defer resp.Body.Close()`, set timeouts on the client or context.
|
|
40
|
+
|
|
41
|
+
**Shutdown:** trap `SIGINT`/`SIGTERM`, `Shutdown` with ~30s context, then exit.
|
|
42
|
+
|
|
43
|
+
Avoid sending on unbuffered channels without a receiver; prefer buffer size 1 plus cancel branch when exposing async helpers.
|
|
44
|
+
|
|
45
|
+
## Struct patterns
|
|
46
|
+
|
|
47
|
+
**Functional options:** `type Option func(*Server)` with defaults inside `New`, variadic `opts ...Option`.
|
|
48
|
+
|
|
49
|
+
**Embedding:** compose behavior (logging, metrics) without deep inheritance trees.
|
|
50
|
+
|
|
51
|
+
## Performance habits
|
|
52
|
+
|
|
53
|
+
| Situation | Approach |
|
|
54
|
+
|-----------|----------|
|
|
55
|
+
| Known output count | Pre-sized slice |
|
|
56
|
+
| Repeated temp buffers | `sync.Pool` with reset in defer |
|
|
57
|
+
| String assembly in loop | `strings.Builder` |
|
|
58
|
+
| Map/set membership hot path | `map[T]struct{}` |
|
|
59
|
+
|
|
60
|
+
Profile before `sync.Pool`; misuse adds complexity.
|
|
61
|
+
|
|
62
|
+
## Tooling
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
go build ./...
|
|
66
|
+
go test -race -cover ./...
|
|
67
|
+
go vet ./...
|
|
68
|
+
staticcheck ./...
|
|
69
|
+
golangci-lint run
|
|
70
|
+
go mod tidy && go mod verify
|
|
71
|
+
gofmt -w . && goimports -w .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Enable errcheck, staticcheck, govet (shadow), ineffassign in CI linter config.
|
|
75
|
+
|
|
76
|
+
## Anti-patterns
|
|
77
|
+
|
|
78
|
+
| Avoid | Prefer |
|
|
79
|
+
|-------|--------|
|
|
80
|
+
| `panic` for expected errors | Return `error` |
|
|
81
|
+
| Context stored on structs | Context as first param |
|
|
82
|
+
| Naked returns in long funcs | Named returns only when signature is tiny |
|
|
83
|
+
| Mixed value/pointer receivers | One style per type |
|
|
84
|
+
| Returning `interface{}` from domain APIs | Concrete structs |
|
|
85
|
+
| Global `var db *sql.DB` | Struct field + constructor |
|
|
86
|
+
|
|
87
|
+
Cross-stack HTTP and middleware conventions → **techne:services**.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Go testing
|
|
2
|
+
|
|
3
|
+
## TDD loop
|
|
4
|
+
|
|
5
|
+
1. Sketch signature or interface.
|
|
6
|
+
2. Write failing test (table row or subtest).
|
|
7
|
+
3. Minimal implementation until green.
|
|
8
|
+
4. Refactor with `-race` still passing.
|
|
9
|
+
|
|
10
|
+
Broader red-green discipline → **techne:quality**.
|
|
11
|
+
|
|
12
|
+
## Table-driven template
|
|
13
|
+
|
|
14
|
+
```go
|
|
15
|
+
func TestNormalizeEmail(t *testing.T) {
|
|
16
|
+
cases := []struct {
|
|
17
|
+
name string
|
|
18
|
+
in string
|
|
19
|
+
want string
|
|
20
|
+
err bool
|
|
21
|
+
}{
|
|
22
|
+
{"trim lower", " A@B.com ", "a@b.com", false},
|
|
23
|
+
{"empty", "", "", true},
|
|
24
|
+
}
|
|
25
|
+
for _, tc := range cases {
|
|
26
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
27
|
+
got, err := NormalizeEmail(tc.in)
|
|
28
|
+
if tc.err {
|
|
29
|
+
if err == nil {
|
|
30
|
+
t.Fatal("expected error")
|
|
31
|
+
}
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if err != nil {
|
|
35
|
+
t.Fatalf("unexpected: %v", err)
|
|
36
|
+
}
|
|
37
|
+
if got != tc.want {
|
|
38
|
+
t.Fatalf("got %q want %q", got, tc.want)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use `reflect.DeepEqual` only when no clearer field asserts exist.
|
|
46
|
+
|
|
47
|
+
## Organization
|
|
48
|
+
|
|
49
|
+
- Group related flows under `t.Run` blocks sharing setup.
|
|
50
|
+
- `t.Parallel()` only when cases do not share mutable fixtures.
|
|
51
|
+
- Helpers: `t.Helper()`, fail with `t.Fatalf`, register `t.Cleanup` for closes.
|
|
52
|
+
|
|
53
|
+
## Golden files
|
|
54
|
+
|
|
55
|
+
Store expected bytes under `testdata/*.golden`. Gate updates behind a flag:
|
|
56
|
+
|
|
57
|
+
```go
|
|
58
|
+
var updateGoldens = flag.Bool("update", false, "rewrite golden files")
|
|
59
|
+
|
|
60
|
+
if *updateGoldens {
|
|
61
|
+
os.WriteFile(goldenPath, got, 0o644)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run `go test -update` locally; never commit refreshed goldens without reviewing diff.
|
|
66
|
+
|
|
67
|
+
## Mocks
|
|
68
|
+
|
|
69
|
+
Define small interfaces in the consumer package; tests supply stub structs with function fields:
|
|
70
|
+
|
|
71
|
+
```go
|
|
72
|
+
type stubStore struct {
|
|
73
|
+
load func(id string) (*User, error)
|
|
74
|
+
}
|
|
75
|
+
func (s stubStore) Load(id string) (*User, error) { return s.load(id) }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Prefer real sqlite/postgres test containers for integration paths when feasible.
|
|
79
|
+
|
|
80
|
+
## HTTP handlers
|
|
81
|
+
|
|
82
|
+
```go
|
|
83
|
+
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
|
84
|
+
rec := httptest.NewRecorder()
|
|
85
|
+
Handler(rec, req)
|
|
86
|
+
if rec.Code != http.StatusOK {
|
|
87
|
+
t.Fatalf("status %d", rec.Code)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Table-drive method, path, body, expected status and JSON snippets.
|
|
92
|
+
|
|
93
|
+
## Benchmarks and fuzz
|
|
94
|
+
|
|
95
|
+
```go
|
|
96
|
+
func BenchmarkEncode(b *testing.B) {
|
|
97
|
+
payload := mkPayload(1024)
|
|
98
|
+
b.ReportAllocs()
|
|
99
|
+
for i := 0; i < b.N; i++ {
|
|
100
|
+
_, _ = Encode(payload)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Run `go test -bench=. -benchmem`. Fuzz decoders with seed corpus plus `go test -fuzz=FuzzDecode -fuzztime=30s`; assert invariants (roundtrip, no panic on reject).
|
|
106
|
+
|
|
107
|
+
## Coverage
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
go test -race -coverprofile=coverage.out ./...
|
|
111
|
+
go tool cover -func=coverage.out
|
|
112
|
+
go tool cover -html=coverage.out
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Layer | Target |
|
|
116
|
+
|-------|--------|
|
|
117
|
+
| Money / auth / invariants | 100% |
|
|
118
|
+
| Exported API | ≥90% |
|
|
119
|
+
| Everything else | ≥80% |
|
|
120
|
+
|
|
121
|
+
Exclude generated mocks from coverage via build tags when needed.
|
|
122
|
+
|
|
123
|
+
## Commands cheat sheet
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
go test ./...
|
|
127
|
+
go test -run TestUser/Create ./...
|
|
128
|
+
go test -short ./...
|
|
129
|
+
go test -count=10 ./... # flake hunt
|
|
130
|
+
go test -timeout 30s ./...
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Practices
|
|
134
|
+
|
|
135
|
+
| Do | Skip |
|
|
136
|
+
|----|------|
|
|
137
|
+
| Assert behavior through public API | Importing `_test` packages to poke privates |
|
|
138
|
+
| `t.Cleanup` for files and servers | `time.Sleep` for readiness—poll or channels |
|
|
139
|
+
| Descriptive subtest names | Ignoring flaky tests |
|
|
140
|
+
| Error-path table rows | Mocking every leaf when one integration test suffices |
|
|
141
|
+
|
|
142
|
+
Security-sensitive parsing → add fuzz + **security-review** / **exo-security** pass.
|