groundwork-method 0.0.1 → 0.10.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 +781 -0
- package/LICENSE +21 -0
- package/README.md +44 -29
- package/bin/groundwork.js +1654 -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 +125 -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 +68 -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 +82 -0
- package/src/docs/principles/index.md +23 -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 +118 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/ipc-contracts.md +138 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/packaging-and-updates.md +82 -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 +107 -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 +14 -0
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +108 -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/navigation.md +122 -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/state-management.md +166 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +135 -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 +15 -0
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +171 -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/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 +139 -0
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +11 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +107 -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/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/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 +433 -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 +278 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/visual-language.md +69 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +9 -0
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +196 -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/testing.md +177 -0
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +13 -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 +47 -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 +68 -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 +74 -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 +30 -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 +152 -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 +281 -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/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 +227 -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 +142 -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 +129 -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/slice-worker.md +191 -0
- package/src/hidden-skills/groundwork-bet/instructions.md +88 -0
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +126 -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 +35 -0
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +35 -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 +198 -0
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +168 -0
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +246 -0
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +193 -0
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +199 -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 +54 -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 +46 -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
package/src/generators/capabilities/llm/providers/openai/stacks/go/internal/llm/llm.go.template
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"net/http"
|
|
10
|
+
"os"
|
|
11
|
+
"time"
|
|
12
|
+
|
|
13
|
+
"<%= moduleName %>/internal/core/service"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// Ensure Client implements the service.TextGenerator interface.
|
|
17
|
+
var _ service.TextGenerator = (*Client)(nil)
|
|
18
|
+
|
|
19
|
+
// Client calls the OpenAI Chat Completions API (or any
|
|
20
|
+
// OpenAI-compatible endpoint via LLM_BASE_URL) over HTTPS. Minimal net/http
|
|
21
|
+
// adapter (no SDK dependency) — extend or swap for the official SDK behind this
|
|
22
|
+
// same port. Reads LLM_API_KEY / LLM_BASE_URL / LLM_MODEL from env.
|
|
23
|
+
type Client struct {
|
|
24
|
+
apiKey string
|
|
25
|
+
baseURL string
|
|
26
|
+
model string
|
|
27
|
+
http *http.Client
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func NewClient() *Client {
|
|
31
|
+
base := os.Getenv("LLM_BASE_URL")
|
|
32
|
+
if base == "" {
|
|
33
|
+
base = "https://api.openai.com/v1"
|
|
34
|
+
}
|
|
35
|
+
model := os.Getenv("LLM_MODEL")
|
|
36
|
+
if model == "" {
|
|
37
|
+
model = "gpt-4o"
|
|
38
|
+
}
|
|
39
|
+
return &Client{
|
|
40
|
+
apiKey: os.Getenv("LLM_API_KEY"),
|
|
41
|
+
baseURL: base,
|
|
42
|
+
model: model,
|
|
43
|
+
http: &http.Client{Timeout: 60 * time.Second},
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (a *Client) GenerateText(ctx context.Context, prompt string, maxTokens int) (string, error) {
|
|
48
|
+
reqBody, err := json.Marshal(map[string]any{
|
|
49
|
+
"model": a.model,
|
|
50
|
+
"max_tokens": maxTokens,
|
|
51
|
+
"messages": []map[string]string{{"role": "user", "content": prompt}},
|
|
52
|
+
})
|
|
53
|
+
if err != nil {
|
|
54
|
+
return "", fmt.Errorf("llm: marshal request: %w", err)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var lastErr error
|
|
58
|
+
for attempt := 0; attempt < 3; attempt++ {
|
|
59
|
+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.baseURL+"/chat/completions", bytes.NewReader(reqBody))
|
|
60
|
+
if err != nil {
|
|
61
|
+
return "", fmt.Errorf("llm: build request: %w", err)
|
|
62
|
+
}
|
|
63
|
+
req.Header.Set("content-type", "application/json")
|
|
64
|
+
req.Header.Set("authorization", "Bearer "+a.apiKey)
|
|
65
|
+
|
|
66
|
+
resp, err := a.http.Do(req)
|
|
67
|
+
if err != nil {
|
|
68
|
+
lastErr = fmt.Errorf("llm: connection error: %w", err)
|
|
69
|
+
continue // transient — retry
|
|
70
|
+
}
|
|
71
|
+
body, _ := io.ReadAll(resp.Body)
|
|
72
|
+
resp.Body.Close()
|
|
73
|
+
|
|
74
|
+
if resp.StatusCode >= 500 || resp.StatusCode == http.StatusTooManyRequests {
|
|
75
|
+
lastErr = fmt.Errorf("llm: transient provider error (status %d): %s", resp.StatusCode, body)
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
if resp.StatusCode >= 400 {
|
|
79
|
+
return "", fmt.Errorf("llm: provider request error (status %d): %s", resp.StatusCode, body)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var parsed struct {
|
|
83
|
+
Choices []struct {
|
|
84
|
+
Message struct {
|
|
85
|
+
Content string `json:"content"`
|
|
86
|
+
} `json:"message"`
|
|
87
|
+
} `json:"choices"`
|
|
88
|
+
}
|
|
89
|
+
if err := json.Unmarshal(body, &parsed); err != nil {
|
|
90
|
+
return "", fmt.Errorf("llm: decode response: %w", err)
|
|
91
|
+
}
|
|
92
|
+
if len(parsed.Choices) == 0 || parsed.Choices[0].Message.Content == "" {
|
|
93
|
+
return "", fmt.Errorf("llm: empty response from provider")
|
|
94
|
+
}
|
|
95
|
+
return parsed.Choices[0].Message.Content, nil
|
|
96
|
+
}
|
|
97
|
+
return "", fmt.Errorf("llm: exhausted retries: %w", lastErr)
|
|
98
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from <%= packageName %>.core.domain.exceptions import TransientInferenceError, PermanentInferenceError, CircuitBreakerOpenError
|
|
2
|
+
from tenacity import retry, wait_exponential_jitter, stop_after_attempt, retry_if_exception_type
|
|
3
|
+
from circuitbreaker import circuit
|
|
4
|
+
from <%= packageName %>.adapters.config import settings
|
|
5
|
+
import openai
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
def fallback_circuit_breaker(e):
|
|
11
|
+
logger.error(f"Circuit breaker tripped or open! {e}")
|
|
12
|
+
raise CircuitBreakerOpenError(f"External LLM API is unavailable. Please try again later.")
|
|
13
|
+
|
|
14
|
+
class LLMClient:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
# Defer building the provider client until the first generate_text call so
|
|
17
|
+
# the service boots and serves its non-LLM routes even when no API key is
|
|
18
|
+
# configured — the OpenAI client raises at construction on a missing key,
|
|
19
|
+
# which would otherwise 500 every route that depends on this service.
|
|
20
|
+
self._client: openai.AsyncOpenAI | None = None
|
|
21
|
+
self.model = settings.llm_model
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def client(self) -> openai.AsyncOpenAI:
|
|
25
|
+
if self._client is None:
|
|
26
|
+
self._client = openai.AsyncOpenAI(
|
|
27
|
+
api_key=settings.llm_api_key,
|
|
28
|
+
base_url=settings.llm_base_url,
|
|
29
|
+
)
|
|
30
|
+
return self._client
|
|
31
|
+
|
|
32
|
+
@circuit(failure_threshold=5, recovery_timeout=30, fallback_function=fallback_circuit_breaker, expected_exception=TransientInferenceError)
|
|
33
|
+
@retry(
|
|
34
|
+
wait=wait_exponential_jitter(initial=1, max=30),
|
|
35
|
+
stop=stop_after_attempt(5),
|
|
36
|
+
retry=retry_if_exception_type(TransientInferenceError)
|
|
37
|
+
)
|
|
38
|
+
async def generate_text(self, prompt: str, max_tokens: int = 100) -> str:
|
|
39
|
+
try:
|
|
40
|
+
response = await self.client.chat.completions.create(
|
|
41
|
+
model=self.model,
|
|
42
|
+
messages=[{"role": "user", "content": prompt}],
|
|
43
|
+
max_tokens=max_tokens
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Boundary validation: ensure we got a valid response
|
|
47
|
+
content = response.choices[0].message.content
|
|
48
|
+
if not content:
|
|
49
|
+
raise PermanentInferenceError("Received empty response from LLM provider")
|
|
50
|
+
|
|
51
|
+
return content
|
|
52
|
+
|
|
53
|
+
except openai.APIConnectionError as e:
|
|
54
|
+
raise TransientInferenceError(f"Connection error: {e}")
|
|
55
|
+
except openai.RateLimitError as e:
|
|
56
|
+
raise TransientInferenceError(f"Rate limited: {e}")
|
|
57
|
+
except openai.APIStatusError as e:
|
|
58
|
+
if e.status_code >= 500:
|
|
59
|
+
raise TransientInferenceError(f"Provider server error: {e}")
|
|
60
|
+
raise PermanentInferenceError(f"Provider request error: {e}")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package service
|
|
2
|
+
|
|
3
|
+
import "context"
|
|
4
|
+
|
|
5
|
+
// TextGenerator is the interface the core depends on for large-language-model
|
|
6
|
+
// text generation. It is declared here, in the consuming service package, so
|
|
7
|
+
// the dependency arrow points inward: concrete adapters live in internal/llm
|
|
8
|
+
// and are swappable at scaffold time (anthropic | openai | local | none)
|
|
9
|
+
// without touching this interface or its callers.
|
|
10
|
+
type TextGenerator interface {
|
|
11
|
+
GenerateText(ctx context.Context, prompt string, maxTokens int) (string, error)
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package llm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"<%= moduleName %>/internal/core/service"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// Compile-time conformance: the adapter satisfies the service.TextGenerator
|
|
11
|
+
// interface. If the interface and adapter drift, this test file fails to build —
|
|
12
|
+
// the contract is enforced at compile time, the strongest place to put it.
|
|
13
|
+
var _ service.TextGenerator = (*Client)(nil)
|
|
14
|
+
|
|
15
|
+
func TestClientConformsToInterface(t *testing.T) {
|
|
16
|
+
var _ service.TextGenerator = (*Client)(nil)
|
|
17
|
+
}
|
|
18
|
+
<% if (provider === 'none') { %>
|
|
19
|
+
// Bet: the bare adapter has no implementation yet. While the stub returns an
|
|
20
|
+
// error this test Skips, so a freshly scaffolded service stays green. The moment
|
|
21
|
+
// you implement GenerateText it runs for real and asserts the contract — your
|
|
22
|
+
// cue that the bet is delivered and this Skip can come out. The interface is the
|
|
23
|
+
// spec, the adapter is the bet.
|
|
24
|
+
func TestClientGeneratesText(t *testing.T) {
|
|
25
|
+
out, err := NewClient().GenerateText(context.Background(), "ping", 8)
|
|
26
|
+
if err != nil {
|
|
27
|
+
t.Skipf("bet: LLM adapter not implemented yet: %v", err)
|
|
28
|
+
}
|
|
29
|
+
if out == "" {
|
|
30
|
+
t.Fatal("expected non-empty text from the LLM client")
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
<% } %>
|
package/src/generators/capabilities/llm/stacks/python/src/__packageName__/core/llm.py.template
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@runtime_checkable
|
|
5
|
+
class TextGenerator(Protocol):
|
|
6
|
+
"""Port: large-language-model text generation.
|
|
7
|
+
|
|
8
|
+
This interface is the stable contract the domain depends on. Concrete
|
|
9
|
+
adapters live in src/<%= packageName %>/adapters/llm.py and are swappable at scaffold time
|
|
10
|
+
(anthropic | openai | local | none) without touching this port or its
|
|
11
|
+
callers — that is the whole point of the ports/adapters boundary.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
async def generate_text(self, prompt: str, max_tokens: int = 100) -> str:
|
|
15
|
+
...
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Contract test for the LLM capability port.
|
|
2
|
+
|
|
3
|
+
Generated by the `llm` capability. Asserts the chosen adapter satisfies the
|
|
4
|
+
TextGenerator port so a provider swap can never silently break the boundary.
|
|
5
|
+
"""
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from <%= packageName %>.core.llm import TextGenerator
|
|
9
|
+
from <%= packageName %>.adapters.llm import LLMClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_adapter_conforms_to_text_generator_port():
|
|
13
|
+
"""Structural conformance: the adapter implements the port's method surface.
|
|
14
|
+
|
|
15
|
+
Uses issubclass against the runtime_checkable Protocol so it never has to
|
|
16
|
+
construct the adapter (no network, no API key, no provider SDK call).
|
|
17
|
+
"""
|
|
18
|
+
assert issubclass(LLMClient, TextGenerator)
|
|
19
|
+
<% if (provider === 'none') { %>
|
|
20
|
+
|
|
21
|
+
@pytest.mark.asyncio
|
|
22
|
+
@pytest.mark.xfail(
|
|
23
|
+
strict=True,
|
|
24
|
+
reason="bet: implement the LLM adapter, then remove this marker",
|
|
25
|
+
)
|
|
26
|
+
async def test_unimplemented_adapter_generates_text():
|
|
27
|
+
"""RED bet — the bare adapter has no implementation yet.
|
|
28
|
+
|
|
29
|
+
Marked xfail(strict) so a freshly scaffolded service stays green while the
|
|
30
|
+
bet is open. The moment you implement LLMClient.generate_text this test
|
|
31
|
+
XPASSes and the suite goes red: your cue to remove this marker and own the
|
|
32
|
+
behavioural contract. This is GroundWork's design-locked-in-tests loop —
|
|
33
|
+
the port is the spec, the adapter is the bet.
|
|
34
|
+
"""
|
|
35
|
+
result = await LLMClient().generate_text("ping", max_tokens=8)
|
|
36
|
+
assert isinstance(result, str) and result
|
|
37
|
+
<% } %>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# <%= name %>
|
|
2
|
+
|
|
3
|
+
A command-line tool scaffolded by GroundWork. It is built on the **composable spine**
|
|
4
|
+
every GroundWork CLI shares — structured `--json` output, exit-code discipline, colour
|
|
5
|
+
that degrades to plain text, and a non-TTY fallback so it never blocks in CI or pipes —
|
|
6
|
+
and it is themed from your design system's **brand tokens**, so it looks like a sibling of
|
|
7
|
+
this project's `./dev` control plane.
|
|
8
|
+
|
|
9
|
+
## Develop
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install
|
|
13
|
+
npm run build # bundle to dist/cli.js
|
|
14
|
+
npm run typecheck # tsc --noEmit
|
|
15
|
+
npm test # node:test — compiles src/ to dist-test/ and runs *.test.ts
|
|
16
|
+
node dist/cli.js --help
|
|
17
|
+
node dist/cli.js hello you
|
|
18
|
+
node dist/cli.js hello you --json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/
|
|
25
|
+
├── cli.ts # entry: arg parsing, dispatch, --json, exit codes
|
|
26
|
+
├── registry.ts # the command registry (single source of truth)
|
|
27
|
+
├── registry.test.ts # keeps the registry honest as commands accrete
|
|
28
|
+
├── brand.json # brand tokens projected from the design system
|
|
29
|
+
├── theme/ # the shared render layer (colour resolution, output hierarchy)
|
|
30
|
+
│ ├── tokens.ts
|
|
31
|
+
│ ├── color.ts
|
|
32
|
+
│ └── render.ts
|
|
33
|
+
<% if (core) { %>├── core/
|
|
34
|
+
│ ├── client.ts # the core-access seam — the ONLY place this CLI talks to the workspace gateway
|
|
35
|
+
│ └── client.test.ts # seam proven without a network (fetch injected)
|
|
36
|
+
<% } %>└── commands/
|
|
37
|
+
└── hello.ts # a sample command — replace with your own
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`src/brand.json` is projected from `.groundwork/config/brand-tokens.json` at scaffold time.
|
|
41
|
+
Re-run the design system to evolve the brand; update `brand.json` to match.
|
|
42
|
+
|
|
43
|
+
<% if (core) { %>## Workspace core mode
|
|
44
|
+
|
|
45
|
+
This CLI fronts the workspace's services. All gateway traffic flows through the
|
|
46
|
+
**core-access seam** (`src/core/client.ts`) — commands consume its typed functions and
|
|
47
|
+
never carry transport detail themselves.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node dist/cli.js status # the wiring proof: probes the core's /health
|
|
51
|
+
node dist/cli.js status --json # {"baseUrl": ..., "reachable": ..., "status": ...}
|
|
52
|
+
API_BASE_URL=http://staging:4000 node dist/cli.js status
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **Base URL** — defaults to `http://localhost:4000` (the workspace gateway); override
|
|
56
|
+
with the `API_BASE_URL` environment variable.
|
|
57
|
+
- **Auth** — unauthenticated by default. Wire your identity provider in
|
|
58
|
+
`coreAuthHeaders` (a service token becomes a `Bearer` header); `/health` stays public.
|
|
59
|
+
- **Exit codes** — `status` exits 0 when the core is reachable, 1 when it is not, so
|
|
60
|
+
scripts and the system-test harness gate on it without parsing output.
|
|
61
|
+
- **Growing the contract** — add one thin function per promoted-contract operation to
|
|
62
|
+
`src/core/client.ts`; when the contract outgrows hand-rolled functions, switch to a
|
|
63
|
+
generated client and keep this module as the seam.
|
|
64
|
+
|
|
65
|
+
<% } else { %>## Standalone
|
|
66
|
+
|
|
67
|
+
This CLI was scaffolded **standalone** — it fronts no workspace services and carries no
|
|
68
|
+
core client. If it later grows into a frontend for the workspace's services, re-run the
|
|
69
|
+
generator with `--core=true` (or copy the `src/core/` seam from a core-wired sibling)
|
|
70
|
+
to add the core-access seam and the `status` wiring-proof command.
|
|
71
|
+
|
|
72
|
+
<% } %>## Add a command
|
|
73
|
+
|
|
74
|
+
1. Create `src/commands/<name>.ts` exporting `async function <name>(ctx): Promise<number>`.
|
|
75
|
+
2. Register it in `src/registry.ts`.
|
|
76
|
+
3. It gets `--json`, exit-code handling, and themed output for free.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Bundles the CLI into a single executable at dist/cli.js.
|
|
2
|
+
import { build } from 'esbuild';
|
|
3
|
+
|
|
4
|
+
await build({
|
|
5
|
+
entryPoints: ['src/cli.ts'],
|
|
6
|
+
bundle: true,
|
|
7
|
+
platform: 'node',
|
|
8
|
+
target: 'node18',
|
|
9
|
+
format: 'cjs',
|
|
10
|
+
outfile: 'dist/cli.js',
|
|
11
|
+
banner: { js: '#!/usr/bin/env node' },
|
|
12
|
+
loader: { '.json': 'json' },
|
|
13
|
+
legalComments: 'none',
|
|
14
|
+
logLevel: 'info',
|
|
15
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= binName %>",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"<%= binName %>": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "node build.mjs",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "tsc -p tsconfig.test.json && node --test \"dist-test/**/*.test.js\"",
|
|
13
|
+
"dev": "node build.mjs && node dist/cli.js",
|
|
14
|
+
"start": "node dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^22.0.0",
|
|
18
|
+
"esbuild": "^0.25.0",
|
|
19
|
+
"typescript": "^5.6.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* <%= name %> — command-line entry point.
|
|
3
|
+
*
|
|
4
|
+
* Built on the composable spine every GroundWork CLI shares: structured `--json`
|
|
5
|
+
* output, exit-code discipline (0/1/2, 130 on interrupt), colour that degrades, and a
|
|
6
|
+
* non-TTY fallback so the tool never blocks in CI or pipes. The render layer in
|
|
7
|
+
* src/theme/ is themed from your design system's brand tokens (src/brand.json), so this
|
|
8
|
+
* CLI and the project's ./dev control plane look like kin.
|
|
9
|
+
*/
|
|
10
|
+
import brand from './brand.json';
|
|
11
|
+
import { COMMANDS, findCommand, CommandGroup } from './registry';
|
|
12
|
+
import { makeRenderer, Renderer } from './theme/render';
|
|
13
|
+
|
|
14
|
+
function showHelp(r: Renderer): void {
|
|
15
|
+
r.logo('<%= name %>');
|
|
16
|
+
const groups: CommandGroup[] = ['CORE', 'META'];
|
|
17
|
+
for (const g of groups) {
|
|
18
|
+
const cmds = COMMANDS.filter((c) => c.group === g);
|
|
19
|
+
if (cmds.length === 0) continue;
|
|
20
|
+
r.category(g);
|
|
21
|
+
for (const c of cmds) r.cmd(c.name, c.summary);
|
|
22
|
+
}
|
|
23
|
+
process.stderr.write('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function main(): Promise<number> {
|
|
27
|
+
const argv = process.argv.slice(2);
|
|
28
|
+
let json = false;
|
|
29
|
+
let help = false;
|
|
30
|
+
const rest: string[] = [];
|
|
31
|
+
for (const a of argv) {
|
|
32
|
+
if (a === '--json') json = true;
|
|
33
|
+
else if (a === '-h' || a === '--help') help = true;
|
|
34
|
+
else if (a === '--version' || a === '-V') {
|
|
35
|
+
process.stdout.write('<%= binName %> 0.1.0\n');
|
|
36
|
+
return 0;
|
|
37
|
+
} else rest.push(a);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const r = makeRenderer(brand);
|
|
41
|
+
const command = rest[0];
|
|
42
|
+
const args = rest.slice(1);
|
|
43
|
+
|
|
44
|
+
if (!command || command === 'help' || help) {
|
|
45
|
+
showHelp(r);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
const def = findCommand(command);
|
|
49
|
+
if (!def) {
|
|
50
|
+
r.error(`Unknown command: ${command}`);
|
|
51
|
+
showHelp(r);
|
|
52
|
+
return 2;
|
|
53
|
+
}
|
|
54
|
+
return def.handler({ r, json, args });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
process.on('SIGINT', () => {
|
|
58
|
+
process.stderr.write('\n\x1b[?25h');
|
|
59
|
+
process.exit(130);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
main()
|
|
63
|
+
.then((code) => process.exit(code))
|
|
64
|
+
.catch((err) => {
|
|
65
|
+
makeRenderer(brand).error(err?.message ?? String(err));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Ctx } from '../registry';
|
|
2
|
+
|
|
3
|
+
/** A sample command. It demonstrates the spine: themed human output by default, clean
|
|
4
|
+
* JSON on stdout under --json. Replace it with your own commands. */
|
|
5
|
+
export async function hello(ctx: Ctx): Promise<number> {
|
|
6
|
+
const who = ctx.args[0] ?? 'world';
|
|
7
|
+
|
|
8
|
+
if (ctx.json) {
|
|
9
|
+
process.stdout.write(JSON.stringify({ greeting: `Hello, ${who}!` }) + '\n');
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ctx.r.logo();
|
|
14
|
+
ctx.r.success(`Hello, ${who}!`);
|
|
15
|
+
ctx.r.info('Edit src/commands/ and register new commands in src/registry.ts.');
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Ctx } from '../registry';
|
|
2
|
+
import { coreBaseUrl, fetchCoreHealth } from '../core/client';
|
|
3
|
+
|
|
4
|
+
/** Probe the workspace core this CLI fronts — the surface's wiring proof.
|
|
5
|
+
* Exit-code discipline carries the verdict: 0 reachable, 1 unreachable, so
|
|
6
|
+
* scripts and the system-test harness gate on it without parsing output. */
|
|
7
|
+
export async function status(ctx: Ctx): Promise<number> {
|
|
8
|
+
const baseUrl = coreBaseUrl();
|
|
9
|
+
const health = await fetchCoreHealth(baseUrl);
|
|
10
|
+
|
|
11
|
+
if (ctx.json) {
|
|
12
|
+
process.stdout.write(JSON.stringify({ baseUrl, ...health }) + '\n');
|
|
13
|
+
return health.reachable ? 0 : 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (health.reachable) {
|
|
17
|
+
ctx.r.success(`Workspace core reachable at ${baseUrl} — status ${health.status}`);
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
ctx.r.error(`Workspace core unreachable at ${baseUrl} (${health.status})`);
|
|
21
|
+
ctx.r.info('Set API_BASE_URL to point at your workspace gateway.');
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// The core-access seam is proven without a network: fetch is injected, so
|
|
2
|
+
// every reachability outcome (ok, non-200, thrown) is a deterministic case.
|
|
3
|
+
// Runs on node:test — the CLI's test layer adds no dependencies, like the
|
|
4
|
+
// CLI itself.
|
|
5
|
+
import { describe, it } from 'node:test';
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_CORE_BASE_URL,
|
|
9
|
+
coreAuthHeaders,
|
|
10
|
+
coreBaseUrl,
|
|
11
|
+
fetchCoreHealth,
|
|
12
|
+
} from './client';
|
|
13
|
+
|
|
14
|
+
function fetchReturning(status: number, body: unknown): typeof fetch {
|
|
15
|
+
return (async () =>
|
|
16
|
+
new Response(JSON.stringify(body), {
|
|
17
|
+
status,
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
})) as typeof fetch;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('coreBaseUrl', () => {
|
|
23
|
+
it('defaults to the workspace gateway', () => {
|
|
24
|
+
assert.equal(coreBaseUrl({}), DEFAULT_CORE_BASE_URL);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('honours API_BASE_URL from the environment', () => {
|
|
28
|
+
assert.equal(coreBaseUrl({ API_BASE_URL: 'http://10.0.0.5:4000' }), 'http://10.0.0.5:4000');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('treats a blank API_BASE_URL as unset', () => {
|
|
32
|
+
assert.equal(coreBaseUrl({ API_BASE_URL: ' ' }), DEFAULT_CORE_BASE_URL);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('coreAuthHeaders', () => {
|
|
37
|
+
it('is unauthenticated by default', () => {
|
|
38
|
+
assert.deepEqual(coreAuthHeaders(), {});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('carries a supplied token as a Bearer header', () => {
|
|
42
|
+
assert.deepEqual(coreAuthHeaders('jwt-123'), { Authorization: 'Bearer jwt-123' });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('fetchCoreHealth', () => {
|
|
47
|
+
it('maps a healthy core to reachable + its reported status', async () => {
|
|
48
|
+
const health = await fetchCoreHealth(
|
|
49
|
+
'http://core.test',
|
|
50
|
+
fetchReturning(200, { status: 'ok', checks: { db: 'ok' } }),
|
|
51
|
+
);
|
|
52
|
+
assert.deepEqual(health, { reachable: true, status: 'ok' });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('requests the /health route of the configured base URL', async () => {
|
|
56
|
+
let requested: string | undefined;
|
|
57
|
+
const probe = (async (input: Parameters<typeof fetch>[0]) => {
|
|
58
|
+
requested = String(input);
|
|
59
|
+
return new Response('{"status":"ok"}', { status: 200 });
|
|
60
|
+
}) as typeof fetch;
|
|
61
|
+
await fetchCoreHealth('http://core.test:4000', probe);
|
|
62
|
+
assert.equal(requested, 'http://core.test:4000/health');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('maps a non-200 to unreachable with the code as the status', async () => {
|
|
66
|
+
const health = await fetchCoreHealth(
|
|
67
|
+
'http://core.test',
|
|
68
|
+
fetchReturning(503, { error: 'overloaded' }),
|
|
69
|
+
);
|
|
70
|
+
assert.deepEqual(health, { reachable: false, status: 'http 503' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('maps a network failure to a value, never a throw', async () => {
|
|
74
|
+
const down = (async () => {
|
|
75
|
+
throw new Error('ECONNREFUSED');
|
|
76
|
+
}) as typeof fetch;
|
|
77
|
+
const health = await fetchCoreHealth('http://core.test', down);
|
|
78
|
+
assert.deepEqual(health, { reachable: false, status: 'unreachable' });
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// The core-access seam: the ONLY place this CLI talks to the workspace
|
|
2
|
+
// gateway. Commands consume these functions and never carry transport
|
|
3
|
+
// detail themselves — the terminal twin of the desktop scaffold's
|
|
4
|
+
// main-process core client and the mobile scaffold's thin dio client.
|
|
5
|
+
//
|
|
6
|
+
// Contract-client stance (O8): hand-rolled and deliberately thin — one
|
|
7
|
+
// function per promoted-contract operation, typed results beside them.
|
|
8
|
+
// When the promoted openapi.yaml grows past a handful of operations, switch
|
|
9
|
+
// to a generated TypeScript client and keep this module as the seam the
|
|
10
|
+
// commands consume — nothing above it changes.
|
|
11
|
+
|
|
12
|
+
/** The workspace core's health as commands render it. An unreachable core
|
|
13
|
+
* is a value, not an exception: a CLI fronting a down service reports it
|
|
14
|
+
* through output and exit code, never a stack trace. */
|
|
15
|
+
export type CoreHealth = {
|
|
16
|
+
reachable: boolean;
|
|
17
|
+
status: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_CORE_BASE_URL = 'http://localhost:4000';
|
|
21
|
+
|
|
22
|
+
/** Where the workspace core lives. The system-test harness (and any
|
|
23
|
+
* deployed environment) overrides via the API_BASE_URL environment
|
|
24
|
+
* variable. Standalone use never reaches this module — the seam only
|
|
25
|
+
* exists when the CLI fronts workspace services. */
|
|
26
|
+
export function coreBaseUrl(env: NodeJS.ProcessEnv = process.env): string {
|
|
27
|
+
const configured = env['API_BASE_URL']?.trim();
|
|
28
|
+
return configured ? configured : DEFAULT_CORE_BASE_URL;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The auth seam: headers attached to every core request. Unauthenticated
|
|
32
|
+
* by default — wire your identity provider here (e.g. a service token from
|
|
33
|
+
* the environment becomes `Authorization: Bearer <token>`); the core's
|
|
34
|
+
* /health route stays public. */
|
|
35
|
+
export function coreAuthHeaders(token?: string): Record<string, string> {
|
|
36
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Probe the gateway's health endpoint — the wiring proof the `status`
|
|
40
|
+
* command renders. `/health` is what GroundWork's Go and Python cores
|
|
41
|
+
* serve; a Next.js BFF would be `/api/healthz` — adjust to your gateway.
|
|
42
|
+
* Failures map to a value, never a throw: an unreachable core is a state
|
|
43
|
+
* the command reports (exit 1), not an exception that kills the process. */
|
|
44
|
+
export async function fetchCoreHealth(
|
|
45
|
+
baseUrl: string = coreBaseUrl(),
|
|
46
|
+
fetchImpl: typeof fetch = fetch,
|
|
47
|
+
): Promise<CoreHealth> {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetchImpl(new URL('/health', baseUrl), {
|
|
50
|
+
headers: { Accept: 'application/json', ...coreAuthHeaders() },
|
|
51
|
+
signal: AbortSignal.timeout(5_000),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
return { reachable: false, status: `http ${response.status}` };
|
|
55
|
+
}
|
|
56
|
+
const body = (await response.json()) as { status?: unknown };
|
|
57
|
+
return {
|
|
58
|
+
reachable: true,
|
|
59
|
+
status: typeof body.status === 'string' ? body.status : 'ok',
|
|
60
|
+
};
|
|
61
|
+
} catch {
|
|
62
|
+
return { reachable: false, status: 'unreachable' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The registry is the single source of truth for dispatch and help — these
|
|
2
|
+
// tests keep it honest as commands accrete. Runs on node:test (zero deps).
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { COMMANDS, findCommand } from './registry';
|
|
6
|
+
|
|
7
|
+
describe('command registry', () => {
|
|
8
|
+
it('registers at least one command', () => {
|
|
9
|
+
assert.ok(COMMANDS.length > 0);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('has no duplicate command names', () => {
|
|
13
|
+
const names = COMMANDS.map((c) => c.name);
|
|
14
|
+
assert.equal(new Set(names).size, names.length);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('gives every command a group and a summary (help renders from these)', () => {
|
|
18
|
+
for (const c of COMMANDS) {
|
|
19
|
+
assert.ok(c.summary.length > 0, `command ${c.name} has no summary`);
|
|
20
|
+
assert.ok(['CORE', 'META'].includes(c.group), `command ${c.name} has an unknown group`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('resolves a registered command', () => {
|
|
25
|
+
assert.ok(findCommand('hello'));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns undefined for an unknown command (cli.ts exits 2 on this)', () => {
|
|
29
|
+
assert.equal(findCommand('no-such-command'), undefined);
|
|
30
|
+
});
|
|
31
|
+
<% if (core) { %>
|
|
32
|
+
it('registers the core status probe — the wiring proof to the workspace core', () => {
|
|
33
|
+
assert.ok(findCommand('status'));
|
|
34
|
+
});
|
|
35
|
+
<% } %>});
|