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,142 @@
|
|
|
1
|
+
# Rust testing
|
|
2
|
+
|
|
3
|
+
## TDD loop
|
|
4
|
+
|
|
5
|
+
1. Add `#[test]` (or `proptest!`) against the public API.
|
|
6
|
+
2. Stub body with `todo!()` until failure is meaningful.
|
|
7
|
+
3. Implement minimally; run `cargo test` and `clippy`.
|
|
8
|
+
4. Refactor without shrinking behavior coverage.
|
|
9
|
+
|
|
10
|
+
Org-wide verification habits → **techne:quality**.
|
|
11
|
+
|
|
12
|
+
## Unit tests in-module
|
|
13
|
+
|
|
14
|
+
```rust
|
|
15
|
+
#[cfg(test)]
|
|
16
|
+
mod tests {
|
|
17
|
+
use super::*;
|
|
18
|
+
|
|
19
|
+
#[test]
|
|
20
|
+
fn rejects_blank_title() {
|
|
21
|
+
let err = Item::new("", 10).unwrap_err();
|
|
22
|
+
assert!(err.to_string().contains("title"));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Return `Result<(), E>` from tests to use `?` on setup helpers.
|
|
28
|
+
|
|
29
|
+
## Panics vs Results
|
|
30
|
+
|
|
31
|
+
Prefer asserting `is_err()` and matching error enums. Reserve `#[should_panic(expected = "...")]` for truly unreachable internal invariants.
|
|
32
|
+
|
|
33
|
+
## Integration tests
|
|
34
|
+
|
|
35
|
+
Each file under `tests/` is its own crate—import the library by name:
|
|
36
|
+
|
|
37
|
+
```rust
|
|
38
|
+
use mylib::{App, Settings};
|
|
39
|
+
|
|
40
|
+
#[test]
|
|
41
|
+
fn health_endpoint_returns_ok() {
|
|
42
|
+
let app = App::boot(Settings::fixture());
|
|
43
|
+
let resp = app.get("/health").unwrap();
|
|
44
|
+
assert_eq!(resp.status, 200);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Share helpers via `tests/common/mod.rs` (not `tests/common.rs` alone if multiple binaries need it).
|
|
49
|
+
|
|
50
|
+
## Async
|
|
51
|
+
|
|
52
|
+
```rust
|
|
53
|
+
#[tokio::test]
|
|
54
|
+
async fn times_out_on_slow_peer() {
|
|
55
|
+
let result = tokio::time::timeout(
|
|
56
|
+
Duration::from_millis(50),
|
|
57
|
+
client.fetch("/slow"),
|
|
58
|
+
).await;
|
|
59
|
+
assert!(result.is_err());
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `tokio::test` with multi-thread runtime when tests spawn tasks.
|
|
64
|
+
|
|
65
|
+
## Parameterized tests
|
|
66
|
+
|
|
67
|
+
**rstest** for readable matrices:
|
|
68
|
+
|
|
69
|
+
```rust
|
|
70
|
+
#[rstest]
|
|
71
|
+
#[case("", false)]
|
|
72
|
+
#[case("ok", true)]
|
|
73
|
+
fn validates_token(#[case] token: &str, #[case] ok: bool) {
|
|
74
|
+
assert_eq!(check(token).is_ok(), ok);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**proptest** for invariants:
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
proptest! {
|
|
82
|
+
#[test]
|
|
83
|
+
fn roundtrip(bytes in prop::collection::vec(any::<u8>(), 0..256)) {
|
|
84
|
+
let enc = encode(&bytes);
|
|
85
|
+
prop_assert_eq!(decode(&enc).unwrap(), bytes);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Custom strategies for emails, IDs, or bounded numerics.
|
|
91
|
+
|
|
92
|
+
## mockall
|
|
93
|
+
|
|
94
|
+
```rust
|
|
95
|
+
#[automock]
|
|
96
|
+
trait Repo {
|
|
97
|
+
fn fetch(&self, id: u64) -> Option<Row>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[test]
|
|
101
|
+
fn returns_row_when_present() {
|
|
102
|
+
let mut mock = MockRepo::new();
|
|
103
|
+
mock.expect_fetch().with(eq(7)).returning(|_| Some(Row { id: 7 }));
|
|
104
|
+
let svc = Service::new(mock);
|
|
105
|
+
assert_eq!(svc.load(7).unwrap().id, 7);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Do not mock types you do not own—wrap behind your trait.
|
|
110
|
+
|
|
111
|
+
## Doc tests
|
|
112
|
+
|
|
113
|
+
Executable examples in `///` blocks; use `no_run` when I/O required. They run via `cargo test --doc`.
|
|
114
|
+
|
|
115
|
+
## Benchmarks (Criterion)
|
|
116
|
+
|
|
117
|
+
`[[bench]]` with `harness = false`; use `black_box` on inputs; commit baseline HTML only when team agrees.
|
|
118
|
+
|
|
119
|
+
## Coverage
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
cargo llvm-cov
|
|
123
|
+
cargo llvm-cov --html
|
|
124
|
+
cargo llvm-cov --fail-under-lines 80
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Coverage goals: domain core 100%, exported API at least 90%, everything else at least 80%; leave generated code and FFI shims out of the measurement.
|
|
128
|
+
|
|
129
|
+
## CI sketch
|
|
130
|
+
|
|
131
|
+
Format check → Clippy → tests → llvm-cov threshold. Pin toolchain via `rust-toolchain.toml` or CI action.
|
|
132
|
+
|
|
133
|
+
## Practices
|
|
134
|
+
|
|
135
|
+
| Do | Skip |
|
|
136
|
+
|----|------|
|
|
137
|
+
| Independent tests | Shared `static mut` |
|
|
138
|
+
| Descriptive `snake_case` test names | Testing private functions directly |
|
|
139
|
+
| Property tests on parsers/crypto | Sleeping for timing—use mocks or `pause` |
|
|
140
|
+
| `cargo test -- --nocapture` while debugging | Ignored flaky tests without ticket |
|
|
141
|
+
|
|
142
|
+
Untrusted deserialization → combine proptest with **security-review**.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: services
|
|
3
|
+
description: Backend and API engineering — layered Node/Express/Next/Nest services, REST contracts, typed errors, ports-and-adapters boundaries, MCP servers. Use when designing APIs, server modules, persistence, jobs, or TypeScript MCP tooling.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Scope: design and implement server-side features with clear layers, stable HTTP contracts, and testable domain boundaries. Map persistence/cache/auth details to `techne:databases` and delivery to `techne:delivery` when those dominate the task. Use `biblos` MCP for library/API signatures; `atlas` MCP to locate existing modules. Session notes: `/session-handoff`. Reviews: `security-review` / `exo-security`, `exo-qa`; Nest/Node implementation work can delegate to `exo-backend`.
|
|
7
|
+
|
|
8
|
+
## Non-negotiable rules
|
|
9
|
+
|
|
10
|
+
1. **Thin transport** — controllers/routes parse/validate input, call application services, map results to HTTP; no business rules in handlers.
|
|
11
|
+
2. **REST semantics** — nouns in URLs, correct status codes (no `200` with `success: false`); validate bodies with schemas (Zod, class-validator, etc.).
|
|
12
|
+
3. **Error contract** — structured `{ error: { code, message, details? } }`; log full context server-side; never return stack traces or SQL to clients.
|
|
13
|
+
4. **Operational errors vs bugs** — typed/domain errors with stable `code`; unexpected failures become generic 500 after logging.
|
|
14
|
+
5. **Data access** — repository or port behind services; avoid N+1 (batch/join); select only needed columns; transactions for multi-write invariants.
|
|
15
|
+
6. **Caching** — cache-aside with explicit TTL and invalidation; production rate limits use shared stores (Redis/gateway), not per-process counters on scaled runtimes.
|
|
16
|
+
7. **AuthZ** — authenticate at the edge; authorize per resource in the service layer; JWT/session checks are not a substitute for server-side permission checks on mutations.
|
|
17
|
+
8. **Hexagonal slice** — domain free of framework imports; use cases depend on port interfaces; adapters map HTTP/ORM/SDK; wire in one composition root.
|
|
18
|
+
9. **Retries** — exponential backoff with jitter on transient failures only; do not retry most 4xx; propagate `abortSignal` on outbound HTTP.
|
|
19
|
+
10. **NestJS** — global `ValidationPipe` with `whitelist` + `forbidNonWhitelisted`; response DTOs, not ORM entities; env validated at bootstrap.
|
|
20
|
+
11. **MCP servers** — schema-first tools; transport isolated from tool logic; pin SDK versions; verify registration APIs via `biblos` MCP (SDKs change).
|
|
21
|
+
12. **Git hygiene** — conventional commit messages in English; no Co-Authored-By or AI attribution trailers; never skip git hooks or force-push shared branches.
|
|
22
|
+
|
|
23
|
+
## References
|
|
24
|
+
|
|
25
|
+
| File | Load when |
|
|
26
|
+
|------|-----------|
|
|
27
|
+
| [reference/backend-layers.md](reference/backend-layers.md) | Repositories, services, middleware, DB/cache/jobs/logging |
|
|
28
|
+
| [reference/api-contracts.md](reference/api-contracts.md) | REST shape, pagination, filtering, versioning, rate-limit headers |
|
|
29
|
+
| [reference/errors-and-resilience.md](reference/errors-and-resilience.md) | Error types, Result pattern, retries, user-facing messages |
|
|
30
|
+
| [reference/hexagonal-boundaries.md](reference/hexagonal-boundaries.md) | Ports/adapters layout, migration, testing by boundary |
|
|
31
|
+
| [reference/nestjs.md](reference/nestjs.md) | Modules, DTOs, guards, filters, config, Nest tests |
|
|
32
|
+
| [reference/mcp-servers.md](reference/mcp-servers.md) | Building MCP servers (tools/resources/transport) |
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# REST API contracts
|
|
2
|
+
|
|
3
|
+
## URLs and methods
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
GET /api/v1/orders
|
|
7
|
+
GET /api/v1/orders/:id
|
|
8
|
+
POST /api/v1/orders
|
|
9
|
+
PATCH /api/v1/orders/:id
|
|
10
|
+
DELETE /api/v1/orders/:id
|
|
11
|
+
POST /api/v1/orders/:id/cancel # non-CRUD actions — verbs sparingly
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Plural kebab-case nouns; no `/getOrders`. Sub-resources express ownership: `/users/:id/orders`.
|
|
15
|
+
|
|
16
|
+
## Status codes
|
|
17
|
+
|
|
18
|
+
| Code | When |
|
|
19
|
+
|------|------|
|
|
20
|
+
| 200 | GET/PATCH with body |
|
|
21
|
+
| 201 | POST create + `Location` |
|
|
22
|
+
| 204 | DELETE success, no body |
|
|
23
|
+
| 400 | Malformed JSON |
|
|
24
|
+
| 401 | Missing/invalid auth |
|
|
25
|
+
| 403 | Authenticated, forbidden |
|
|
26
|
+
| 404 | Missing resource |
|
|
27
|
+
| 409 | Conflict / duplicate |
|
|
28
|
+
| 422 | Valid JSON, invalid semantics |
|
|
29
|
+
| 429 | Rate limited + `Retry-After` |
|
|
30
|
+
| 503 | Overload + `Retry-After` |
|
|
31
|
+
|
|
32
|
+
Never encode failure only in JSON while returning HTTP 200.
|
|
33
|
+
|
|
34
|
+
## Envelopes
|
|
35
|
+
|
|
36
|
+
Public APIs often wrap:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{ "data": { … }, "meta": { "page": 1, "per_page": 20, "total": 142 } }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Errors:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"error": {
|
|
47
|
+
"code": "validation_error",
|
|
48
|
+
"message": "…",
|
|
49
|
+
"details": [{ "field": "email", "message": "…", "code": "invalid_format" }]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Internal APIs may return raw resources on success if status codes disambiguate.
|
|
55
|
+
|
|
56
|
+
## Pagination
|
|
57
|
+
|
|
58
|
+
| Style | When |
|
|
59
|
+
|-------|------|
|
|
60
|
+
| Offset `page`/`per_page` | Admin UIs, small tables, search with page numbers |
|
|
61
|
+
| Cursor `cursor` + `limit` | Feeds, large tables, public APIs |
|
|
62
|
+
|
|
63
|
+
Cursor: fetch `limit+1` to compute `has_next`; opaque cursor encoding.
|
|
64
|
+
|
|
65
|
+
## Filtering and sorting
|
|
66
|
+
|
|
67
|
+
Equality filters as query params; ranges via bracket notation (`price[gte]=10`); sort `sort=-created_at,name`; sparse fieldsets `fields=id,name`.
|
|
68
|
+
|
|
69
|
+
## Auth headers
|
|
70
|
+
|
|
71
|
+
`Authorization: Bearer …` for users; `X-API-Key` for service accounts when documented.
|
|
72
|
+
|
|
73
|
+
Resource auth: load entity, compare `order.userId` to caller; admin routes behind role guard **and** service check.
|
|
74
|
+
|
|
75
|
+
## Versioning
|
|
76
|
+
|
|
77
|
+
Start at `/api/v1`. Maintain at most two active versions. Breaking changes → new version; additive response fields / optional params → same version. Deprecate with `Sunset` header and timeline; eventually `410 Gone`.
|
|
78
|
+
|
|
79
|
+
## Rate limit headers
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
X-RateLimit-Limit: 100
|
|
83
|
+
X-RateLimit-Remaining: 95
|
|
84
|
+
X-RateLimit-Reset: 1700000000
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Typical tiers: anonymous per IP, authenticated per user, elevated per API key — tune to product policy.
|
|
88
|
+
|
|
89
|
+
## Ship checklist
|
|
90
|
+
|
|
91
|
+
Naming, method, status, schema validation, error shape, pagination on lists, authn/authz, rate limits, no leak of internals, OpenAPI updated, naming convention consistent (snake_case vs camelCase — pick one).
|
|
92
|
+
|
|
93
|
+
## Handler examples
|
|
94
|
+
|
|
95
|
+
### TypeScript (Next.js route)
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const bodySchema = z.object({ email: z.string().email(), name: z.string().min(1).max(100) });
|
|
99
|
+
|
|
100
|
+
export async function POST(req: NextRequest) {
|
|
101
|
+
const parsed = bodySchema.safeParse(await req.json());
|
|
102
|
+
if (!parsed.success) {
|
|
103
|
+
return NextResponse.json({
|
|
104
|
+
error: {
|
|
105
|
+
code: 'validation_error',
|
|
106
|
+
message: 'Invalid body',
|
|
107
|
+
details: parsed.error.issues.map((i) => ({
|
|
108
|
+
field: i.path.join('.'),
|
|
109
|
+
message: i.message,
|
|
110
|
+
code: i.code,
|
|
111
|
+
})),
|
|
112
|
+
},
|
|
113
|
+
}, { status: 422 });
|
|
114
|
+
}
|
|
115
|
+
const user = await users.create(parsed.data);
|
|
116
|
+
return NextResponse.json({ data: user }, {
|
|
117
|
+
status: 201,
|
|
118
|
+
headers: { Location: `/api/v1/users/${user.id}` },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Conflict mapping
|
|
124
|
+
|
|
125
|
+
Duplicate unique key → `409` with `code: 'email_taken'` (or domain-specific code), not `500`.
|
|
126
|
+
|
|
127
|
+
### List endpoint
|
|
128
|
+
|
|
129
|
+
Always cap `per_page` (e.g. max 100); default sort documented; filter whitelist to indexed columns where possible (`techne:databases` for index strategy).
|
|
130
|
+
|
|
131
|
+
### Python (DRF-style)
|
|
132
|
+
|
|
133
|
+
Separate write serializer from read serializer; `create` returns `201` + `Location`; validation errors become `400`/`422` with field keys — align JSON keys with TypeScript clients if both exist.
|
|
134
|
+
|
|
135
|
+
### Go `net/http`
|
|
136
|
+
|
|
137
|
+
Decode JSON → validate struct tags/custom `Validate()` → map `domain.Err*` with `errors.Is` → never branch on error string contents.
|
|
138
|
+
|
|
139
|
+
Framework-specific guides: `techne:python`, `techne:go`, `techne:java` when the stack is fixed.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Backend layers and infrastructure patterns
|
|
2
|
+
|
|
3
|
+
## REST handler shape
|
|
4
|
+
|
|
5
|
+
Resource URLs, query filters, pagination params at the edge; business rules in services.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface OrderRepo {
|
|
9
|
+
list(filter: ListFilter): Promise<Order[]>;
|
|
10
|
+
byId(id: string): Promise<Order | null>;
|
|
11
|
+
insert(dto: CreateOrder): Promise<Order>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class OrderService {
|
|
15
|
+
constructor(private repo: OrderRepo) {}
|
|
16
|
+
async search(q: string, limit = 20) {
|
|
17
|
+
const ids = await this.vectorIndex.search(q, limit);
|
|
18
|
+
const rows = await this.repo.byIds(ids);
|
|
19
|
+
return rows.sort((a, b) => ids.indexOf(a.id) - ids.indexOf(b.id));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Middleware
|
|
25
|
+
|
|
26
|
+
Compose cross-cutting concerns (auth, logging, rate limit) as wrappers or framework middleware; attach user/context before the handler runs.
|
|
27
|
+
|
|
28
|
+
## Database
|
|
29
|
+
|
|
30
|
+
- Project columns explicitly — avoid `SELECT *` in hot paths.
|
|
31
|
+
- Batch related lookups (map by id) instead of per-row queries in loops.
|
|
32
|
+
- Multi-step invariants: DB transaction or RPC — single commit/rollback boundary.
|
|
33
|
+
|
|
34
|
+
## Caching
|
|
35
|
+
|
|
36
|
+
Decorator/wrapper repo: read Redis → on miss load DB → `SETEX` with TTL; delete key on writes.
|
|
37
|
+
|
|
38
|
+
## Background work
|
|
39
|
+
|
|
40
|
+
Queue jobs from HTTP handlers when work is slow or unreliable; return `202`/accepted semantics if the API contract allows. In-process queues OK for dev only — production uses durable queues (`techne:delivery`).
|
|
41
|
+
|
|
42
|
+
## Auth helpers
|
|
43
|
+
|
|
44
|
+
Verify bearer JWT with explicit secret/config; map claims to a typed user; RBAC via role → permission table checked in services.
|
|
45
|
+
|
|
46
|
+
## Logging
|
|
47
|
+
|
|
48
|
+
Structured JSON logs: `timestamp`, `level`, `message`, `requestId`, `userId`, error fields. Never log secrets or raw tokens.
|
|
49
|
+
|
|
50
|
+
## Rate limiting
|
|
51
|
+
|
|
52
|
+
Implement at API gateway, Redis sliding window, or platform limiter — not in-memory per Node instance when horizontally scaled.
|
|
53
|
+
|
|
54
|
+
## Next.js / Express wiring
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
export function withAuth(handler: RouteHandler): RouteHandler {
|
|
58
|
+
return async (req, ctx) => {
|
|
59
|
+
const token = req.headers.get('authorization')?.replace(/^Bearer\s+/i, '');
|
|
60
|
+
if (!token) return NextResponse.json({ error: { code: 'unauthorized', message: 'Missing token' } }, { status: 401 });
|
|
61
|
+
try {
|
|
62
|
+
const user = await verifyAccessToken(token);
|
|
63
|
+
return handler(req, { ...ctx, user });
|
|
64
|
+
} catch {
|
|
65
|
+
return NextResponse.json({ error: { code: 'unauthorized', message: 'Invalid token' } }, { status: 401 });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
GraphQL APIs: same layering — resolvers stay thin; batch loaders kill N+1 (`DataLoader` pattern). Depth/complexity limits at the gateway.
|
|
72
|
+
|
|
73
|
+
## Simple in-process queue (dev only)
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
class MemoryQueue<T> {
|
|
77
|
+
private q: T[] = [];
|
|
78
|
+
private busy = false;
|
|
79
|
+
enqueue(item: T) {
|
|
80
|
+
this.q.push(item);
|
|
81
|
+
if (!this.busy) void this.drain();
|
|
82
|
+
}
|
|
83
|
+
private async drain() {
|
|
84
|
+
this.busy = true;
|
|
85
|
+
while (this.q.length) {
|
|
86
|
+
const job = this.q.shift()!;
|
|
87
|
+
try { await this.handle(job); } catch (e) { logger.error('job_failed', e); }
|
|
88
|
+
}
|
|
89
|
+
this.busy = false;
|
|
90
|
+
}
|
|
91
|
+
protected async handle(_job: T) { /* override */ }
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Production: durable queue + worker process (`techne:delivery`).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Errors and resilience
|
|
2
|
+
|
|
3
|
+
## Principles
|
|
4
|
+
|
|
5
|
+
1. Fail fast at boundaries; do not swallow `catch` without log, remap, or rethrow.
|
|
6
|
+
2. Errors are typed values with stable `code` for clients.
|
|
7
|
+
3. User-visible text ≠ engineer logs (no stacks in responses).
|
|
8
|
+
4. Document every error code clients may see.
|
|
9
|
+
|
|
10
|
+
## TypeScript hierarchy
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
export class ServiceError extends Error {
|
|
14
|
+
constructor(
|
|
15
|
+
message: string,
|
|
16
|
+
readonly code: string,
|
|
17
|
+
readonly httpStatus = 500,
|
|
18
|
+
readonly details?: unknown,
|
|
19
|
+
) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'ServiceError';
|
|
22
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class MissingEntityError extends ServiceError {
|
|
27
|
+
constructor(kind: string, id: string) {
|
|
28
|
+
super(`${kind} not found`, 'NOT_FOUND', 404, { id });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Central mapper maps `ServiceError`, validation library errors, and unknown → HTTP JSON envelope.
|
|
34
|
+
|
|
35
|
+
## Result type (expected failure)
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
type Outcome<T, E = ServiceError> =
|
|
39
|
+
| { ok: true; value: T }
|
|
40
|
+
| { ok: false; error: E };
|
|
41
|
+
|
|
42
|
+
async function loadUser(id: string): Promise<Outcome<User>> {
|
|
43
|
+
try {
|
|
44
|
+
const row = await db.user.findUnique({ where: { id } });
|
|
45
|
+
if (!row) return { ok: false, error: new MissingEntityError('User', id) };
|
|
46
|
+
return { ok: true, value: row };
|
|
47
|
+
} catch {
|
|
48
|
+
return { ok: false, error: new ServiceError('Storage failure', 'DB_ERROR') };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Retries
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
async function withBackoff<T>(
|
|
57
|
+
fn: () => Promise<T>,
|
|
58
|
+
opts: { attempts?: number; baseMs?: number; maxMs?: number; retry?: (e: unknown) => boolean } = {},
|
|
59
|
+
): Promise<T> {
|
|
60
|
+
const attempts = opts.attempts ?? 3;
|
|
61
|
+
let delay = opts.baseMs ?? 500;
|
|
62
|
+
const max = opts.maxMs ?? 10_000;
|
|
63
|
+
const retry = opts.retry ?? (() => true);
|
|
64
|
+
|
|
65
|
+
for (let i = 1; i <= attempts; i++) {
|
|
66
|
+
try {
|
|
67
|
+
return await fn();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (i === attempts || !retry(e)) throw e;
|
|
70
|
+
await new Promise((r) => setTimeout(r, delay + Math.random() * delay));
|
|
71
|
+
delay = Math.min(delay * 2, max);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error('unreachable');
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Retry transient 5xx/timeouts/network — not typical 4xx. Pass cancellation/`AbortSignal` to outbound HTTP.
|
|
79
|
+
|
|
80
|
+
## User-facing copy
|
|
81
|
+
|
|
82
|
+
Map `code` → short friendly string; keep codes in API JSON for clients that localize.
|
|
83
|
+
|
|
84
|
+
## Python
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
class ServiceError(Exception):
|
|
88
|
+
def __init__(self, message: str, code: str, status_code: int = 500):
|
|
89
|
+
super().__init__(message)
|
|
90
|
+
self.code = code
|
|
91
|
+
self.status_code = status_code
|
|
92
|
+
|
|
93
|
+
@app.exception_handler(ServiceError)
|
|
94
|
+
async def on_service_error(_: Request, exc: ServiceError) -> JSONResponse:
|
|
95
|
+
return JSONResponse(
|
|
96
|
+
status_code=exc.status_code,
|
|
97
|
+
content={"error": {"code": exc.code, "message": str(exc)}},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@app.exception_handler(Exception)
|
|
101
|
+
async def on_unexpected(_: Request, exc: Exception) -> JSONResponse:
|
|
102
|
+
logger.exception("unexpected")
|
|
103
|
+
return JSONResponse(status_code=500, content={
|
|
104
|
+
"error": {"code": "internal_error", "message": "An unexpected error occurred"},
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Deeper stack conventions: `techne:python`.
|
|
109
|
+
|
|
110
|
+
## Go
|
|
111
|
+
|
|
112
|
+
```go
|
|
113
|
+
var ErrNotFound = errors.New("not found")
|
|
114
|
+
|
|
115
|
+
func (r *Repo) ByID(ctx context.Context, id string) (*User, error) {
|
|
116
|
+
row, err := r.db.Query(ctx, id)
|
|
117
|
+
if errors.Is(err, sql.ErrNoRows) {
|
|
118
|
+
return nil, fmt.Errorf("user %s: %w", id, ErrNotFound)
|
|
119
|
+
}
|
|
120
|
+
return row, err
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func writeDomainError(w http.ResponseWriter, err error) {
|
|
124
|
+
switch {
|
|
125
|
+
case errors.Is(err, ErrNotFound):
|
|
126
|
+
writeJSON(w, 404, map[string]any{"error": map[string]string{"code": "not_found", "message": err.Error()}})
|
|
127
|
+
default:
|
|
128
|
+
slog.Error("handler", "err", err)
|
|
129
|
+
writeJSON(w, 500, map[string]any{"error": map[string]string{"code": "internal_error", "message": "An unexpected error occurred"}})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Deeper patterns: `techne:go`.
|
|
135
|
+
|
|
136
|
+
## UI boundary
|
|
137
|
+
|
|
138
|
+
Server-rendered apps: framework error boundaries for render failures (see `techne:react` / `techne:web` for front-end specifics).
|
|
139
|
+
|
|
140
|
+
## Circuit breaker (external dependencies)
|
|
141
|
+
|
|
142
|
+
For flaky third-party APIs: track failure rate in a sliding window; open circuit → fail fast with `503`/`SERVICE_UNAVAILABLE` and structured code; half-open probe after cooldown. Combine with timeouts and bulkheads (separate pool per dependency). Log state transitions.
|
|
143
|
+
|
|
144
|
+
## Checklist
|
|
145
|
+
|
|
146
|
+
Every catch handled; API envelope consistent; logs contain context; retries selective; async work awaited or explicitly fire-and-forget with monitoring.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Hexagonal (ports and adapters)
|
|
2
|
+
|
|
3
|
+
Business rules sit at the center; frameworks and I/O sit at the edge. Dependencies point inward.
|
|
4
|
+
|
|
5
|
+
## Layers
|
|
6
|
+
|
|
7
|
+
| Layer | Contains |
|
|
8
|
+
|-------|----------|
|
|
9
|
+
| Domain | Entities, value objects, pure policies — no HTTP/ORM imports |
|
|
10
|
+
| Application | Use cases, inbound/outbound port interfaces, DTOs |
|
|
11
|
+
| Adapters | HTTP controllers, ORM repos, message consumers, SDK wrappers |
|
|
12
|
+
| Composition | Wire concrete adapters to use cases once |
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
1. Define use case input/output DTOs (no `Request` types inside).
|
|
17
|
+
2. List outbound ports (`OrderRepositoryPort`, `PaymentPort`, `ClockPort`).
|
|
18
|
+
3. Implement use case orchestration injecting ports.
|
|
19
|
+
4. Inbound adapter maps HTTP/job payload → DTO → use case → HTTP response.
|
|
20
|
+
5. Outbound adapter implements port with SQL/SDK calls.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
export interface OrderStore {
|
|
24
|
+
save(order: Order): Promise<void>;
|
|
25
|
+
find(id: string): Promise<Order | null>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class PlaceOrder {
|
|
29
|
+
constructor(private store: OrderStore, private pay: PaymentPort) {}
|
|
30
|
+
async run(cmd: PlaceOrderCmd): Promise<PlaceOrderResult> {
|
|
31
|
+
const order = Order.create(cmd);
|
|
32
|
+
const auth = await this.pay.authorize({ amount: order.totalCents, id: order.id });
|
|
33
|
+
await this.store.save(order.markPaid(auth.id));
|
|
34
|
+
return { orderId: order.id };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Layout (feature-first)
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
src/features/billing/
|
|
43
|
+
domain/
|
|
44
|
+
application/ports/ + use-cases/
|
|
45
|
+
adapters/inbound/http/ + outbound/postgres/
|
|
46
|
+
composition/registerBilling.ts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Testing
|
|
50
|
+
|
|
51
|
+
- Domain: pure unit tests.
|
|
52
|
+
- Use case: fakes for ports.
|
|
53
|
+
- Adapter: contract tests per port; integration with real DB/API.
|
|
54
|
+
- E2E: inbound adapter through stack.
|
|
55
|
+
|
|
56
|
+
## Migration
|
|
57
|
+
|
|
58
|
+
Strangler: one vertical slice at a time; facade legacy behind ports; characterization tests before moves; centralize wiring early. Avoid big-bang rewrites.
|
|
59
|
+
|
|
60
|
+
## Anti-patterns
|
|
61
|
+
|
|
62
|
+
ORM entities in domain; use cases reading `req.query`; adapter-to-adapter calls bypassing use cases; hidden global singletons for dependencies.
|
|
63
|
+
|
|
64
|
+
## Multi-language
|
|
65
|
+
|
|
66
|
+
Same boundaries in Java/Kotlin (`application.port.in/out`), Go (small interfaces in application package, wire in `cmd/`), etc. Framework annotations stay in adapters only.
|
|
67
|
+
|
|
68
|
+
## Flow
|
|
69
|
+
|
|
70
|
+
```mermaid
|
|
71
|
+
flowchart LR
|
|
72
|
+
Client --> InboundAdapter
|
|
73
|
+
InboundAdapter --> UseCase
|
|
74
|
+
UseCase --> OutboundPort
|
|
75
|
+
OutboundAdapter --> OutboundPort
|
|
76
|
+
OutboundAdapter --> External[(DB / API / Queue)]
|
|
77
|
+
UseCase --> Domain
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Language packaging
|
|
81
|
+
|
|
82
|
+
| Runtime | Port location | Wiring |
|
|
83
|
+
|---------|---------------|--------|
|
|
84
|
+
| TypeScript | `application/ports` | factory module / Nest `Module` providers |
|
|
85
|
+
| Java | `application.port.in/out` | Spring `@Configuration` or manual |
|
|
86
|
+
| Kotlin | same as Java | Koin/Dagger/Spring modules |
|
|
87
|
+
| Go | interfaces beside use case | `main` or `wire/` package |
|
|
88
|
+
|
|
89
|
+
Inbound adapter responsibilities: protocol validation, auth extraction, map to command DTO, map domain errors to HTTP/status. Outbound: translate port calls to SQL/SDK, map infra errors to domain errors (no leaking driver types upward).
|
|
90
|
+
|
|
91
|
+
## Refactor sequence (repeat per slice)
|
|
92
|
+
|
|
93
|
+
Extract command + result types → introduce ports around existing DB calls → move orchestration from fat controller/service into use case → make legacy controller call use case → unit test use case with fakes → integration test one adapter → delete dead paths when parity proven.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# MCP server implementation
|
|
2
|
+
|
|
3
|
+
Model Context Protocol exposes **tools** (actions), **resources** (read-only URIs), and **prompts** (parameterized templates) to AI clients.
|
|
4
|
+
|
|
5
|
+
## SDK setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @modelcontextprotocol/sdk zod
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
|
|
15
|
+
const server = new McpServer({ name: 'acme-tools', version: '1.0.0' });
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Registration method names differ by SDK version (`tool`, `registerTool`, object vs positional args). **Confirm current signatures with `biblos` MCP** before copying examples.
|
|
19
|
+
|
|
20
|
+
## Transport
|
|
21
|
+
|
|
22
|
+
| Transport | Use |
|
|
23
|
+
|-----------|-----|
|
|
24
|
+
| stdio | Local IDE/desktop clients |
|
|
25
|
+
| Streamable HTTP | Remote agents (single MCP HTTP endpoint per spec) |
|
|
26
|
+
| Legacy SSE | Backward compatibility only |
|
|
27
|
+
|
|
28
|
+
Keep tool/resource handlers free of transport code; connect transport in the entrypoint.
|
|
29
|
+
|
|
30
|
+
## Tool design
|
|
31
|
+
|
|
32
|
+
- Zod (or SDK-native schema) for every input; describe side effects and return shape in the tool description.
|
|
33
|
+
- Return structured errors the model can reason about — not raw stack traces.
|
|
34
|
+
- Prefer idempotent tools when retries are likely.
|
|
35
|
+
- Document rate/cost for paid external APIs in the description.
|
|
36
|
+
- Pin `@modelcontextprotocol/sdk` version; read release notes on upgrade.
|
|
37
|
+
|
|
38
|
+
## Resources and prompts
|
|
39
|
+
|
|
40
|
+
Resources: read-only handlers keyed by URI. Prompts: reusable templates with typed arguments.
|
|
41
|
+
|
|
42
|
+
## Security
|
|
43
|
+
|
|
44
|
+
Validate and sanitize inputs; never expose secrets in tool responses; treat client-supplied URIs and fetched web content as untrusted data, not instructions.
|
|
45
|
+
|
|
46
|
+
## Official SDKs
|
|
47
|
+
|
|
48
|
+
TypeScript (`@modelcontextprotocol/sdk`), Go, C# — same conceptual split (capabilities vs transport).
|
|
49
|
+
|
|
50
|
+
For capability placement (rule vs skill vs MCP vs CLI), decide at product level; this file covers MCP implementation only.
|