groundwork-method 0.0.1 → 0.11.0
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/CHANGELOG.md +823 -0
- package/LICENSE +21 -0
- package/README.md +44 -29
- package/bin/groundwork.js +1723 -0
- package/dist/src/generators/add-capability/generator.d.ts +8 -0
- package/dist/src/generators/add-capability/generator.js +60 -0
- package/dist/src/generators/add-capability/generator.js.map +1 -0
- package/dist/src/generators/cli-app/generator.d.ts +9 -0
- package/dist/src/generators/cli-app/generator.js +140 -0
- package/dist/src/generators/cli-app/generator.js.map +1 -0
- package/dist/src/generators/docs-site/generator.d.ts +5 -0
- package/dist/src/generators/docs-site/generator.js +441 -0
- package/dist/src/generators/docs-site/generator.js.map +1 -0
- package/dist/src/generators/electron-app/generator.d.ts +6 -0
- package/dist/src/generators/electron-app/generator.js +261 -0
- package/dist/src/generators/electron-app/generator.js.map +1 -0
- package/dist/src/generators/flutter-app/generator.d.ts +6 -0
- package/dist/src/generators/flutter-app/generator.js +314 -0
- package/dist/src/generators/flutter-app/generator.js.map +1 -0
- package/dist/src/generators/go-microservice/generator.d.ts +8 -0
- package/dist/src/generators/go-microservice/generator.js +232 -0
- package/dist/src/generators/go-microservice/generator.js.map +1 -0
- package/dist/src/generators/nextjs-app/generator.d.ts +8 -0
- package/dist/src/generators/nextjs-app/generator.js +294 -0
- package/dist/src/generators/nextjs-app/generator.js.map +1 -0
- package/dist/src/generators/python-microservice/generator.d.ts +13 -0
- package/dist/src/generators/python-microservice/generator.js +265 -0
- package/dist/src/generators/python-microservice/generator.js.map +1 -0
- package/dist/src/generators/shared/brand-tokens.d.ts +89 -0
- package/dist/src/generators/shared/brand-tokens.js +308 -0
- package/dist/src/generators/shared/brand-tokens.js.map +1 -0
- package/dist/src/generators/shared/capabilities.d.ts +101 -0
- package/dist/src/generators/shared/capabilities.js +279 -0
- package/dist/src/generators/shared/capabilities.js.map +1 -0
- package/dist/src/generators/shared/provenance.d.ts +2 -0
- package/dist/src/generators/shared/provenance.js +85 -0
- package/dist/src/generators/shared/provenance.js.map +1 -0
- package/dist/src/generators/shared/scaffold-helpers.d.ts +72 -0
- package/dist/src/generators/shared/scaffold-helpers.js +309 -0
- package/dist/src/generators/shared/scaffold-helpers.js.map +1 -0
- package/dist/src/generators/system-test-runner/generator.d.ts +23 -0
- package/dist/src/generators/system-test-runner/generator.js +173 -0
- package/dist/src/generators/system-test-runner/generator.js.map +1 -0
- package/dist/src/generators/workspace-dev-cli/generator.d.ts +7 -0
- package/dist/src/generators/workspace-dev-cli/generator.js +138 -0
- package/dist/src/generators/workspace-dev-cli/generator.js.map +1 -0
- package/generators.json +57 -0
- package/lib/repo-map/grammars/tree-sitter-c.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-cpp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-csharp.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-dart.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-go.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-java.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-javascript.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-kotlin.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-lua.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-php.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-python.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-ruby.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-rust.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-scala.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-swift.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-tsx.wasm +0 -0
- package/lib/repo-map/grammars/tree-sitter-typescript.wasm +0 -0
- package/lib/repo-map/index.js +386 -0
- package/lib/repo-map/languages.js +514 -0
- package/lib/repo-map/pagerank.js +59 -0
- package/migrations/README.md +60 -0
- package/migrations/_template/cli-migration.js +27 -0
- package/migrations/gw-bet-prose-redesign.js +105 -0
- package/migrations/gw-drop-test-manifest.js +37 -0
- package/migrations/gw-register-serena-mcp.js +42 -0
- package/migrations/gw-relocate-hidden-skills.js +40 -0
- package/migrations/gw-seed-config-toml.js +24 -0
- package/migrations/index.json +40 -0
- package/package.json +70 -6
- package/src/AGENTS.md +36 -0
- package/src/config/config.toml +30 -0
- package/src/config/groundwork-state.json +5 -0
- package/src/docs/llms.txt +72 -0
- package/src/docs/principles/ai-native/agent-native-systems.md +90 -0
- package/src/docs/principles/ai-native/agentic-systems.md +78 -0
- package/src/docs/principles/ai-native/ai-engineering.md +100 -0
- package/src/docs/principles/ai-native/ai-native-product.md +76 -0
- package/src/docs/principles/delivery/cost-engineering.md +89 -0
- package/src/docs/principles/delivery/day-2-operational-baseline.md +57 -0
- package/src/docs/principles/delivery/devex.md +88 -0
- package/src/docs/principles/delivery/platform.md +101 -0
- package/src/docs/principles/delivery/progressive-delivery.md +92 -0
- package/src/docs/principles/design/ai-native-design.md +73 -0
- package/src/docs/principles/design/design-foundations.md +80 -0
- package/src/docs/principles/design/design-systems-and-tokens.md +72 -0
- package/src/docs/principles/design/interaction-and-motion.md +69 -0
- package/src/docs/principles/design/layout-and-space.md +72 -0
- package/src/docs/principles/design/usability-and-ux.md +79 -0
- package/src/docs/principles/design/visual-design.md +84 -0
- package/src/docs/principles/foundations/code-craft.md +86 -0
- package/src/docs/principles/foundations/continuous-discovery.md +75 -0
- package/src/docs/principles/foundations/documentation.md +102 -0
- package/src/docs/principles/foundations/prioritization-and-appetite.md +78 -0
- package/src/docs/principles/foundations/product-engineering.md +90 -0
- package/src/docs/principles/foundations/product-risks.md +89 -0
- package/src/docs/principles/foundations/requirements-and-specs.md +80 -0
- package/src/docs/principles/foundations/success-metrics.md +66 -0
- package/src/docs/principles/foundations/testing.md +108 -0
- package/src/docs/principles/index.md +24 -0
- package/src/docs/principles/quality/accessibility.md +88 -0
- package/src/docs/principles/quality/observability.md +84 -0
- package/src/docs/principles/quality/performance.md +84 -0
- package/src/docs/principles/quality/privacy.md +92 -0
- package/src/docs/principles/quality/reliability.md +89 -0
- package/src/docs/principles/quality/security.md +78 -0
- package/src/docs/principles/stack/postgres.md +100 -0
- package/src/docs/principles/system-design/api-design.md +86 -0
- package/src/docs/principles/system-design/architecture-decisions.md +81 -0
- package/src/docs/principles/system-design/code-structure.md +104 -0
- package/src/docs/principles/system-design/data-engineering.md +87 -0
- package/src/docs/principles/system-design/durable-execution.md +89 -0
- package/src/docs/principles/system-design/evolutionary-architecture.md +81 -0
- package/src/docs/principles/system-design/identity-and-access.md +76 -0
- package/src/docs/principles/system-design/integration-patterns.md +84 -0
- package/src/docs/principles/system-design/real-time.md +83 -0
- package/src/docs/principles/system-design/surface-architecture.md +74 -0
- package/src/docs/ways-of-working/documentation.md +69 -0
- package/src/docs/ways-of-working/how-we-work.md +76 -0
- package/src/docs/ways-of-working/units-of-work.md +40 -0
- package/src/engineer-skills/groundwork-electron-engineer/SKILL.md +123 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/documentation.md +126 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/performance-and-reliability.md +80 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/process-model.md +94 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/security.md +107 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/testing-and-smoke.md +129 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/theming-and-tokens.md +74 -0
- package/src/engineer-skills/groundwork-electron-engineer/sync-anchor.md +22 -0
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +114 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/accessibility.md +92 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/architecture.md +189 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/data-and-contracts.md +136 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/documentation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/navigation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/performance-and-reliability.md +100 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/platform-channels.md +93 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/releases-and-distribution.md +84 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/security.md +96 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/state-management.md +166 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +160 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/theming-and-design-tokens.md +109 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/widgets-and-composition.md +123 -0
- package/src/engineer-skills/groundwork-flutter-engineer/sync-anchor.md +24 -0
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +174 -0
- package/src/engineer-skills/groundwork-go-engineer/references/api-design.md +82 -0
- package/src/engineer-skills/groundwork-go-engineer/references/architecture.md +42 -0
- package/src/engineer-skills/groundwork-go-engineer/references/capability-ports.md +50 -0
- package/src/engineer-skills/groundwork-go-engineer/references/code-craft-security.md +34 -0
- package/src/engineer-skills/groundwork-go-engineer/references/concurrency.md +108 -0
- package/src/engineer-skills/groundwork-go-engineer/references/documentation.md +130 -0
- package/src/engineer-skills/groundwork-go-engineer/references/go-services.md +77 -0
- package/src/engineer-skills/groundwork-go-engineer/references/http-handlers.md +172 -0
- package/src/engineer-skills/groundwork-go-engineer/references/implementation-patterns.md +156 -0
- package/src/engineer-skills/groundwork-go-engineer/references/integration-realtime-data.md +57 -0
- package/src/engineer-skills/groundwork-go-engineer/references/observability.md +49 -0
- package/src/engineer-skills/groundwork-go-engineer/references/postgres.md +41 -0
- package/src/engineer-skills/groundwork-go-engineer/references/reliability-performance.md +105 -0
- package/src/engineer-skills/groundwork-go-engineer/references/testing.md +201 -0
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +20 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +112 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/accessibility.md +111 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/architecture.md +323 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/data-fetching.md +458 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/documentation.md +324 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/error-boundaries.md +383 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/mutations-and-forms.md +396 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/observability.md +48 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/performance-and-deployment.md +947 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/routing-and-navigation.md +405 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/security.md +131 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/server-components.md +394 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/tailwind-and-styling.md +134 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/testing.md +491 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/type-system.md +368 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/ux-principles.md +230 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +16 -0
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +199 -0
- package/src/engineer-skills/groundwork-python-engineer/references/api-standards.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/architecture.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/async-patterns.md +103 -0
- package/src/engineer-skills/groundwork-python-engineer/references/capability-ports.md +44 -0
- package/src/engineer-skills/groundwork-python-engineer/references/database.md +88 -0
- package/src/engineer-skills/groundwork-python-engineer/references/documentation-mcp.md +167 -0
- package/src/engineer-skills/groundwork-python-engineer/references/implementation-patterns.md +166 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-pipelines.md +119 -0
- package/src/engineer-skills/groundwork-python-engineer/references/ml-systems-ai-engineering.md +74 -0
- package/src/engineer-skills/groundwork-python-engineer/references/observability.md +57 -0
- package/src/engineer-skills/groundwork-python-engineer/references/resilience.md +126 -0
- package/src/engineer-skills/groundwork-python-engineer/references/security.md +148 -0
- package/src/engineer-skills/groundwork-python-engineer/references/testing.md +216 -0
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +20 -0
- package/src/generators/add-capability/generator.ts +70 -0
- package/src/generators/add-capability/schema.json +30 -0
- package/src/generators/capabilities/llm/capability.json +28 -0
- package/src/generators/capabilities/llm/providers/anthropic/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/anthropic/stacks/python/src/__packageName__/adapters/llm.py.template +61 -0
- package/src/generators/capabilities/llm/providers/local/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/local/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/local/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/localai/footprint.json +29 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/localai/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/none/footprint.json +9 -0
- package/src/generators/capabilities/llm/providers/none/stacks/go/internal/llm/llm.go.template +35 -0
- package/src/generators/capabilities/llm/providers/none/stacks/python/src/__packageName__/adapters/llm.py.template +25 -0
- package/src/generators/capabilities/llm/providers/ollama/footprint.json +20 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/go/internal/llm/llm.go.template +102 -0
- package/src/generators/capabilities/llm/providers/ollama/stacks/python/src/__packageName__/adapters/llm.py.template +53 -0
- package/src/generators/capabilities/llm/providers/openai/footprint.json +13 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/go/internal/llm/llm.go.template +98 -0
- package/src/generators/capabilities/llm/providers/openai/stacks/python/src/__packageName__/adapters/llm.py.template +60 -0
- package/src/generators/capabilities/llm/stacks/go/internal/core/service/llm.go.template +12 -0
- package/src/generators/capabilities/llm/stacks/go/internal/llm/llm_test.go.template +33 -0
- package/src/generators/capabilities/llm/stacks/python/src/__packageName__/core/llm.py.template +15 -0
- package/src/generators/capabilities/llm/stacks/python/tests/contracts/test_llm.py.template +37 -0
- package/src/generators/cli-app/files/README.md.template +76 -0
- package/src/generators/cli-app/files/build.mjs.template +15 -0
- package/src/generators/cli-app/files/package.json.template +21 -0
- package/src/generators/cli-app/files/src/cli.ts.template +67 -0
- package/src/generators/cli-app/files/src/commands/hello.ts.template +17 -0
- package/src/generators/cli-app/files/src/commands/status.ts.template +23 -0
- package/src/generators/cli-app/files/src/core/client.test.ts.template +80 -0
- package/src/generators/cli-app/files/src/core/client.ts.template +64 -0
- package/src/generators/cli-app/files/src/registry.test.ts.template +35 -0
- package/src/generators/cli-app/files/src/registry.ts.template +31 -0
- package/src/generators/cli-app/files/tsconfig.json.template +16 -0
- package/src/generators/cli-app/files/tsconfig.test.json.template +11 -0
- package/src/generators/cli-app/generator.ts +138 -0
- package/src/generators/cli-app/schema.json +24 -0
- package/src/generators/docs-site/files/.gitignore.ejs +40 -0
- package/src/generators/docs-site/files/app/docs/__slug__/page.tsx +101 -0
- package/src/generators/docs-site/files/app/docs/layout.tsx +14 -0
- package/src/generators/docs-site/files/app/docs.css +43 -0
- package/src/generators/docs-site/files/app/layout.tsx +24 -0
- package/src/generators/docs-site/files/app/page.tsx +135 -0
- package/src/generators/docs-site/files/app/source.ts +8 -0
- package/src/generators/docs-site/files/components/mermaid.tsx +67 -0
- package/src/generators/docs-site/files/next.config.mjs +10 -0
- package/src/generators/docs-site/files/package.json +32 -0
- package/src/generators/docs-site/files/pnpm-workspace.yaml +7 -0
- package/src/generators/docs-site/files/postcss.config.mjs +6 -0
- package/src/generators/docs-site/files/source.config.ts +77 -0
- package/src/generators/docs-site/files/tailwind.config.js +10 -0
- package/src/generators/docs-site/files/tsconfig.json +27 -0
- package/src/generators/docs-site/generator.ts +476 -0
- package/src/generators/docs-site/schema.json +17 -0
- package/src/generators/electron-app/docs/principles/stack/electron/index.md +49 -0
- package/src/generators/electron-app/docs/principles/stack/electron/ipc-contracts.md +71 -0
- package/src/generators/electron-app/docs/principles/stack/electron/packaging-and-updates.md +59 -0
- package/src/generators/electron-app/docs/principles/stack/electron/process-model.md +53 -0
- package/src/generators/electron-app/docs/principles/stack/electron/security.md +70 -0
- package/src/generators/electron-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/electron-app/files/.gitignore.template +20 -0
- package/src/generators/electron-app/files/README.md.template +125 -0
- package/src/generators/electron-app/files/electron.vite.config.ts +31 -0
- package/src/generators/electron-app/files/eslint.config.mjs +92 -0
- package/src/generators/electron-app/files/forge.config.ts.template +44 -0
- package/src/generators/electron-app/files/package.json.template +54 -0
- package/src/generators/electron-app/files/playwright.config.ts +18 -0
- package/src/generators/electron-app/files/project.json.template +65 -0
- package/src/generators/electron-app/files/src/main/core-client.test.ts +81 -0
- package/src/generators/electron-app/files/src/main/core-client.ts +55 -0
- package/src/generators/electron-app/files/src/main/index.ts +157 -0
- package/src/generators/electron-app/files/src/main/ipc.ts +52 -0
- package/src/generators/electron-app/files/src/main/policy.test.ts +71 -0
- package/src/generators/electron-app/files/src/main/policy.ts +73 -0
- package/src/generators/electron-app/files/src/preload/index.ts +23 -0
- package/src/generators/electron-app/files/src/renderer/index.html.template +20 -0
- package/src/generators/electron-app/files/src/renderer/src/App.test.tsx +61 -0
- package/src/generators/electron-app/files/src/renderer/src/App.tsx.template +43 -0
- package/src/generators/electron-app/files/src/renderer/src/assets/main.css +40 -0
- package/src/generators/electron-app/files/src/renderer/src/env.d.ts +14 -0
- package/src/generators/electron-app/files/src/renderer/src/main.tsx +25 -0
- package/src/generators/electron-app/files/src/shared/ipc.ts +54 -0
- package/src/generators/electron-app/files/tests/smoke/app.spec.ts.template +133 -0
- package/src/generators/electron-app/files/tool/electron_exec.sh.template +83 -0
- package/src/generators/electron-app/files/tsconfig.json +7 -0
- package/src/generators/electron-app/files/tsconfig.node.json +27 -0
- package/src/generators/electron-app/files/tsconfig.web.json +22 -0
- package/src/generators/electron-app/files/vitest.config.ts +32 -0
- package/src/generators/electron-app/files/vitest.setup.ts +1 -0
- package/src/generators/electron-app/generator.ts +288 -0
- package/src/generators/electron-app/schema.json +23 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/architecture.md +78 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/index.md +38 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/platform-channels.md +51 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/releases-and-distribution.md +59 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/state-management.md +85 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/testing.md +86 -0
- package/src/generators/flutter-app/docs/principles/stack/flutter/widgets-and-composition.md +69 -0
- package/src/generators/flutter-app/files/.gitignore.template +30 -0
- package/src/generators/flutter-app/files/README.md.template +100 -0
- package/src/generators/flutter-app/files/analysis_options.yaml.template +18 -0
- package/src/generators/flutter-app/files/integration_test/app_test.dart.template +64 -0
- package/src/generators/flutter-app/files/lib/app.dart.template +24 -0
- package/src/generators/flutter-app/files/lib/config/app_config.dart +15 -0
- package/src/generators/flutter-app/files/lib/data/repositories/status_repository.dart +36 -0
- package/src/generators/flutter-app/files/lib/data/services/api_client.dart +71 -0
- package/src/generators/flutter-app/files/lib/domain/models/health_status.dart +23 -0
- package/src/generators/flutter-app/files/lib/main.dart +11 -0
- package/src/generators/flutter-app/files/lib/router.dart +23 -0
- package/src/generators/flutter-app/files/lib/ui/core/theme/app_theme.dart +110 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view.dart +89 -0
- package/src/generators/flutter-app/files/lib/ui/home/home_view_model.dart.template +38 -0
- package/src/generators/flutter-app/files/project.json.template +51 -0
- package/src/generators/flutter-app/files/pubspec.yaml.template +47 -0
- package/src/generators/flutter-app/files/test/api_client_test.dart.template +63 -0
- package/src/generators/flutter-app/files/test/fakes/fake_status_repository.dart.template +19 -0
- package/src/generators/flutter-app/files/test/home_view_test.dart.template +58 -0
- package/src/generators/flutter-app/files/tool/flutter_exec.sh.template +60 -0
- package/src/generators/flutter-app/generator.ts +362 -0
- package/src/generators/flutter-app/schema.json +23 -0
- package/src/generators/go-microservice/docs/principles/stack/go/concurrency.md +123 -0
- package/src/generators/go-microservice/docs/principles/stack/go/index.md +70 -0
- package/src/generators/go-microservice/docs/principles/stack/go/testing.md +168 -0
- package/src/generators/go-microservice/files/.air.toml.template +38 -0
- package/src/generators/go-microservice/files/.env.template +4 -0
- package/src/generators/go-microservice/files/.golangci.yml.template +82 -0
- package/src/generators/go-microservice/files/Dockerfile.dev.template +12 -0
- package/src/generators/go-microservice/files/asyncapi-pubsub.yaml.template +33 -0
- package/src/generators/go-microservice/files/asyncapi-ws.yaml.template +34 -0
- package/src/generators/go-microservice/files/cmd/api/main.go.template +149 -0
- package/src/generators/go-microservice/files/cmd/api/main_test.go.template +99 -0
- package/src/generators/go-microservice/files/cmd/worker/cleanup/main.go.template +39 -0
- package/src/generators/go-microservice/files/db/schema.sql.template +24 -0
- package/src/generators/go-microservice/files/go.mod.template +39 -0
- package/src/generators/go-microservice/files/internal/config/config.go.template +52 -0
- package/src/generators/go-microservice/files/internal/config/otel.go.template +93 -0
- package/src/generators/go-microservice/files/internal/core/domain/errors.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/domain/model.go.template +28 -0
- package/src/generators/go-microservice/files/internal/core/domain/user.go.template +13 -0
- package/src/generators/go-microservice/files/internal/core/pagination.go.template +16 -0
- package/src/generators/go-microservice/files/internal/core/service/app_service.go.template +79 -0
- package/src/generators/go-microservice/files/internal/core/service/event_hub.go.template +9 -0
- package/src/generators/go-microservice/files/internal/core/service/message_queue.go.template +10 -0
- package/src/generators/go-microservice/files/internal/core/service/outbox_repository.go.template +31 -0
- package/src/generators/go-microservice/files/internal/core/service/repository.go.template +23 -0
- package/src/generators/go-microservice/files/internal/core/service/user_repository.go.template +15 -0
- package/src/generators/go-microservice/files/internal/core/service/user_service.go.template +43 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/app_handler.go.template +108 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/auth_middleware_test.go.template +52 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook.go.template +202 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/clerk_webhook_test.go.template +82 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/health_handler.go.template +80 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware.go.template +87 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/middleware_test.go.template +76 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/idempotency/repository.go.template +37 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_auth.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_loadshed.go.template +38 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_logging.go.template +40 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_ratelimit.go.template +48 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/middleware_test.go.template +81 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/router.go.template +105 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/types.go.template +70 -0
- package/src/generators/go-microservice/files/internal/entrypoints/api/websocket_handler.go.template +39 -0
- package/src/generators/go-microservice/files/internal/httpclient/http_client.go.template +87 -0
- package/src/generators/go-microservice/files/internal/kafka/kafka.go.template +34 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres.go.template +195 -0
- package/src/generators/go-microservice/files/internal/postgres/postgres_test.go.template +156 -0
- package/src/generators/go-microservice/files/internal/postgres/user_repository.go.template +56 -0
- package/src/generators/go-microservice/files/internal/pubsub/gcp_pubsub.go.template +35 -0
- package/src/generators/go-microservice/files/internal/websocket/client.go.template +151 -0
- package/src/generators/go-microservice/files/internal/websocket/hub.go.template +261 -0
- package/src/generators/go-microservice/files/scripts/apply-schema.sh.template +21 -0
- package/src/generators/go-microservice/files/tools/tools.go.template +10 -0
- package/src/generators/go-microservice/generator.ts +240 -0
- package/src/generators/go-microservice/schema.json +63 -0
- package/src/generators/nextjs-app/docs/principles/stack/typescript/frontend.md +65 -0
- package/src/generators/nextjs-app/files/.dockerignore.template +7 -0
- package/src/generators/nextjs-app/files/.env.example.template +24 -0
- package/src/generators/nextjs-app/files/.gitignore.template +5 -0
- package/src/generators/nextjs-app/files/Dockerfile +53 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-in/__sign-in__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/(auth)/sign-up/__sign-up__/page.tsx.template +9 -0
- package/src/generators/nextjs-app/files/app/api/config/route.ts.template +39 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.test.ts +15 -0
- package/src/generators/nextjs-app/files/app/api/healthz/route.ts +5 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.test.ts.template +55 -0
- package/src/generators/nextjs-app/files/app/api/proxy/__path__/route.ts.template +126 -0
- package/src/generators/nextjs-app/files/app/error.tsx +39 -0
- package/src/generators/nextjs-app/files/app/global-error.tsx +68 -0
- package/src/generators/nextjs-app/files/app/globals.css +105 -0
- package/src/generators/nextjs-app/files/app/layout.tsx +59 -0
- package/src/generators/nextjs-app/files/app/loading.tsx +13 -0
- package/src/generators/nextjs-app/files/app/not-found.tsx +30 -0
- package/src/generators/nextjs-app/files/app/page.tsx +20 -0
- package/src/generators/nextjs-app/files/components/providers/default.tsx +19 -0
- package/src/generators/nextjs-app/files/components/providers/production.tsx +32 -0
- package/src/generators/nextjs-app/files/components/providers/telemetry.tsx +76 -0
- package/src/generators/nextjs-app/files/components/render-smoke.test.tsx +29 -0
- package/src/generators/nextjs-app/files/components/theme-provider.tsx +11 -0
- package/src/generators/nextjs-app/files/components.json +21 -0
- package/src/generators/nextjs-app/files/eslint.config.mjs +120 -0
- package/src/generators/nextjs-app/files/hooks/use-toast.ts +7 -0
- package/src/generators/nextjs-app/files/instrumentation.ts +90 -0
- package/src/generators/nextjs-app/files/lib/api/fetcher.ts.template +130 -0
- package/src/generators/nextjs-app/files/lib/config.ts +21 -0
- package/src/generators/nextjs-app/files/lib/logger.ts +29 -0
- package/src/generators/nextjs-app/files/lib/schemas/index.ts +19 -0
- package/src/generators/nextjs-app/files/lib/utils.ts +6 -0
- package/src/generators/nextjs-app/files/next.config.mjs +9 -0
- package/src/generators/nextjs-app/files/package.json +70 -0
- package/src/generators/nextjs-app/files/postcss.config.mjs +8 -0
- package/src/generators/nextjs-app/files/proxy.test.ts.template +30 -0
- package/src/generators/nextjs-app/files/proxy.ts +31 -0
- package/src/generators/nextjs-app/files/public/.gitkeep +1 -0
- package/src/generators/nextjs-app/files/tsconfig.json +42 -0
- package/src/generators/nextjs-app/files/vitest.config.mts +15 -0
- package/src/generators/nextjs-app/files/vitest.setup.ts +7 -0
- package/src/generators/nextjs-app/generator.ts +307 -0
- package/src/generators/nextjs-app/schema.json +44 -0
- package/src/generators/python-microservice/docs/principles/stack/python/async.md +168 -0
- package/src/generators/python-microservice/docs/principles/stack/python/documentation.md +240 -0
- package/src/generators/python-microservice/docs/principles/stack/python/mcp.md +147 -0
- package/src/generators/python-microservice/docs/principles/stack/python/resilience.md +193 -0
- package/src/generators/python-microservice/docs/principles/stack/python/testing.md +322 -0
- package/src/generators/python-microservice/files/.env.example.template +30 -0
- package/src/generators/python-microservice/files/Dockerfile.template +36 -0
- package/src/generators/python-microservice/files/db/schema.sql.template +19 -0
- package/src/generators/python-microservice/files/pyproject.toml.template +76 -0
- package/src/generators/python-microservice/files/scripts/apply-schema.sh.template +25 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/comfyui.py.template +87 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/config.py.template +48 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/database.py.template +21 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/message_queue.py.template +29 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/repository.py.template +130 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/telemetry.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/adapters/websocket_hub.py.template +36 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/entities.py.template +22 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/domain/exceptions.py.template +43 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/ports.py.template +42 -0
- package/src/generators/python-microservice/files/src/__packageName__/core/service/example_service.py.template +68 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/dependencies.py.template +50 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/middleware.py.template +131 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/router.py.template +37 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/api/websocket_handler.py.template +20 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/cleanup.py.template +35 -0
- package/src/generators/python-microservice/files/src/__packageName__/entrypoints/worker/worker.py.template +28 -0
- package/src/generators/python-microservice/files/src/__packageName__/main.py.template +108 -0
- package/src/generators/python-microservice/files/tests/test_main.py.template +74 -0
- package/src/generators/python-microservice/files/tests/test_middleware.py.template +109 -0
- package/src/generators/python-microservice/files/tests/test_worker.py.template +16 -0
- package/src/generators/python-microservice/generator.ts +286 -0
- package/src/generators/python-microservice/schema.json +86 -0
- package/src/generators/shared/brand-tokens.ts +301 -0
- package/src/generators/shared/capabilities.ts +349 -0
- package/src/generators/shared/provenance.ts +61 -0
- package/src/generators/shared/scaffold-helpers.ts +309 -0
- package/src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md +20 -0
- package/src/generators/system-test-runner/files/tests/bets/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/bets/_archive/.gitkeep +0 -0
- package/src/generators/system-test-runner/files/tests/conftest.py.template +503 -0
- package/src/generators/system-test-runner/files/tests/pyproject.toml.template +20 -0
- package/src/generators/system-test-runner/files/tests/system/pages/__init__.py.template +9 -0
- package/src/generators/system-test-runner/files/tests/system/pages/base_page.py.template +36 -0
- package/src/generators/system-test-runner/files/tests/system/test_a11y_smoke.py.template +132 -0
- package/src/generators/system-test-runner/files/tests/system/test_contract_conformance.py.template +140 -0
- package/src/generators/system-test-runner/files/tests/system/test_layout_geometry.py.template +109 -0
- package/src/generators/system-test-runner/files/tests/system/test_render_smoke.py.template +257 -0
- package/src/generators/system-test-runner/files/tests/system/test_system.py.template +158 -0
- package/src/generators/system-test-runner/files/tests/system/test_token_conformance.py.template +206 -0
- package/src/generators/system-test-runner/files/tests/system/test_visual_regression.py.template +104 -0
- package/src/generators/system-test-runner/generator.ts +196 -0
- package/src/generators/system-test-runner/schema.json +24 -0
- package/src/generators/workspace-dev-cli/cli-src/build.mjs +42 -0
- package/src/generators/workspace-dev-cli/cli-src/dist/dev-bundle.js +2168 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/bet.ts +442 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/completion.ts +87 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/doctor.ts +139 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/lifecycle.ts +548 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/quality.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/commands/surface.ts +214 -0
- package/src/generators/workspace-dev-cli/cli-src/src/index.ts +127 -0
- package/src/generators/workspace-dev-cli/cli-src/src/registry.ts +194 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/color.ts +130 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/render.ts +158 -0
- package/src/generators/workspace-dev-cli/cli-src/src/theme/tokens.ts +122 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/context.ts +43 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/extensions.ts +99 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/paths.ts +46 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/proc.ts +106 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/prompt.ts +108 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/runners.ts +70 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/services.ts +221 -0
- package/src/generators/workspace-dev-cli/cli-src/src/util/version.ts +21 -0
- package/src/generators/workspace-dev-cli/cli-src/tsconfig.json +16 -0
- package/src/generators/workspace-dev-cli/files/.agents/skills/workspace-cli/SKILL.md.template +74 -0
- package/src/generators/workspace-dev-cli/files/dev.template +16 -0
- package/src/generators/workspace-dev-cli/files/docker-compose.yml.template +20 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/milestone-test.pytmpl.template +46 -0
- package/src/generators/workspace-dev-cli/files/scripts/cli/templates/slice-test.pytmpl.template +38 -0
- package/src/generators/workspace-dev-cli/generator.ts +136 -0
- package/src/generators/workspace-dev-cli/schema.json +22 -0
- package/src/hidden-skills/code-intelligence.md +135 -0
- package/src/hidden-skills/groundwork-architect/SKILL.md +114 -0
- package/src/hidden-skills/groundwork-architect/references/agentic-systems.md +44 -0
- package/src/hidden-skills/groundwork-architect/references/ai-native-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/api-and-contracts.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/core-and-boundaries.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/data-architecture.md +33 -0
- package/src/hidden-skills/groundwork-architect/references/decision-records.md +34 -0
- package/src/hidden-skills/groundwork-architect/references/durable-execution.md +45 -0
- package/src/hidden-skills/groundwork-architect/references/evolutionary-architecture.md +37 -0
- package/src/hidden-skills/groundwork-architect/references/identity-and-access.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/integration-patterns.md +39 -0
- package/src/hidden-skills/groundwork-architect/references/observability.md +36 -0
- package/src/hidden-skills/groundwork-architect/references/performance-and-scale.md +41 -0
- package/src/hidden-skills/groundwork-architect/references/platform-and-delivery.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/realtime-and-async.md +28 -0
- package/src/hidden-skills/groundwork-architect/references/reliability.md +31 -0
- package/src/hidden-skills/groundwork-architect/references/security-and-trust.md +47 -0
- package/src/hidden-skills/groundwork-architect/references/surface-architecture.md +40 -0
- package/src/hidden-skills/groundwork-architect/sync-anchor.md +34 -0
- package/src/hidden-skills/groundwork-architecture/architecture-template.md +50 -0
- package/src/hidden-skills/groundwork-architecture/instructions.md +139 -0
- package/src/hidden-skills/groundwork-architecture/phases/01-context-ingestion.md +18 -0
- package/src/hidden-skills/groundwork-architecture/phases/02-technical-constraints.md +27 -0
- package/src/hidden-skills/groundwork-architecture/phases/03-service-design.md +19 -0
- package/src/hidden-skills/groundwork-architecture/phases/04-data-flow-communication.md +23 -0
- package/src/hidden-skills/groundwork-architecture/phases/05-component-boundaries-contracts.md +17 -0
- package/src/hidden-skills/groundwork-architecture/phases/06-draft-review-present.md +38 -0
- package/src/hidden-skills/groundwork-architecture/phases/07-commit.md +33 -0
- package/src/hidden-skills/groundwork-architecture/templates/architecture-cache.md +43 -0
- package/src/hidden-skills/groundwork-architecture-extract/instructions.md +163 -0
- package/src/hidden-skills/groundwork-architecture-extract/templates/architecture-extract-cache.md +21 -0
- package/src/hidden-skills/groundwork-bet/briefs/acceptance-auditor.md +68 -0
- package/src/hidden-skills/groundwork-bet/briefs/blind-reviewer.md +56 -0
- package/src/hidden-skills/groundwork-bet/briefs/coverage-auditor.md +95 -0
- package/src/hidden-skills/groundwork-bet/briefs/edge-case-tracer.md +64 -0
- package/src/hidden-skills/groundwork-bet/briefs/experience-auditor.md +83 -0
- package/src/hidden-skills/groundwork-bet/briefs/slice-worker.md +257 -0
- package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +115 -0
- package/src/hidden-skills/groundwork-bet/templates/change-proposal.md +38 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/meta.json +4 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/milestone-index.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +31 -0
- package/src/hidden-skills/groundwork-bet/templates/pitch.md +45 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/01-ui-design.md +51 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/02-data-flows.md +36 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/03-api-design.md +90 -0
- package/src/hidden-skills/groundwork-bet/templates/technical-design/04-data-design.md +29 -0
- package/src/hidden-skills/groundwork-bet/workflows/01-discovery.md +200 -0
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +178 -0
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +242 -0
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +226 -0
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +210 -0
- package/src/hidden-skills/groundwork-design-system/instructions.md +125 -0
- package/src/hidden-skills/groundwork-design-system/templates/brand-tokens.md +182 -0
- package/src/hidden-skills/groundwork-design-system/templates/design-system-cache.md +64 -0
- package/src/hidden-skills/groundwork-design-system/tracks/_foundation.md +136 -0
- package/src/hidden-skills/groundwork-design-system/tracks/agentic-protocol.md +269 -0
- package/src/hidden-skills/groundwork-design-system/tracks/cli.md +355 -0
- package/src/hidden-skills/groundwork-design-system/tracks/graphical-ui.md +330 -0
- package/src/hidden-skills/groundwork-design-system-extract/instructions.md +124 -0
- package/src/hidden-skills/groundwork-design-system-extract/templates/design-system-extract-cache.md +19 -0
- package/src/hidden-skills/groundwork-designer/SKILL.md +108 -0
- package/src/hidden-skills/groundwork-designer/references/accessibility.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/ai-native-design.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/design-review.md +29 -0
- package/src/hidden-skills/groundwork-designer/references/design-systems-and-tokens.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/interaction-and-motion.md +37 -0
- package/src/hidden-skills/groundwork-designer/references/layout-and-space.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/usability-and-ux.md +33 -0
- package/src/hidden-skills/groundwork-designer/references/visual-craft.md +49 -0
- package/src/hidden-skills/groundwork-designer/sync-anchor.md +20 -0
- package/src/hidden-skills/groundwork-doc-sync/instructions.md +100 -0
- package/src/hidden-skills/groundwork-elicit/instructions.md +66 -0
- package/src/hidden-skills/groundwork-elicit/methods.md +65 -0
- package/src/hidden-skills/groundwork-infra-adopt/instructions.md +168 -0
- package/src/hidden-skills/groundwork-infra-adopt/templates/infra-adopt-cache.md +21 -0
- package/src/hidden-skills/groundwork-mvp/instructions.md +223 -0
- package/src/hidden-skills/groundwork-mvp/templates/mvp-cache.md +9 -0
- package/src/hidden-skills/groundwork-patch/instructions.md +40 -0
- package/src/hidden-skills/groundwork-persona/instructions.md +65 -0
- package/src/hidden-skills/groundwork-product/SKILL.md +102 -0
- package/src/hidden-skills/groundwork-product/references/ai-native-product.md +45 -0
- package/src/hidden-skills/groundwork-product/references/discovery-and-opportunity.md +38 -0
- package/src/hidden-skills/groundwork-product/references/product-risks.md +52 -0
- package/src/hidden-skills/groundwork-product/references/requirements-and-specs.md +39 -0
- package/src/hidden-skills/groundwork-product/references/scope-and-sequencing.md +35 -0
- package/src/hidden-skills/groundwork-product/references/shaping-and-appetite.md +48 -0
- package/src/hidden-skills/groundwork-product/references/success-metrics-and-signals.md +37 -0
- package/src/hidden-skills/groundwork-product/sync-anchor.md +19 -0
- package/src/hidden-skills/groundwork-product-brief/instructions.md +231 -0
- package/src/hidden-skills/groundwork-product-brief-extract/instructions.md +139 -0
- package/src/hidden-skills/groundwork-product-brief-extract/templates/product-brief-extract-cache.md +17 -0
- package/src/hidden-skills/groundwork-review/checklists/architecture.md +93 -0
- package/src/hidden-skills/groundwork-review/checklists/bet-pitch.md +94 -0
- package/src/hidden-skills/groundwork-review/checklists/decomposition.md +135 -0
- package/src/hidden-skills/groundwork-review/checklists/design-system.md +85 -0
- package/src/hidden-skills/groundwork-review/checklists/domain-entity.md +66 -0
- package/src/hidden-skills/groundwork-review/checklists/implementation-readiness.md +47 -0
- package/src/hidden-skills/groundwork-review/checklists/infrastructure.md +68 -0
- package/src/hidden-skills/groundwork-review/checklists/maturity.md +71 -0
- package/src/hidden-skills/groundwork-review/checklists/product-brief.md +69 -0
- package/src/hidden-skills/groundwork-review/checklists/technical-design.md +112 -0
- package/src/hidden-skills/groundwork-review/instructions.md +181 -0
- package/src/hidden-skills/groundwork-scaffold/instructions.md +254 -0
- package/src/hidden-skills/groundwork-scaffold/phases/01-ingestion-service-mapping.md +87 -0
- package/src/hidden-skills/groundwork-scaffold/phases/02-scaffolding-execution.md +15 -0
- package/src/hidden-skills/groundwork-scaffold/phases/03-service-documentation-api-stubs.md +100 -0
- package/src/hidden-skills/groundwork-scaffold/phases/04-infrastructure-verification.md +17 -0
- package/src/hidden-skills/groundwork-scaffold/phases/05-draft-review.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/phases/06-commit.md +19 -0
- package/src/hidden-skills/groundwork-scaffold/templates/scaffold-cache.md +23 -0
- package/src/hidden-skills/groundwork-scan/instructions.md +164 -0
- package/src/hidden-skills/groundwork-scan/references/digest-schema.md +66 -0
- package/src/hidden-skills/groundwork-scan/references/exclusions.md +44 -0
- package/src/hidden-skills/groundwork-scan/templates/architecture-findings.md +42 -0
- package/src/hidden-skills/groundwork-scan/templates/design-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/overview.md +26 -0
- package/src/hidden-skills/groundwork-scan/templates/product-findings.md +23 -0
- package/src/hidden-skills/groundwork-scan/templates/scan-state.json +19 -0
- package/src/hidden-skills/groundwork-stack-forge/instructions.md +150 -0
- package/src/hidden-skills/groundwork-stack-forge/references/authoring-engineer-skills.md +107 -0
- package/src/hidden-skills/groundwork-surface-activation/instructions.md +138 -0
- package/src/hidden-skills/groundwork-update/briefs/reconcile-worker.md +196 -0
- package/src/hidden-skills/groundwork-update/instructions.md +200 -0
- package/src/hidden-skills/groundwork-writer/SKILL.md +278 -0
- package/src/hidden-skills/maturity-model.md +125 -0
- package/src/hidden-skills/operating-contract.md +400 -0
- package/src/hidden-skills/repo-map-schema.md +90 -0
- package/src/hidden-skills/templates/adr.md +57 -0
- package/src/hidden-skills/templates/capability-ports.md +71 -0
- package/src/hidden-skills/templates/discovery-notes.md +33 -0
- package/src/hidden-skills/templates/domain-entity.md +80 -0
- package/src/hidden-skills/templates/gap-ledger.md +21 -0
- package/src/hidden-skills/templates/handoff.md +37 -0
- package/src/hidden-skills/templates/maturity.md +39 -0
- package/src/hidden-skills/templates/surfaces.md +207 -0
- package/src/skills/groundwork-check/SKILL.md +56 -0
- package/src/skills/groundwork-check/instructions.md +70 -0
- package/src/skills/groundwork-orchestrator/SKILL.md +176 -0
- package/src/skills/groundwork-orchestrator/workflow-index.md +50 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import json
|
|
2
|
+
<% if (messaging === 'redis') { %>
|
|
3
|
+
import redis.asyncio as redis
|
|
4
|
+
from <%= packageName %>.adapters.config import settings
|
|
5
|
+
|
|
6
|
+
class RedisMessageQueue:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.redis = redis.from_url(settings.redis_url)
|
|
9
|
+
|
|
10
|
+
async def publish(self, topic: str, payload: dict) -> None:
|
|
11
|
+
await self.redis.publish(topic, json.dumps(payload))
|
|
12
|
+
|
|
13
|
+
async def close(self):
|
|
14
|
+
await self.redis.close()
|
|
15
|
+
<% } %>
|
|
16
|
+
|
|
17
|
+
<% if (messaging === 'kafka') { %>
|
|
18
|
+
# Kafka implementation would go here using aiokafka
|
|
19
|
+
class KafkaMessageQueue:
|
|
20
|
+
async def publish(self, topic: str, payload: dict) -> None:
|
|
21
|
+
pass
|
|
22
|
+
<% } %>
|
|
23
|
+
|
|
24
|
+
<% if (messaging === 'gcp-pubsub') { %>
|
|
25
|
+
# GCP PubSub implementation would go here using google-cloud-pubsub
|
|
26
|
+
class GCPMessageQueue:
|
|
27
|
+
async def publish(self, topic: str, payload: dict) -> None:
|
|
28
|
+
pass
|
|
29
|
+
<% } %>
|
package/src/generators/python-microservice/files/src/__packageName__/adapters/repository.py.template
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import datetime
|
|
4
|
+
from typing import Optional, Tuple
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
+
from sqlalchemy import select, func
|
|
7
|
+
from sqlalchemy.dialects.postgresql import insert
|
|
8
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
9
|
+
from <%= packageName %>.core.domain.entities import ExampleEntity, PaginatedResult, IdempotencyResponse
|
|
10
|
+
from <%= packageName %>.adapters.database import Base
|
|
11
|
+
|
|
12
|
+
# SQLAlchemy Models
|
|
13
|
+
class ExampleModel(Base):
|
|
14
|
+
__tablename__ = "examples"
|
|
15
|
+
|
|
16
|
+
id: Mapped[str] = mapped_column(primary_key=True)
|
|
17
|
+
name: Mapped[str]
|
|
18
|
+
description: Mapped[Optional[str]]
|
|
19
|
+
|
|
20
|
+
def to_domain(self) -> ExampleEntity:
|
|
21
|
+
return ExampleEntity(id=self.id, name=self.name, description=self.description)
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_domain(cls, entity: ExampleEntity) -> "ExampleModel":
|
|
25
|
+
return cls(id=entity.id, name=entity.name, description=entity.description)
|
|
26
|
+
|
|
27
|
+
class IdempotencyKeyModel(Base):
|
|
28
|
+
__tablename__ = "idempotency_keys"
|
|
29
|
+
|
|
30
|
+
key: Mapped[str] = mapped_column(primary_key=True)
|
|
31
|
+
user_id: Mapped[str]
|
|
32
|
+
status: Mapped[str]
|
|
33
|
+
response_code: Mapped[Optional[int]]
|
|
34
|
+
response_body: Mapped[Optional[str]]
|
|
35
|
+
response_headers: Mapped[Optional[str]]
|
|
36
|
+
created_at: Mapped[datetime.datetime] = mapped_column(server_default=func.now())
|
|
37
|
+
updated_at: Mapped[datetime.datetime] = mapped_column(server_default=func.now(), onupdate=func.now())
|
|
38
|
+
|
|
39
|
+
# Repository Implementation
|
|
40
|
+
class PostgresExampleRepository:
|
|
41
|
+
def __init__(self, session: AsyncSession):
|
|
42
|
+
self.session = session
|
|
43
|
+
|
|
44
|
+
async def get_by_id(self, entity_id: str) -> Optional[ExampleEntity]:
|
|
45
|
+
result = await self.session.execute(
|
|
46
|
+
select(ExampleModel).where(ExampleModel.id == entity_id)
|
|
47
|
+
)
|
|
48
|
+
model = result.scalar_one_or_none()
|
|
49
|
+
return model.to_domain() if model else None
|
|
50
|
+
|
|
51
|
+
async def save(self, entity: ExampleEntity) -> None:
|
|
52
|
+
model = ExampleModel.from_domain(entity)
|
|
53
|
+
self.session.add(model)
|
|
54
|
+
await self.session.commit()
|
|
55
|
+
|
|
56
|
+
async def list_items(self, limit: int = 10, cursor: Optional[str] = None) -> PaginatedResult[ExampleEntity]:
|
|
57
|
+
query = select(ExampleModel).order_by(ExampleModel.id.asc()).limit(limit + 1)
|
|
58
|
+
|
|
59
|
+
if cursor:
|
|
60
|
+
try:
|
|
61
|
+
decoded_cursor = base64.b64decode(cursor).decode('utf-8')
|
|
62
|
+
query = query.where(ExampleModel.id > decoded_cursor)
|
|
63
|
+
except Exception:
|
|
64
|
+
pass # Invalid cursor handled gracefully
|
|
65
|
+
|
|
66
|
+
result = await self.session.execute(query)
|
|
67
|
+
models = result.scalars().all()
|
|
68
|
+
|
|
69
|
+
has_more = len(models) > limit
|
|
70
|
+
items = models[:limit]
|
|
71
|
+
|
|
72
|
+
next_cursor = None
|
|
73
|
+
if has_more and items:
|
|
74
|
+
last_id = items[-1].id
|
|
75
|
+
next_cursor = base64.b64encode(last_id.encode('utf-8')).decode('utf-8')
|
|
76
|
+
|
|
77
|
+
domain_items = [model.to_domain() for model in items]
|
|
78
|
+
return PaginatedResult(items=domain_items, next_cursor=next_cursor, has_more=has_more)
|
|
79
|
+
|
|
80
|
+
class PostgresIdempotencyRepository:
|
|
81
|
+
def __init__(self, session: AsyncSession):
|
|
82
|
+
self.session = session
|
|
83
|
+
|
|
84
|
+
async def get(self, key: str, user_id: str) -> Optional[Tuple[str, Optional[IdempotencyResponse]]]:
|
|
85
|
+
result = await self.session.execute(
|
|
86
|
+
select(IdempotencyKeyModel).where(
|
|
87
|
+
IdempotencyKeyModel.key == key,
|
|
88
|
+
IdempotencyKeyModel.user_id == user_id
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
model = result.scalar_one_or_none()
|
|
92
|
+
if not model:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
if model.status == "COMPLETED" and model.response_code is not None:
|
|
96
|
+
headers = json.loads(model.response_headers) if model.response_headers else {}
|
|
97
|
+
resp = IdempotencyResponse(
|
|
98
|
+
status_code=model.response_code,
|
|
99
|
+
headers=headers,
|
|
100
|
+
body=model.response_body or ""
|
|
101
|
+
)
|
|
102
|
+
return (model.status, resp)
|
|
103
|
+
|
|
104
|
+
return (model.status, None)
|
|
105
|
+
|
|
106
|
+
async def lock(self, key: str, user_id: str) -> bool:
|
|
107
|
+
# Atomic insert: if the key exists, do nothing (fails to acquire lock)
|
|
108
|
+
stmt = insert(IdempotencyKeyModel).values(
|
|
109
|
+
key=key,
|
|
110
|
+
user_id=user_id,
|
|
111
|
+
status="IN_PROGRESS"
|
|
112
|
+
).on_conflict_do_nothing()
|
|
113
|
+
|
|
114
|
+
result = await self.session.execute(stmt)
|
|
115
|
+
await self.session.commit()
|
|
116
|
+
return result.rowcount > 0
|
|
117
|
+
|
|
118
|
+
async def save(self, key: str, user_id: str, response: IdempotencyResponse) -> None:
|
|
119
|
+
result = await self.session.execute(
|
|
120
|
+
select(IdempotencyKeyModel).where(
|
|
121
|
+
IdempotencyKeyModel.key == key,
|
|
122
|
+
IdempotencyKeyModel.user_id == user_id
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
model = result.scalar_one()
|
|
126
|
+
model.status = "COMPLETED"
|
|
127
|
+
model.response_code = response.status_code
|
|
128
|
+
model.response_body = response.body
|
|
129
|
+
model.response_headers = json.dumps(response.headers)
|
|
130
|
+
await self.session.commit()
|
package/src/generators/python-microservice/files/src/__packageName__/adapters/telemetry.py.template
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""OpenTelemetry bootstrap for the FastAPI app.
|
|
2
|
+
|
|
3
|
+
Builds a TracerProvider with an OTLP gRPC exporter and instruments the FastAPI
|
|
4
|
+
app so inbound requests produce spans that are exported to the configured
|
|
5
|
+
collector (Jaeger in dev). Mirrors the Go scaffold: BatchSpanProcessor +
|
|
6
|
+
ParentBased(AlwaysSample) default sampler, service.name set on the resource.
|
|
7
|
+
|
|
8
|
+
Also configures structured JSON logging with trace_id/span_id correlation so
|
|
9
|
+
logs are joinable to traces.
|
|
10
|
+
"""
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from opentelemetry import trace
|
|
14
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
15
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
16
|
+
from opentelemetry.sdk.resources import Resource
|
|
17
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
18
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
19
|
+
from opentelemetry.semconv.resource import ResourceAttributes
|
|
20
|
+
from pythonjsonlogger import jsonlogger
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def setup_telemetry(app, endpoint: str) -> TracerProvider:
|
|
24
|
+
"""Initialize tracing and instrument the FastAPI app.
|
|
25
|
+
|
|
26
|
+
Returns the TracerProvider so the caller can shut it down (flush the
|
|
27
|
+
BatchSpanProcessor) on application stop.
|
|
28
|
+
"""
|
|
29
|
+
resource = Resource.create({ResourceAttributes.SERVICE_NAME: "<%= name %>"})
|
|
30
|
+
|
|
31
|
+
provider = TracerProvider(resource=resource)
|
|
32
|
+
# insecure=True: the dev endpoint is plain h2c (http://...:4317), no TLS.
|
|
33
|
+
exporter = OTLPSpanExporter(endpoint=endpoint, insecure=True)
|
|
34
|
+
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
35
|
+
|
|
36
|
+
trace.set_tracer_provider(provider)
|
|
37
|
+
FastAPIInstrumentor.instrument_app(app)
|
|
38
|
+
return provider
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _TraceContextFilter(logging.Filter):
|
|
42
|
+
"""Attaches the active span's trace_id/span_id to every log record."""
|
|
43
|
+
|
|
44
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
45
|
+
span = trace.get_current_span()
|
|
46
|
+
ctx = span.get_span_context()
|
|
47
|
+
if ctx.is_valid:
|
|
48
|
+
record.trace_id = format(ctx.trace_id, "032x")
|
|
49
|
+
record.span_id = format(ctx.span_id, "016x")
|
|
50
|
+
else:
|
|
51
|
+
record.trace_id = ""
|
|
52
|
+
record.span_id = ""
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def setup_logging() -> None:
|
|
57
|
+
"""Configure root logging as JSON with trace correlation fields."""
|
|
58
|
+
handler = logging.StreamHandler()
|
|
59
|
+
handler.setFormatter(
|
|
60
|
+
jsonlogger.JsonFormatter(
|
|
61
|
+
"%(asctime)s %(levelname)s %(name)s %(message)s %(trace_id)s %(span_id)s"
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
handler.addFilter(_TraceContextFilter())
|
|
65
|
+
root = logging.getLogger()
|
|
66
|
+
root.handlers.clear()
|
|
67
|
+
root.addHandler(handler)
|
|
68
|
+
root.setLevel(logging.INFO)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import redis.asyncio as redis
|
|
3
|
+
from <%= packageName %>.adapters.config import settings
|
|
4
|
+
import asyncio
|
|
5
|
+
from typing import Set
|
|
6
|
+
|
|
7
|
+
class WebSocketHub:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.redis = redis.from_url(settings.redis_url)
|
|
10
|
+
self.pubsub = self.redis.pubsub()
|
|
11
|
+
self.active_connections: Set = set()
|
|
12
|
+
self.background_tasks: Set[asyncio.Task] = set()
|
|
13
|
+
|
|
14
|
+
async def connect(self, websocket):
|
|
15
|
+
await websocket.accept()
|
|
16
|
+
self.active_connections.add(websocket)
|
|
17
|
+
|
|
18
|
+
def disconnect(self, websocket):
|
|
19
|
+
self.active_connections.discard(websocket)
|
|
20
|
+
|
|
21
|
+
async def subscribe(self, channel: str):
|
|
22
|
+
await self.pubsub.subscribe(channel)
|
|
23
|
+
|
|
24
|
+
task = asyncio.create_task(self._listen())
|
|
25
|
+
self.background_tasks.add(task)
|
|
26
|
+
task.add_done_callback(self.background_tasks.discard)
|
|
27
|
+
|
|
28
|
+
async def _listen(self):
|
|
29
|
+
async for message in self.pubsub.listen():
|
|
30
|
+
if message['type'] == 'message':
|
|
31
|
+
data = message['data'].decode('utf-8')
|
|
32
|
+
for connection in self.active_connections:
|
|
33
|
+
await connection.send_text(data)
|
|
34
|
+
|
|
35
|
+
async def broadcast(self, channel: str, message: dict):
|
|
36
|
+
await self.redis.publish(channel, json.dumps(message))
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Generic, TypeVar, List, Optional, Dict, Any
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
T = TypeVar("T")
|
|
5
|
+
|
|
6
|
+
class PaginatedResult(BaseModel, Generic[T]):
|
|
7
|
+
"""Generic cursor-based pagination result."""
|
|
8
|
+
items: List[T]
|
|
9
|
+
next_cursor: Optional[str] = None
|
|
10
|
+
has_more: bool = False
|
|
11
|
+
|
|
12
|
+
class IdempotencyResponse(BaseModel):
|
|
13
|
+
"""Domain representation of a cached HTTP response."""
|
|
14
|
+
status_code: int
|
|
15
|
+
headers: Dict[str, str]
|
|
16
|
+
body: str
|
|
17
|
+
|
|
18
|
+
class ExampleEntity(BaseModel):
|
|
19
|
+
"""An example entity for the service."""
|
|
20
|
+
id: str
|
|
21
|
+
name: str
|
|
22
|
+
description: Optional[str] = None
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class DomainError(Exception):
|
|
2
|
+
"""Base class for domain exceptions."""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class NotFoundError(DomainError):
|
|
6
|
+
"""Raised when a requested resource is not found."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class UnauthorizedError(DomainError):
|
|
10
|
+
"""Raised when authentication is missing or invalid."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class ForbiddenError(DomainError):
|
|
14
|
+
"""Raised when the user does not have permission."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class ConflictError(DomainError):
|
|
18
|
+
"""Raised when there is a conflict, such as a duplicate resource."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
class ValidationError(DomainError):
|
|
22
|
+
"""Raised for field-level validation errors."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class IdempotencyError(DomainError):
|
|
26
|
+
"""Raised when an idempotency key collision occurs."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
class RateLimitExceededError(DomainError):
|
|
30
|
+
"""Raised when concurrency limits or rate limits are exceeded."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
class CircuitBreakerOpenError(DomainError):
|
|
34
|
+
"""Raised when an external service circuit breaker is open."""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
class TransientInferenceError(DomainError):
|
|
38
|
+
"""Raised for retriable provider/ML failures."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
class PermanentInferenceError(DomainError):
|
|
42
|
+
"""Raised for non-retriable provider/ML failures."""
|
|
43
|
+
pass
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Protocol, Optional, Tuple
|
|
2
|
+
from <%= packageName %>.core.domain.entities import ExampleEntity, PaginatedResult, IdempotencyResponse
|
|
3
|
+
|
|
4
|
+
class IdempotencyRepository(Protocol):
|
|
5
|
+
"""Port for storing idempotency state."""
|
|
6
|
+
|
|
7
|
+
async def get(self, key: str, user_id: str) -> Optional[Tuple[str, Optional[IdempotencyResponse]]]:
|
|
8
|
+
"""Returns (status, cached_response) if key exists, else None."""
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
async def lock(self, key: str, user_id: str) -> bool:
|
|
12
|
+
"""Attempts to acquire lock (IN_PROGRESS). Returns True if successful."""
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
async def save(self, key: str, user_id: str, response: IdempotencyResponse) -> None:
|
|
16
|
+
"""Saves completed response and marks as COMPLETED."""
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
<% if (postgres) { %>
|
|
20
|
+
class ExampleRepository(Protocol):
|
|
21
|
+
"""Port for ExampleEntity storage."""
|
|
22
|
+
|
|
23
|
+
async def get_by_id(self, entity_id: str) -> Optional[ExampleEntity]:
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
async def save(self, entity: ExampleEntity) -> None:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
async def list_items(self, limit: int = 10, cursor: Optional[str] = None) -> PaginatedResult[ExampleEntity]:
|
|
30
|
+
...
|
|
31
|
+
<% } %>
|
|
32
|
+
|
|
33
|
+
<% if (messaging !== 'none') { %>
|
|
34
|
+
class MessageQueue(Protocol):
|
|
35
|
+
"""Port for publishing events."""
|
|
36
|
+
|
|
37
|
+
async def publish(self, topic: str, payload: dict) -> None:
|
|
38
|
+
...
|
|
39
|
+
<% } %>
|
|
40
|
+
<%# The TextGenerator port lives in the package's core/llm.py, generated by the
|
|
41
|
+
llm capability (plan WS-F), so the same port is reusable by the standalone
|
|
42
|
+
add-capability generator on existing services. %>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from <%= packageName %>.core.domain.entities import ExampleEntity, PaginatedResult
|
|
2
|
+
from <%= packageName %>.core.domain.exceptions import NotFoundError
|
|
3
|
+
<% if (postgres) { %>
|
|
4
|
+
from <%= packageName %>.core.ports import ExampleRepository
|
|
5
|
+
<% } %>
|
|
6
|
+
<% if (messaging !== 'none') { %>
|
|
7
|
+
from <%= packageName %>.core.ports import MessageQueue
|
|
8
|
+
<% } %>
|
|
9
|
+
<% if (llm) { %>
|
|
10
|
+
from <%= packageName %>.core.llm import TextGenerator
|
|
11
|
+
<% } %>
|
|
12
|
+
import uuid
|
|
13
|
+
|
|
14
|
+
class ExampleService:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
<% if (postgres) { %>
|
|
18
|
+
repository: ExampleRepository,
|
|
19
|
+
<% } %>
|
|
20
|
+
<% if (messaging !== 'none') { %>
|
|
21
|
+
message_queue: MessageQueue,
|
|
22
|
+
<% } %>
|
|
23
|
+
<% if (llm) { %>
|
|
24
|
+
text_generator: TextGenerator,
|
|
25
|
+
<% } %>
|
|
26
|
+
):
|
|
27
|
+
<% if (postgres) { %>
|
|
28
|
+
self.repository = repository
|
|
29
|
+
<% } %>
|
|
30
|
+
<% if (messaging !== 'none') { %>
|
|
31
|
+
self.message_queue = message_queue
|
|
32
|
+
<% } %>
|
|
33
|
+
<% if (llm) { %>
|
|
34
|
+
self.text_generator = text_generator
|
|
35
|
+
<% } %>
|
|
36
|
+
<% if (!postgres && messaging === 'none' && !llm) { %>
|
|
37
|
+
pass
|
|
38
|
+
<% } %>
|
|
39
|
+
|
|
40
|
+
async def create_example(self, name: str) -> ExampleEntity:
|
|
41
|
+
entity = ExampleEntity(id=str(uuid.uuid4()), name=name)
|
|
42
|
+
|
|
43
|
+
<% if (llm) { %>
|
|
44
|
+
# Generate description using LLM
|
|
45
|
+
description = await self.text_generator.generate_text(f"Describe a thing named {name}")
|
|
46
|
+
entity.description = description
|
|
47
|
+
<% } %>
|
|
48
|
+
|
|
49
|
+
<% if (postgres) { %>
|
|
50
|
+
await self.repository.save(entity)
|
|
51
|
+
<% } %>
|
|
52
|
+
|
|
53
|
+
<% if (messaging !== 'none') { %>
|
|
54
|
+
await self.message_queue.publish("example.created", entity.model_dump())
|
|
55
|
+
<% } %>
|
|
56
|
+
|
|
57
|
+
return entity
|
|
58
|
+
|
|
59
|
+
<% if (postgres) { %>
|
|
60
|
+
async def get_example(self, entity_id: str) -> ExampleEntity:
|
|
61
|
+
entity = await self.repository.get_by_id(entity_id)
|
|
62
|
+
if not entity:
|
|
63
|
+
raise NotFoundError(f"Example {entity_id} not found")
|
|
64
|
+
return entity
|
|
65
|
+
|
|
66
|
+
async def list_examples(self, limit: int = 10, cursor: str | None = None) -> PaginatedResult[ExampleEntity]:
|
|
67
|
+
return await self.repository.list_items(limit, cursor)
|
|
68
|
+
<% } %>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from fastapi import Depends
|
|
2
|
+
from <%= packageName %>.core.service.example_service import ExampleService
|
|
3
|
+
|
|
4
|
+
<% if (postgres) { %>
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
|
+
from <%= packageName %>.core.ports import ExampleRepository
|
|
7
|
+
from <%= packageName %>.adapters.database import get_db_session
|
|
8
|
+
from <%= packageName %>.adapters.repository import PostgresExampleRepository
|
|
9
|
+
|
|
10
|
+
def get_example_repository(session: AsyncSession = Depends(get_db_session)) -> ExampleRepository:
|
|
11
|
+
return PostgresExampleRepository(session)
|
|
12
|
+
<% } %>
|
|
13
|
+
|
|
14
|
+
<% if (messaging !== 'none') { %>
|
|
15
|
+
from <%= packageName %>.core.ports import MessageQueue
|
|
16
|
+
from <%= packageName %>.adapters.message_queue import <% if (messaging === 'redis') { %>RedisMessageQueue<% } else if (messaging === 'kafka') { %>KafkaMessageQueue<% } else { %>GCPMessageQueue<% } %>
|
|
17
|
+
def get_message_queue() -> MessageQueue:
|
|
18
|
+
# In a real app this might be a singleton in app state, but for scaffold we instantiate
|
|
19
|
+
return <% if (messaging === 'redis') { %>RedisMessageQueue()<% } else if (messaging === 'kafka') { %>KafkaMessageQueue()<% } else { %>GCPMessageQueue()<% } %>
|
|
20
|
+
<% } %>
|
|
21
|
+
|
|
22
|
+
<% if (llm) { %>
|
|
23
|
+
from <%= packageName %>.core.llm import TextGenerator
|
|
24
|
+
from <%= packageName %>.adapters.llm import LLMClient
|
|
25
|
+
def get_text_generator() -> TextGenerator:
|
|
26
|
+
return LLMClient()
|
|
27
|
+
<% } %>
|
|
28
|
+
|
|
29
|
+
def get_example_service(
|
|
30
|
+
<% if (postgres) { %>
|
|
31
|
+
repository: ExampleRepository = Depends(get_example_repository),
|
|
32
|
+
<% } %>
|
|
33
|
+
<% if (messaging !== 'none') { %>
|
|
34
|
+
message_queue: MessageQueue = Depends(get_message_queue),
|
|
35
|
+
<% } %>
|
|
36
|
+
<% if (llm) { %>
|
|
37
|
+
text_generator: TextGenerator = Depends(get_text_generator),
|
|
38
|
+
<% } %>
|
|
39
|
+
) -> ExampleService:
|
|
40
|
+
return ExampleService(
|
|
41
|
+
<% if (postgres) { %>
|
|
42
|
+
repository=repository,
|
|
43
|
+
<% } %>
|
|
44
|
+
<% if (messaging !== 'none') { %>
|
|
45
|
+
message_queue=message_queue,
|
|
46
|
+
<% } %>
|
|
47
|
+
<% if (llm) { %>
|
|
48
|
+
text_generator=text_generator,
|
|
49
|
+
<% } %>
|
|
50
|
+
)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from fastapi import Request, status
|
|
5
|
+
from fastapi.responses import JSONResponse
|
|
6
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
7
|
+
from <%= packageName %>.core.domain.exceptions import (
|
|
8
|
+
NotFoundError,
|
|
9
|
+
ConflictError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
IdempotencyError,
|
|
12
|
+
RateLimitExceededError,
|
|
13
|
+
CircuitBreakerOpenError,
|
|
14
|
+
TransientInferenceError
|
|
15
|
+
)
|
|
16
|
+
from <%= packageName %>.core.ports import IdempotencyRepository
|
|
17
|
+
from <%= packageName %>.core.domain.entities import IdempotencyResponse
|
|
18
|
+
from <%= packageName %>.adapters.config import settings
|
|
19
|
+
|
|
20
|
+
class DomainExceptionHandlerMiddleware(BaseHTTPMiddleware):
|
|
21
|
+
"""Maps domain exceptions to appropriate HTTP responses."""
|
|
22
|
+
async def dispatch(self, request: Request, call_next):
|
|
23
|
+
try:
|
|
24
|
+
return await call_next(request)
|
|
25
|
+
except NotFoundError as e:
|
|
26
|
+
return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"detail": str(e)})
|
|
27
|
+
except (ConflictError, IdempotencyError) as e:
|
|
28
|
+
return JSONResponse(status_code=status.HTTP_409_CONFLICT, content={"detail": str(e)})
|
|
29
|
+
except ValidationError as e:
|
|
30
|
+
return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={"detail": str(e)})
|
|
31
|
+
except RateLimitExceededError as e:
|
|
32
|
+
return JSONResponse(status_code=status.HTTP_429_TOO_MANY_REQUESTS, content={"detail": str(e)})
|
|
33
|
+
except (TransientInferenceError, CircuitBreakerOpenError) as e:
|
|
34
|
+
return JSONResponse(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, content={"detail": str(e)})
|
|
35
|
+
except Exception as e:
|
|
36
|
+
# Let unhandled exceptions propagate or log them here
|
|
37
|
+
raise e
|
|
38
|
+
|
|
39
|
+
class GlobalTimeoutMiddleware(BaseHTTPMiddleware):
|
|
40
|
+
"""Ensures requests do not hang indefinitely."""
|
|
41
|
+
def __init__(self, app, timeout: float = 30.0):
|
|
42
|
+
super().__init__(app)
|
|
43
|
+
self.timeout = timeout
|
|
44
|
+
|
|
45
|
+
async def dispatch(self, request: Request, call_next):
|
|
46
|
+
try:
|
|
47
|
+
return await asyncio.wait_for(call_next(request), timeout=self.timeout)
|
|
48
|
+
except asyncio.TimeoutError:
|
|
49
|
+
return JSONResponse(
|
|
50
|
+
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
|
|
51
|
+
content={"detail": "Request execution timed out."}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
class ConcurrencyLimitingMiddleware(BaseHTTPMiddleware):
|
|
55
|
+
"""Load shedder utilizing a semaphore to limit concurrent requests."""
|
|
56
|
+
def __init__(self, app, max_concurrent_requests: int):
|
|
57
|
+
super().__init__(app)
|
|
58
|
+
self.semaphore = asyncio.Semaphore(max_concurrent_requests)
|
|
59
|
+
|
|
60
|
+
async def dispatch(self, request: Request, call_next):
|
|
61
|
+
if self.semaphore.locked():
|
|
62
|
+
return JSONResponse(
|
|
63
|
+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
64
|
+
content={"detail": "Service overloaded. Please try again later."}
|
|
65
|
+
)
|
|
66
|
+
async with self.semaphore:
|
|
67
|
+
return await call_next(request)
|
|
68
|
+
|
|
69
|
+
class IdempotencyMiddleware(BaseHTTPMiddleware):
|
|
70
|
+
"""
|
|
71
|
+
Ensures mutating requests (POST/PUT/PATCH/DELETE) with an Idempotency-Key
|
|
72
|
+
are only executed once. Uses injected IdempotencyRepository from app state.
|
|
73
|
+
"""
|
|
74
|
+
async def dispatch(self, request: Request, call_next):
|
|
75
|
+
if request.method in ["GET", "OPTIONS", "HEAD"]:
|
|
76
|
+
return await call_next(request)
|
|
77
|
+
|
|
78
|
+
idem_key = request.headers.get("Idempotency-Key")
|
|
79
|
+
if not idem_key:
|
|
80
|
+
return await call_next(request)
|
|
81
|
+
|
|
82
|
+
# In real life, extract user_id from auth token
|
|
83
|
+
user_id = "anonymous"
|
|
84
|
+
|
|
85
|
+
# We need the repository. Ideally injected into app state during lifespan.
|
|
86
|
+
repo: Optional[IdempotencyRepository] = getattr(request.app.state, "idempotency_repo", None)
|
|
87
|
+
if not repo:
|
|
88
|
+
# If repo isn't configured, degrade gracefully or fail.
|
|
89
|
+
return await call_next(request)
|
|
90
|
+
|
|
91
|
+
# 1. Check if already executed
|
|
92
|
+
result = await repo.get(idem_key, user_id)
|
|
93
|
+
if result:
|
|
94
|
+
state, cached_response = result
|
|
95
|
+
if state == "IN_PROGRESS":
|
|
96
|
+
return JSONResponse(
|
|
97
|
+
status_code=status.HTTP_409_CONFLICT,
|
|
98
|
+
content={"detail": "Request with this Idempotency-Key is already in progress."}
|
|
99
|
+
)
|
|
100
|
+
if state == "COMPLETED" and cached_response:
|
|
101
|
+
return JSONResponse(
|
|
102
|
+
status_code=cached_response.status_code,
|
|
103
|
+
content=json.loads(cached_response.body) if cached_response.body else None,
|
|
104
|
+
headers=cached_response.headers
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# 2. Acquire lock
|
|
108
|
+
acquired = await repo.lock(idem_key, user_id)
|
|
109
|
+
if not acquired:
|
|
110
|
+
return JSONResponse(
|
|
111
|
+
status_code=status.HTTP_409_CONFLICT,
|
|
112
|
+
content={"detail": "Concurrent request with this Idempotency-Key detected."}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# 3. Execute
|
|
116
|
+
response = await call_next(request)
|
|
117
|
+
|
|
118
|
+
# 4. Cache successful responses (2xx)
|
|
119
|
+
if 200 <= response.status_code < 300:
|
|
120
|
+
# To cache the body, we have to extract it, which is complex in ASGI.
|
|
121
|
+
# For simplicity in this scaffold, we store a basic body or use custom routers.
|
|
122
|
+
# Real robust implementation should buffer the ASGI chunks.
|
|
123
|
+
# We'll just cache the status here for demonstration.
|
|
124
|
+
resp_to_cache = IdempotencyResponse(
|
|
125
|
+
status_code=response.status_code,
|
|
126
|
+
headers=dict(response.headers),
|
|
127
|
+
body='{"status": "completed"}' # Mocked for middleware.
|
|
128
|
+
)
|
|
129
|
+
await repo.save(idem_key, user_id, resp_to_cache)
|
|
130
|
+
|
|
131
|
+
return response
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from fastapi import APIRouter, Depends, Query
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from <%= packageName %>.core.service.example_service import ExampleService
|
|
5
|
+
from <%= packageName %>.entrypoints.api.dependencies import get_example_service
|
|
6
|
+
|
|
7
|
+
router = APIRouter(prefix="/examples", tags=["examples"])
|
|
8
|
+
|
|
9
|
+
class CreateExampleRequest(BaseModel):
|
|
10
|
+
name: str
|
|
11
|
+
|
|
12
|
+
@router.post("")
|
|
13
|
+
async def create_example(
|
|
14
|
+
request: CreateExampleRequest,
|
|
15
|
+
service: ExampleService = Depends(get_example_service)
|
|
16
|
+
):
|
|
17
|
+
entity = await service.create_example(name=request.name)
|
|
18
|
+
return entity
|
|
19
|
+
|
|
20
|
+
<% if (postgres) { %>
|
|
21
|
+
@router.get("/{entity_id}")
|
|
22
|
+
async def get_example(
|
|
23
|
+
entity_id: str,
|
|
24
|
+
service: ExampleService = Depends(get_example_service)
|
|
25
|
+
):
|
|
26
|
+
entity = await service.get_example(entity_id)
|
|
27
|
+
return entity
|
|
28
|
+
|
|
29
|
+
@router.get("")
|
|
30
|
+
async def list_examples(
|
|
31
|
+
limit: int = Query(10, ge=1, le=100),
|
|
32
|
+
cursor: Optional[str] = None,
|
|
33
|
+
service: ExampleService = Depends(get_example_service)
|
|
34
|
+
):
|
|
35
|
+
result = await service.list_examples(limit=limit, cursor=cursor)
|
|
36
|
+
return result
|
|
37
|
+
<% } %>
|