workspai 0.43.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.
Files changed (255) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +624 -0
  3. package/contracts/agent-customization-pack.v1.json +229 -0
  4. package/contracts/analyze-last-run.v1.json +106 -0
  5. package/contracts/artifact-remediation-plan.v1.json +163 -0
  6. package/contracts/backend-import-stack-parity.snapshot.json +37 -0
  7. package/contracts/cli-log-event.v1.json +51 -0
  8. package/contracts/create-planner-capabilities.v1.json +251 -0
  9. package/contracts/doctor-project-evidence.v1.json +304 -0
  10. package/contracts/doctor-remediation-plan.v1.json +496 -0
  11. package/contracts/doctor-workspace-evidence.v1.json +315 -0
  12. package/contracts/extension-cli-compatibility.v1.json +26 -0
  13. package/contracts/infra-stack.v1.json +305 -0
  14. package/contracts/module-layout.v1.json +34 -0
  15. package/contracts/module-support.v1.json +20 -0
  16. package/contracts/pipeline-last-run.v1.json +80 -0
  17. package/contracts/release-readiness.v1.json +60 -0
  18. package/contracts/runtime-command-surface.v1.json +364 -0
  19. package/contracts/workspace-intelligence/agent-action-outcome.v1.json +38 -0
  20. package/contracts/workspace-intelligence/blocker-resolution.v1.json +65 -0
  21. package/contracts/workspace-intelligence/doctor-fix-result.v1.json +44 -0
  22. package/contracts/workspace-intelligence/fact-freshness.v1.json +58 -0
  23. package/contracts/workspace-intelligence/studio-blocker-handoff.v1.json +115 -0
  24. package/contracts/workspace-intelligence/workspace-context.v1.json +142 -0
  25. package/contracts/workspace-intelligence/workspace-contract-verify.v1.json +51 -0
  26. package/contracts/workspace-intelligence/workspace-dependency-graph.v1.json +152 -0
  27. package/contracts/workspace-intelligence/workspace-explain.v1.json +31 -0
  28. package/contracts/workspace-intelligence/workspace-impact.v1.json +114 -0
  29. package/contracts/workspace-intelligence/workspace-intelligence-history.v1.json +40 -0
  30. package/contracts/workspace-intelligence/workspace-model-diff.v1.json +105 -0
  31. package/contracts/workspace-intelligence/workspace-model-snapshot.v1.json +36 -0
  32. package/contracts/workspace-intelligence/workspace-model.v1.json +135 -0
  33. package/contracts/workspace-intelligence/workspace-operational-skill.v1.json +37 -0
  34. package/contracts/workspace-intelligence/workspace-skills-index.v1.json +27 -0
  35. package/contracts/workspace-intelligence/workspace-verify.v1.json +213 -0
  36. package/contracts/workspace-registry.v1.json +83 -0
  37. package/contracts/workspace-run-last.v1.json +112 -0
  38. package/data/modules-embeddings.json +16953 -0
  39. package/dist/analyze-5BU4QIPK.js +1 -0
  40. package/dist/artifact-remediation-plan-5GJWPPRS.js +3 -0
  41. package/dist/autopilot-release-QYUOVB26.js +1 -0
  42. package/dist/chunk-22DT744Z.js +1 -0
  43. package/dist/chunk-2E6WGUN6.js +1 -0
  44. package/dist/chunk-2FIX2MDC.js +1 -0
  45. package/dist/chunk-3KXW53QI.js +1 -0
  46. package/dist/chunk-3ZK2GU7C.js +1 -0
  47. package/dist/chunk-4KUIFXHM.js +5 -0
  48. package/dist/chunk-54XJNLYC.js +13 -0
  49. package/dist/chunk-5SWNSJGW.js +1 -0
  50. package/dist/chunk-5XATWNME.js +1 -0
  51. package/dist/chunk-753LOKFZ.js +2 -0
  52. package/dist/chunk-7YHK5NM3.js +2 -0
  53. package/dist/chunk-A5FBGRJA.js +1 -0
  54. package/dist/chunk-AAKUNWPR.js +78 -0
  55. package/dist/chunk-AFSZHLQT.js +1 -0
  56. package/dist/chunk-AQ4XZZC6.js +1 -0
  57. package/dist/chunk-CVTCPPEF.js +1 -0
  58. package/dist/chunk-DNAY5ALC.js +1 -0
  59. package/dist/chunk-EWYYLJQY.js +2 -0
  60. package/dist/chunk-GSWPGELT.js +2 -0
  61. package/dist/chunk-GZTYAEWX.js +1 -0
  62. package/dist/chunk-HTYMXMQM.js +1 -0
  63. package/dist/chunk-IBJEQHJR.js +205 -0
  64. package/dist/chunk-ICGWHIMK.js +1 -0
  65. package/dist/chunk-JEI6BTZI.js +3 -0
  66. package/dist/chunk-JHC6SCJC.js +1 -0
  67. package/dist/chunk-LO4C4VXR.js +4 -0
  68. package/dist/chunk-LPZFK3Y6.js +920 -0
  69. package/dist/chunk-LUP4U6EE.js +1 -0
  70. package/dist/chunk-LYC6AIF2.js +2 -0
  71. package/dist/chunk-LZQNVH7D.js +1 -0
  72. package/dist/chunk-NAAQMSO3.js +1 -0
  73. package/dist/chunk-NFUXULIF.js +1 -0
  74. package/dist/chunk-OCGZNSOE.js +1 -0
  75. package/dist/chunk-OJEWQO3Z.js +33 -0
  76. package/dist/chunk-OL64GWQC.js +9 -0
  77. package/dist/chunk-PI5KUXDA.js +7 -0
  78. package/dist/chunk-PYCJWW4B.js +1 -0
  79. package/dist/chunk-RE773WOX.js +13 -0
  80. package/dist/chunk-RO73VVHW.js +2 -0
  81. package/dist/chunk-S5KTATOU.js +1 -0
  82. package/dist/chunk-SI3AQKIP.js +1 -0
  83. package/dist/chunk-T3GJ7YVG.js +1 -0
  84. package/dist/chunk-T6OR6YOI.js +2 -0
  85. package/dist/chunk-VRDF2J46.js +653 -0
  86. package/dist/chunk-VZKSNUEE.js +2 -0
  87. package/dist/chunk-WU4ZRRA5.js +4 -0
  88. package/dist/chunk-XEJD4KCV.js +33 -0
  89. package/dist/chunk-XZGVNGRB.js +1 -0
  90. package/dist/chunk-Y45WZR5N.js +5 -0
  91. package/dist/create-WRZO4HK2.js +1 -0
  92. package/dist/demo-kit-AJ5LIM5G.js +149 -0
  93. package/dist/doctor-MF3M3FS3.js +1 -0
  94. package/dist/dotnet-webapi-clean-IPY3MCGQ.js +425 -0
  95. package/dist/gofiber-standard-N5N3QGH3.js +1397 -0
  96. package/dist/gogin-standard-LAWQTBWL.js +1459 -0
  97. package/dist/imported-projects-registry-FOIE27WT.js +1 -0
  98. package/dist/index.d.ts +141 -0
  99. package/dist/index.js +454 -0
  100. package/dist/managed-agent-markers-AXUM75OE.js +8 -0
  101. package/dist/module-layout-NZ43RSC5.js +1 -0
  102. package/dist/pipeline-QCUHFWDY.js +5 -0
  103. package/dist/platform-capabilities-XAVU4HSM.js +1 -0
  104. package/dist/pythonRapidkitExec-3L3XJBSY.js +1 -0
  105. package/dist/springboot-standard-OZYF7ZM7.js +697 -0
  106. package/dist/workspace-PR6VZRBG.js +1 -0
  107. package/dist/workspace-agent-sync-KTYF3HQR.js +1 -0
  108. package/dist/workspace-archive-GCZNI44O.js +3 -0
  109. package/dist/workspace-context-QUTOP2XV.js +1 -0
  110. package/dist/workspace-contract-XUGMCUSQ.js +1 -0
  111. package/dist/workspace-explain-JEONEGZZ.js +1 -0
  112. package/dist/workspace-explain-contract-DNS2YEL7.js +1 -0
  113. package/dist/workspace-feedback-R6H4UFZB.js +1 -0
  114. package/dist/workspace-foundation-KGSV4E6D.js +1 -0
  115. package/dist/workspace-graph-2A5THUCI.js +1 -0
  116. package/dist/workspace-history-CGCK2DJI.js +1 -0
  117. package/dist/workspace-intelligence-QJGVL37U.js +1 -0
  118. package/dist/workspace-marker-SMBC3Z2Q.js +1 -0
  119. package/dist/workspace-mcp-serve-AK4MYEHQ.js +3 -0
  120. package/dist/workspace-model-PNXXOGPO.js +1 -0
  121. package/dist/workspace-python-engine-state-MTWIIZPY.js +2 -0
  122. package/dist/workspace-registry-summary-RUJE6RPH.js +1 -0
  123. package/dist/workspace-run-Y7YMUIHX.js +1 -0
  124. package/dist/workspace-verify-WSDUUTR5.js +1 -0
  125. package/dist/workspace-watch-AGPOJ74Q.js +1 -0
  126. package/docs/AI_DYNAMIC_INTEGRATION.md +440 -0
  127. package/docs/AI_EXAMPLES.md +419 -0
  128. package/docs/AI_FEATURES.md +460 -0
  129. package/docs/AI_QUICKSTART.md +245 -0
  130. package/docs/DEVELOPMENT.md +90 -0
  131. package/docs/From Code to Shared Understanding.png +0 -0
  132. package/docs/OPEN_SOURCE_USER_SCENARIOS.md +170 -0
  133. package/docs/OPTIMIZATION_GUIDE.md +504 -0
  134. package/docs/PACKAGE_MANAGER_POLICY.md +25 -0
  135. package/docs/README.md +125 -0
  136. package/docs/SECURITY.md +63 -0
  137. package/docs/SETUP.md +107 -0
  138. package/docs/UTILITIES.md +221 -0
  139. package/docs/WORKSPACE_MARKER_SPEC.md +276 -0
  140. package/docs/ci-workflows.md +59 -0
  141. package/docs/commands-reference.md +172 -0
  142. package/docs/config-file-guide.md +299 -0
  143. package/docs/contracts/ARTIFACT_CATALOG.md +333 -0
  144. package/docs/contracts/CLI_LOG_EVENT_STREAM.md +123 -0
  145. package/docs/contracts/COMMAND_OWNERSHIP_MATRIX.md +138 -0
  146. package/docs/contracts/NAMING_AND_COEXISTENCE.md +60 -0
  147. package/docs/contracts/README.md +86 -0
  148. package/docs/contracts/RUNTIME_ACCEPTANCE_MATRIX.md +98 -0
  149. package/docs/contracts/RUNTIME_SUPPORT_MATRIX.md +75 -0
  150. package/docs/contracts/rapidkit-cli-contracts.json +239 -0
  151. package/docs/create-planner-capabilities.md +81 -0
  152. package/docs/doctor-command.md +467 -0
  153. package/docs/examples/ci-agent-grounding.yml +65 -0
  154. package/docs/from-code-to-shared-understanding.md +45 -0
  155. package/docs/governance-policy.enterprise.example.json +40 -0
  156. package/docs/mirror-config.enterprise.example.json +60 -0
  157. package/docs/policies.workspace.example.yml +23 -0
  158. package/docs/workspace-operations.md +160 -0
  159. package/docs/workspace-run.md +104 -0
  160. package/package.json +179 -0
  161. package/rapidkit.config.example.cjs +62 -0
  162. package/scripts/check-agent-customization-drift.mjs +78 -0
  163. package/scripts/check-cli-resolution.cjs +72 -0
  164. package/scripts/enforce-package-manager.cjs +10 -0
  165. package/scripts/enterprise-package-smoke.mjs +463 -0
  166. package/scripts/prepack-enterprise.mjs +43 -0
  167. package/templates/generator.js +179 -0
  168. package/templates/kits/fastapi-ddd/README.md.j2 +117 -0
  169. package/templates/kits/fastapi-ddd/common/env.example.j2 +10 -0
  170. package/templates/kits/fastapi-ddd/env.example.j2 +1 -0
  171. package/templates/kits/fastapi-ddd/pyproject.toml.j2 +64 -0
  172. package/templates/kits/fastapi-ddd/src/__init__.py.j2 +3 -0
  173. package/templates/kits/fastapi-ddd/src/app/__init__.py.j2 +11 -0
  174. package/templates/kits/fastapi-ddd/src/app/application/__init__.py.j2 +5 -0
  175. package/templates/kits/fastapi-ddd/src/app/application/interfaces.py.j2 +43 -0
  176. package/templates/kits/fastapi-ddd/src/app/application/use_cases/__init__.py.j2 +6 -0
  177. package/templates/kits/fastapi-ddd/src/app/application/use_cases/health.py.j2 +14 -0
  178. package/templates/kits/fastapi-ddd/src/app/application/use_cases/notes.py.j2 +24 -0
  179. package/templates/kits/fastapi-ddd/src/app/config/__init__.py.j2 +16 -0
  180. package/templates/kits/fastapi-ddd/src/app/domain/__init__.py.j2 +3 -0
  181. package/templates/kits/fastapi-ddd/src/app/domain/models/__init__.py.j2 +6 -0
  182. package/templates/kits/fastapi-ddd/src/app/domain/models/health.py.j2 +16 -0
  183. package/templates/kits/fastapi-ddd/src/app/domain/models/note.py.j2 +27 -0
  184. package/templates/kits/fastapi-ddd/src/app/infrastructure/__init__.py.j2 +5 -0
  185. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/__init__.py.j2 +6 -0
  186. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/health.py.j2 +17 -0
  187. package/templates/kits/fastapi-ddd/src/app/infrastructure/repositories/notes.py.j2 +28 -0
  188. package/templates/kits/fastapi-ddd/src/app/main.py.j2 +61 -0
  189. package/templates/kits/fastapi-ddd/src/app/presentation/__init__.py.j2 +3 -0
  190. package/templates/kits/fastapi-ddd/src/app/presentation/api/__init__.py.j2 +5 -0
  191. package/templates/kits/fastapi-ddd/src/app/presentation/api/dependencies/__init__.py.j2 +19 -0
  192. package/templates/kits/fastapi-ddd/src/app/presentation/api/router.py.j2 +10 -0
  193. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/__init__.py.j2 +5 -0
  194. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/health.py.j2 +27 -0
  195. package/templates/kits/fastapi-ddd/src/app/presentation/api/routes/notes.py.j2 +50 -0
  196. package/templates/kits/fastapi-ddd/src/app/shared/__init__.py.j2 +5 -0
  197. package/templates/kits/fastapi-ddd/src/app/shared/result.py.j2 +28 -0
  198. package/templates/kits/fastapi-ddd/src/cli.py.j2 +170 -0
  199. package/templates/kits/fastapi-ddd/src/main.py.j2 +35 -0
  200. package/templates/kits/fastapi-ddd/src/modules/__init__.py.j2 +3 -0
  201. package/templates/kits/fastapi-ddd/src/routing/__init__.py.j2 +13 -0
  202. package/templates/kits/fastapi-ddd/src/routing/health.py.j2 +7 -0
  203. package/templates/kits/fastapi-ddd/src/routing/notes.py.j2 +7 -0
  204. package/templates/kits/fastapi-ddd/tests/__init__.py.j2 +1 -0
  205. package/templates/kits/fastapi-ddd/tests/test_app_factory.py.j2 +22 -0
  206. package/templates/kits/fastapi-ddd/tests/test_health.py.j2 +17 -0
  207. package/templates/kits/fastapi-ddd/tests/test_notes.py.j2 +27 -0
  208. package/templates/kits/fastapi-standard/README.md.j2 +140 -0
  209. package/templates/kits/fastapi-standard/common/env.example.j2 +10 -0
  210. package/templates/kits/fastapi-standard/env.example.j2 +1 -0
  211. package/templates/kits/fastapi-standard/pyproject.toml.j2 +64 -0
  212. package/templates/kits/fastapi-standard/src/__init__.py.j2 +3 -0
  213. package/templates/kits/fastapi-standard/src/cli.py.j2 +171 -0
  214. package/templates/kits/fastapi-standard/src/main.py.j2 +66 -0
  215. package/templates/kits/fastapi-standard/src/modules/__init__.py.j2 +3 -0
  216. package/templates/kits/fastapi-standard/src/routing/__init__.py.j2 +16 -0
  217. package/templates/kits/fastapi-standard/src/routing/examples.py.j2 +71 -0
  218. package/templates/kits/fastapi-standard/src/routing/health.py.j2 +22 -0
  219. package/templates/kits/fastapi-standard/tests/__init__.py.j2 +1 -0
  220. package/templates/kits/fastapi-standard/tests/test_examples.py.j2 +29 -0
  221. package/templates/kits/fastapi-standard/tests/test_health.py.j2 +17 -0
  222. package/templates/kits/nestjs-standard/Dockerfile.j2 +41 -0
  223. package/templates/kits/nestjs-standard/README.md.j2 +134 -0
  224. package/templates/kits/nestjs-standard/docker-compose.yml.j2 +94 -0
  225. package/templates/kits/nestjs-standard/docs/README.md.j2 +15 -0
  226. package/templates/kits/nestjs-standard/env.example.j2 +18 -0
  227. package/templates/kits/nestjs-standard/eslint.config.cjs.j2 +9 -0
  228. package/templates/kits/nestjs-standard/jest.config.ts.j2 +28 -0
  229. package/templates/kits/nestjs-standard/nest-cli.json.j2 +10 -0
  230. package/templates/kits/nestjs-standard/package.json.j2 +101 -0
  231. package/templates/kits/nestjs-standard/src/app.controller.ts.j2 +14 -0
  232. package/templates/kits/nestjs-standard/src/app.module.ts.j2 +26 -0
  233. package/templates/kits/nestjs-standard/src/app.service.ts.j2 +16 -0
  234. package/templates/kits/nestjs-standard/src/auth/auth.controller.ts.j2 +20 -0
  235. package/templates/kits/nestjs-standard/src/auth/auth.module.ts.j2 +13 -0
  236. package/templates/kits/nestjs-standard/src/auth/auth.service.ts.j2 +6 -0
  237. package/templates/kits/nestjs-standard/src/auth/entities/token.entity.ts.j2 +3 -0
  238. package/templates/kits/nestjs-standard/src/auth/entities/user.entity.ts.j2 +3 -0
  239. package/templates/kits/nestjs-standard/src/auth/entities/webauthn.entity.ts.j2 +3 -0
  240. package/templates/kits/nestjs-standard/src/config/configuration.ts.j2 +85 -0
  241. package/templates/kits/nestjs-standard/src/config/index.ts.j2 +2 -0
  242. package/templates/kits/nestjs-standard/src/config/validation.ts.j2 +21 -0
  243. package/templates/kits/nestjs-standard/src/examples/dto/create-note.dto.ts.j2 +11 -0
  244. package/templates/kits/nestjs-standard/src/examples/examples.controller.ts.j2 +24 -0
  245. package/templates/kits/nestjs-standard/src/examples/examples.module.ts.j2 +10 -0
  246. package/templates/kits/nestjs-standard/src/examples/examples.service.ts.j2 +33 -0
  247. package/templates/kits/nestjs-standard/src/main.ts.j2 +53 -0
  248. package/templates/kits/nestjs-standard/src/modules/index.ts.j2 +25 -0
  249. package/templates/kits/nestjs-standard/test/app.controller.spec.ts.j2 +24 -0
  250. package/templates/kits/nestjs-standard/test/app.e2e-spec.ts.j2 +60 -0
  251. package/templates/kits/nestjs-standard/test/examples.controller.spec.ts.j2 +28 -0
  252. package/templates/kits/nestjs-standard/test/jest-e2e.json.j2 +15 -0
  253. package/templates/kits/nestjs-standard/tsconfig.build.json.j2 +12 -0
  254. package/templates/kits/nestjs-standard/tsconfig.json.j2 +26 -0
  255. package/workspai.config.example.cjs +62 -0
@@ -0,0 +1,140 @@
1
+ # {{ project_name | replace('_', ' ') | title }}
2
+
3
+ A minimal FastAPI service generated with Workspai's **FastAPI Standard Kit**. Python-backed capabilities (configuration, logging, persistence, observability, authentication, etc.) are provided by RapidKit Core modules when you opt into the Python engine.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ # Bootstrap dependencies (creates .venv + installs Poetry-managed deps)
9
+ npx workspai init # use make init if you prefer a Make target
10
+
11
+ # Copy env templates and install hooks/tooling
12
+ ./bootstrap.sh
13
+
14
+ # Run linting, typing, testing, and supply-chain audits
15
+ make lint
16
+ make typecheck
17
+ make test
18
+ make audit
19
+
20
+ # Start development server with hot reload
21
+ make dev
22
+ npx workspai dev # same as make dev but auto-detects the project
23
+
24
+ # Prefer project-local RapidKit Core helpers when you want it to resolve scripts automatically
25
+ npx workspai lint
26
+ npx workspai test
27
+ npx workspai start
28
+ ```
29
+
30
+ > Re-run `npx workspai init` (or `make init`) whenever dependencies change, or use `SKIP_INIT=1 make install` if you only need to refresh tooling/hooks without reinstalling packages.
31
+
32
+ > Lockfiles are generated automatically during scaffolding. Set `RAPIDKIT_SKIP_LOCKS=1` (or `RAPIDKIT_GENERATE_LOCKS=0`) before running `workspai create` if you need to opt out.
33
+
34
+ > Want the full project-local RapidKit Core catalog? Run `rapidkit --help` when RapidKit Core is installed, or use `npx workspai --help` for workspace-level commands.
35
+
36
+ ---
37
+
38
+ ## Local development
39
+
40
+ - `npx workspai init` bootstraps dependencies via the project-aware CLI.
41
+ - `make init` is an optional alias for `npx workspai init` when you prefer Make targets.
42
+ - `make install` re-runs `npx workspai init` (unless you set `SKIP_INIT=1`) and refreshes developer tooling such as pre-commit hooks.
43
+ - `./bootstrap.sh` copies `.env.example` to `.env` (if missing) and runs `SKIP_INIT=1 make install` for you.
44
+ - `make dev` (or `npx workspai dev`) launches Uvicorn with the correct module path and reload settings.
45
+ - `make lint`, `make typecheck`, and `make test` wrap Ruff, mypy, and pytest for consistent CI parity.
46
+ - `make audit` shells out to `pip-audit` to surface vulnerable dependencies.
47
+ - Prefer `npx workspai lint`, `npx workspai test`, and `npx workspai start` if you want the project-aware launcher to autodetect the virtualenv and command wiring.
48
+ ```
49
+
50
+ ## Available commands
51
+
52
+ ```bash
53
+ npx workspai init # 🔧 Bootstrap the project (create .venv + install deps)
54
+ make init # 🔧 Optional alias for npx workspai init
55
+ ./bootstrap.sh # 🪄 Copy env template + install hooks/tooling (idempotent)
56
+ npx workspai dev # 🚀 Start development server with hot reload
57
+ make dev # 🚀 Alternative via Makefile helper
58
+ npx workspai start # ⚡ Start production server
59
+ npx workspai lint # 🔧 Run lint checks via project-aware CLI
60
+ npx workspai test # 🧪 Run pytest through the project-aware launcher
61
+ make install # 📦 Install Poetry deps + hooks
62
+ make lint # ✅ Lint via Ruff + Black
63
+ make typecheck # 🔍 Run mypy on src
64
+ make test # 🧪 Run tests
65
+ make audit # 🔐 Run pip-audit across dependencies
66
+ make docker-up # 🐳 Start docker compose stack (if enabled)
67
+ ```
68
+
69
+ ## Project layout
70
+
71
+ - `src/main.py` – FastAPI application entrypoint
72
+ - `src/routing/` – Core routers (health) and anchors for module routers
73
+ - `src/modules/` – Module bootstrap anchors
74
+ - `pyproject.toml` – Poetry configuration and dependencies
75
+ - `Makefile` – Common developer workflows (format, lint, docker, etc.)
76
+ - `Dockerfile` / `docker-compose.yml` – Optional container setup for local dev and deployment
77
+ - `.github/workflows/ci.yml` – Optional GitHub Actions pipeline for linting and tests
78
+
79
+ ## Example feature
80
+
81
+ The scaffold ships with a tiny in-memory **notes** API mounted under `/api/examples/notes`. Use it as a safe playground for wiring routers, schemas, and tests without touching your real domain logic:
82
+
83
+ ```bash
84
+ # Create a note
85
+ curl -s -X POST http://localhost:8000/api/examples/notes \
86
+ -H "Content-Type: application/json" \
87
+ -d '{"title":"first","body":"scaffolded by Workspai"}'
88
+
89
+ # List notes
90
+ curl -s http://localhost:8000/api/examples/notes | jq
91
+ ```
92
+
93
+ The implementation intentionally stays in memory so you can replace it with a repository-backed service once you install RapidKit Core database modules.
94
+
95
+ {% if selected_modules %}
96
+ ## Recommended RapidKit Core modules
97
+
98
+ The following RapidKit Core modules are suggested during scaffolding; install them any time with `rapidkit add module <name>` after activating the project launcher:
99
+
100
+ {% for module in selected_modules %}- {{ module.name | replace('_', ' ') | title }} (`{{ module.name }}`{% if not module.required %}, optional{% endif %}) – tier: {{ module.tier }}
101
+ {% endfor %}
102
+ {% endif %}
103
+
104
+ ## Adding features
105
+
106
+ Use `rapidkit add module <module-name>` to install optional capabilities. Modules inject imports, routes, and services through the anchors defined in `src/main.py` and `src/routing/__init__.py`.
107
+
108
+ During kit generation you can decide whether the RapidKit Core modules ship with the scaffold:
109
+
110
+ ```text
111
+ Install the RapidKit Core settings module? [Y/n]
112
+ Install the RapidKit Core logging module? [Y/n]
113
+ Install deployment module assets (Docker/CI)? [Y/n]
114
+ ```
115
+
116
+ ### Scaffold toggles vs RapidKit Core modules
117
+
118
+ - `enable_*` prompts (for Docker, CI, SQLite, etc.) control the starter assets generated by this kit.
119
+ - `install_*` prompts control which RapidKit Core modules are installed up front.
120
+ - You can always add modules later with `rapidkit add module <name>` if you skip them during scaffolding.
121
+
122
+ When you need deployment artefacts in an existing project, install the optional `deployment` module:
123
+
124
+ ```bash
125
+ rapidkit add module deployment
126
+ rapidkit modules lock --overwrite
127
+ ```
128
+
129
+ <!-- <<<inject:module-snippet>>> -->
130
+
131
+ ## 📄 License
132
+
133
+ This project is licensed under the **{{ license }}** License - see the `LICENSE` file included at the project root for details.
134
+
135
+ ## 🔒 Security & secrets
136
+
137
+ - Copy `.env.example` to `.env` and populate secrets (`SECRET_KEY`, DB credentials, etc.) before deploying.
138
+ - **Do not** commit real secrets or `.env` to git; `.gitignore` already contains `.env` to protect accidental commits.
139
+ - For production, tighten CORS and allowed hosts rather than using wildcard settings provided for convenience in dev.
140
+ - Run `make audit` (pip-audit) before releases to catch dependency CVEs early.
@@ -0,0 +1,10 @@
1
+ # Workspai environment defaults
2
+ PROJECT_NAME="{{ project_name | default('workspai-app') }}"
3
+ ENV="development"
4
+ DEBUG="true"
5
+ APP_HOST="0.0.0.0"
6
+ APP_PORT="8000"
7
+ LOG_LEVEL="info"
8
+
9
+ # Module-specific environment variables
10
+ # <<<inject:module-env>>>
@@ -0,0 +1 @@
1
+ {% include 'common/env.example.j2' %}
@@ -0,0 +1,64 @@
1
+ [tool.poetry]
2
+ name = "{{ project_name }}"
3
+ version = "{{ app_version }}"
4
+ description = "{{ description }}"
5
+ authors = ["{{ author }} <you@example.com>"]
6
+ license = "{{ license }}"
7
+ packages = [{ include = "src" }]
8
+
9
+ [tool.poetry.scripts]
10
+ dev = "src.cli:dev"
11
+ start = "src.cli:start"
12
+ build = "src.cli:build"
13
+ test = "src.cli:test"
14
+ lint = "src.cli:lint"
15
+ format = "src.cli:format"
16
+ help = "src.cli:help_cmd"
17
+
18
+ [tool.poetry.dependencies]
19
+ python = "^{{ python_version }}"
20
+ fastapi = "^0.128.0"
21
+ uvicorn = { extras = ["standard"], version = "^0.40.0" }
22
+ pydantic = "^2.12.5"
23
+ {% if has_settings -%}
24
+ pydantic-settings = { version = "^2.12.0", extras = ["yaml"] }
25
+ {% endif %}
26
+ # <<<inject:poetry-dependencies>>>
27
+
28
+ [tool.poetry.group.dev.dependencies]
29
+ pytest = "^9.0.2"
30
+ pytest-asyncio = "^1.3.0"
31
+ pytest-cov = "^7.0.0"
32
+ httpx = "^0.28.1"
33
+ ruff = "^0.14.10"
34
+ black = "^25.12.0"
35
+ isort = "^7.0.0"
36
+ mypy = "^1.19.1"
37
+ build = "^1.3.0"
38
+
39
+ # <<<inject:poetry-dev-dependencies>>>
40
+
41
+ [build-system]
42
+ requires = ["poetry-core"]
43
+ build-backend = "poetry.core.masonry.api"
44
+
45
+ [tool.pytest.ini_options]
46
+ markers = [
47
+ "integration: mark tests as integration tests",
48
+ "template_integration: mark template integration tests",
49
+ ]
50
+
51
+ [tool.ruff]
52
+ line-length = 100
53
+ exclude = [".workspai/vendor", ".rapidkit/vendor"]
54
+
55
+ [tool.ruff.lint]
56
+ ignore = ["E501"]
57
+
58
+ [tool.mypy]
59
+ python_version = "{{ python_version }}"
60
+ ignore_missing_imports = true
61
+ warn_unused_configs = false
62
+ show_error_codes = true
63
+ # Ignore vendor snapshots and generated health shims from strict static checking
64
+ exclude = "(^\\.workspai/vendor/|^\\.rapidkit/vendor/|src/health/|src/main.py$)"
@@ -0,0 +1,3 @@
1
+ """{{ project_name }} package."""
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env python3
2
+ """Project CLI wrapper for {{ project_name }}.
3
+
4
+ This file prefers a per-project CLI (generated at `.workspai/cli.py`) when
5
+ available. If not present it prints helpful guidance directing users to
6
+ `workspai init` and does not attempt to make assumptions about a packaging
7
+ environment.
8
+ """
9
+
10
+ import importlib.util
11
+ import sys
12
+ import argparse
13
+ from pathlib import Path
14
+ from typing import Optional
15
+
16
+
17
+ def _missing_cli(name: str) -> None:
18
+ print(f"❌ Missing project-local CLI (.workspai/cli.py) - can't run '{name}'")
19
+ print("💡 If you're a new user run: workspai init # create venv + install deps")
20
+ sys.exit(127)
21
+
22
+
23
+ def _load_project_cli_module() -> Optional[object]:
24
+ project_root = Path(__file__).resolve().parents[1]
25
+ cli_file = project_root / ".workspai" / "cli.py"
26
+ if not cli_file.exists():
27
+ legacy_cli_file = project_root / ".rapidkit" / "cli.py"
28
+ if not legacy_cli_file.exists():
29
+ return None
30
+ cli_file = legacy_cli_file
31
+ spec = importlib.util.spec_from_file_location("project_cli", str(cli_file))
32
+ if spec is None or spec.loader is None:
33
+ return None
34
+ module = importlib.util.module_from_spec(spec)
35
+ spec.loader.exec_module(module) # type: ignore
36
+ return module
37
+
38
+
39
+ _mod = _load_project_cli_module()
40
+
41
+
42
+ # Default placeholders -- call `_missing_cli` if the project-local CLI isn't
43
+ # available. These are real functions so linters don't complain.
44
+ def init() -> None:
45
+ _missing_cli("init")
46
+
47
+
48
+ def dev() -> None:
49
+ _missing_cli("dev")
50
+
51
+
52
+ def start() -> None:
53
+ _missing_cli("start")
54
+
55
+
56
+ def build() -> None:
57
+ _missing_cli("build")
58
+
59
+
60
+ def test() -> None:
61
+ _missing_cli("test")
62
+
63
+
64
+ def lint() -> None:
65
+ _missing_cli("lint")
66
+
67
+
68
+ def format() -> None: # type: ignore[misc]
69
+ _missing_cli("format")
70
+
71
+
72
+ def help_cmd() -> None:
73
+ print("Usage: poetry run <command> # (init|dev|start|build|test|lint|format)")
74
+
75
+
76
+ if _mod is not None:
77
+ # prefer project-provided implementations when available
78
+ init = getattr(_mod, "init", init)
79
+ dev = getattr(_mod, "dev", dev)
80
+ start = getattr(_mod, "start", start)
81
+ build = getattr(_mod, "build", build)
82
+ test = getattr(_mod, "test", test)
83
+ lint = getattr(_mod, "lint", lint)
84
+ format = getattr(_mod, "format", format)
85
+ help_cmd = getattr(_mod, "help_cmd", help_cmd)
86
+
87
+
88
+ _dev_impl = dev
89
+ _start_impl = start
90
+
91
+
92
+ def _runtime_kwargs_from_argv(argv: list[str]) -> dict:
93
+ parser = argparse.ArgumentParser(add_help=False)
94
+ parser.add_argument("-p", "--port", type=int, dest="port")
95
+ parser.add_argument("--host", dest="host")
96
+ parser.add_argument("--allow-global-runtime", action="store_true", dest="allow_global_runtime")
97
+ ns, _ = parser.parse_known_args(argv)
98
+
99
+ kwargs = {}
100
+ if ns.port is not None:
101
+ kwargs["port"] = ns.port
102
+ if ns.host is not None:
103
+ kwargs["host"] = ns.host
104
+ if ns.allow_global_runtime:
105
+ kwargs["allow_global_runtime"] = True
106
+ return kwargs
107
+
108
+
109
+ def dev() -> None: # type: ignore[no-redef]
110
+ kwargs = _runtime_kwargs_from_argv(sys.argv[1:])
111
+ try:
112
+ _dev_impl(**kwargs)
113
+ except TypeError:
114
+ _dev_impl()
115
+
116
+
117
+ def start() -> None: # type: ignore[no-redef]
118
+ kwargs = _runtime_kwargs_from_argv(sys.argv[1:])
119
+ try:
120
+ _start_impl(**kwargs)
121
+ except TypeError:
122
+ _start_impl()
123
+
124
+
125
+ def main() -> None:
126
+ if len(sys.argv) < 2:
127
+ help_cmd()
128
+ return
129
+
130
+ command = sys.argv[1]
131
+ commands = {
132
+ "init": init,
133
+ "dev": dev,
134
+ "start": start,
135
+ "build": build,
136
+ "test": test,
137
+ "lint": lint,
138
+ "format": format,
139
+ "help": help_cmd,
140
+ }
141
+
142
+ if command in commands:
143
+ if command in ("dev", "start"):
144
+ parser = argparse.ArgumentParser(prog=f"poetry run {command}", add_help=False)
145
+ parser.add_argument("-p", "--port", type=int, dest="port")
146
+ parser.add_argument("--host", dest="host")
147
+ parser.add_argument("--allow-global-runtime", action="store_true", dest="allow_global_runtime")
148
+ ns, _ = parser.parse_known_args(sys.argv[2:])
149
+
150
+ kwargs = {}
151
+ if ns.port is not None:
152
+ kwargs["port"] = ns.port
153
+ if ns.host is not None:
154
+ kwargs["host"] = ns.host
155
+ if ns.allow_global_runtime:
156
+ kwargs["allow_global_runtime"] = True
157
+
158
+ try:
159
+ commands[command](**kwargs)
160
+ except TypeError:
161
+ commands[command]()
162
+ else:
163
+ commands[command]()
164
+ else:
165
+ print(f"Unknown command: {command}")
166
+ print("Run 'poetry run help' to see available commands.")
167
+ sys.exit(1)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()
@@ -0,0 +1,66 @@
1
+ """{{ project_name }} application entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+
8
+ from fastapi import FastAPI
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+
11
+ # <<<inject:imports>>>
12
+
13
+ try:
14
+ # canonical health package
15
+ from src.health import register_health_routes as _core_register_health_routes
16
+ except ImportError: # pragma: no cover - health package not generated yet
17
+
18
+ def _register_health_routes(_: FastAPI) -> None:
19
+ return None
20
+
21
+ else:
22
+
23
+ def _register_health_routes(app: FastAPI) -> None:
24
+ try:
25
+ _core_register_health_routes(app)
26
+ except Exception: # pragma: no cover - defensive best-effort registration
27
+ return None
28
+
29
+
30
+ from .routing import api_router
31
+
32
+
33
+ @asynccontextmanager
34
+ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
35
+ """Coordinate startup/shutdown hooks contributed by RapidKit Core modules."""
36
+
37
+ _ = app # ensure the app reference stays available for injected hooks
38
+ # <<<inject:startup>>>
39
+ try:
40
+ yield
41
+ finally:
42
+ # <<<inject:shutdown>>>
43
+ pass
44
+
45
+
46
+ app = FastAPI(
47
+ title="{{ project_name }}",
48
+ description="{{ description }}",
49
+ version="{{ app_version }}",
50
+ docs_url="/docs",
51
+ redoc_url="/redoc",
52
+ lifespan=lifespan,
53
+ )
54
+
55
+ app.add_middleware(
56
+ CORSMiddleware,
57
+ allow_origins=["*"],
58
+ allow_credentials=True,
59
+ allow_methods=["*"],
60
+ allow_headers=["*"],
61
+ )
62
+
63
+ app.include_router(api_router, prefix="/api")
64
+ # Expose module-provided health routers under /api/health/module/*
65
+ _register_health_routes(app)
66
+ # <<<inject:routes>>>
@@ -0,0 +1,3 @@
1
+ """Module bootstrap namespace."""
2
+
3
+ # <<<inject:module-init>>>
@@ -0,0 +1,16 @@
1
+ """API router assembly."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi import APIRouter
6
+
7
+ from .health import router as health_router
8
+ from .examples import router as examples_router
9
+
10
+ # <<<inject:router-imports>>>
11
+
12
+ api_router = APIRouter()
13
+
14
+ api_router.include_router(health_router, prefix="/health", tags=["health"])
15
+ api_router.include_router(examples_router, prefix="/examples", tags=["examples"])
16
+ # <<<inject:router-mount>>>
@@ -0,0 +1,71 @@
1
+ """Sample in-memory notes endpoints to demonstrate Workspai routing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict, List
6
+
7
+ from fastapi import APIRouter, HTTPException, status
8
+ from pydantic import BaseModel, Field
9
+
10
+ router = APIRouter(tags=["examples"])
11
+
12
+
13
+ class NoteCreate(BaseModel):
14
+ """Payload for creating a note."""
15
+
16
+ title: str = Field(..., max_length=80)
17
+ body: str = Field(..., max_length=500)
18
+
19
+
20
+ class Note(NoteCreate):
21
+ """Serialized note returned by the API."""
22
+
23
+ id: int
24
+
25
+
26
+ _STORE: Dict[int, Note] = {}
27
+ _NEXT_ID = 1
28
+
29
+
30
+ @router.post(
31
+ "/notes",
32
+ response_model=Note,
33
+ status_code=status.HTTP_201_CREATED,
34
+ summary="Create a note",
35
+ )
36
+ def create_note(payload: NoteCreate) -> Note:
37
+ """Persist a note in memory and return it."""
38
+
39
+ global _NEXT_ID # noqa: PLW0603 - intentional in-memory auto increment
40
+ note = Note(id=_NEXT_ID, **payload.model_dump())
41
+ _STORE[_NEXT_ID] = note
42
+ _NEXT_ID += 1
43
+ return note
44
+
45
+
46
+ @router.get(
47
+ "/notes",
48
+ response_model=List[Note],
49
+ summary="List all notes",
50
+ )
51
+ def list_notes() -> List[Note]:
52
+ """Return all notes stored so far."""
53
+
54
+ return list(_STORE.values())
55
+
56
+
57
+ @router.get(
58
+ "/notes/{note_id}",
59
+ response_model=Note,
60
+ summary="Retrieve a single note",
61
+ )
62
+ def get_note(note_id: int) -> Note:
63
+ """Retrieve a note by identifier or raise 404."""
64
+
65
+ note = _STORE.get(note_id)
66
+ if not note:
67
+ raise HTTPException(
68
+ status_code=status.HTTP_404_NOT_FOUND,
69
+ detail="Note not found",
70
+ )
71
+ return note
@@ -0,0 +1,22 @@
1
+ """Health endpoints."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from time import monotonic
6
+
7
+ from fastapi import APIRouter
8
+
9
+ router = APIRouter(tags=["health"])
10
+
11
+ _START_TIME = monotonic()
12
+
13
+
14
+ @router.get("/", summary="Health check")
15
+ async def heartbeat() -> dict[str, object]:
16
+ """Return basic service heartbeat."""
17
+ return {
18
+ "status": "ok",
19
+ "version": "{{ app_version }}",
20
+ "uptime": monotonic() - _START_TIME,
21
+ "module": "app",
22
+ }
@@ -0,0 +1 @@
1
+ """Test package placeholder."""
@@ -0,0 +1,29 @@
1
+ """Example feature tests exercising the in-memory notes API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from fastapi.testclient import TestClient
6
+
7
+ from src.main import app
8
+
9
+ client = TestClient(app)
10
+
11
+
12
+ def test_create_and_list_notes() -> None:
13
+ payload = {"title": "First note", "body": "Documented via Workspai."}
14
+ create_response = client.post("/api/examples/notes", json=payload)
15
+ assert create_response.status_code == 201
16
+ data = create_response.json()
17
+ assert data["title"] == payload["title"]
18
+ assert data["body"] == payload["body"]
19
+
20
+ list_response = client.get("/api/examples/notes")
21
+ assert list_response.status_code == 200
22
+ notes = list_response.json()
23
+ assert any(note["title"] == payload["title"] for note in notes)
24
+
25
+
26
+ def test_get_missing_note_returns_404() -> None:
27
+ response = client.get("/api/examples/notes/999")
28
+ assert response.status_code == 404
29
+ assert response.json()["detail"] == "Note not found"
@@ -0,0 +1,17 @@
1
+ """Health-check tests for generated FastAPI standard projects."""
2
+
3
+ from fastapi.testclient import TestClient
4
+
5
+ from src.main import app
6
+
7
+ client = TestClient(app)
8
+
9
+
10
+ def test_health_endpoint() -> None:
11
+ response = client.get("/api/health/")
12
+ assert response.status_code == 200
13
+ payload = response.json()
14
+ assert payload["status"] == "ok"
15
+ assert "version" in payload
16
+ assert "uptime" in payload
17
+ assert "module" in payload
@@ -0,0 +1,41 @@
1
+ # syntax=docker/dockerfile:1.6
2
+ ARG NODE_VERSION=20.19.0-alpine
3
+ FROM node:${NODE_VERSION} as builder
4
+
5
+ WORKDIR /usr/src/app
6
+
7
+ COPY package*.json ./
8
+ COPY . .
9
+
10
+ RUN set -eux; \
11
+ if [ "{{ package_manager }}" = "pnpm" ]; then \
12
+ corepack enable pnpm && pnpm install --frozen-lockfile; \
13
+ pnpm build; \
14
+ elif [ "{{ package_manager }}" = "yarn" ]; then \
15
+ corepack enable yarn && yarn install --frozen-lockfile; \
16
+ yarn build; \
17
+ else \
18
+ if [ -f package-lock.json ]; then npm ci; else npm install; fi; \
19
+ npm run build; \
20
+ fi
21
+
22
+ FROM node:${NODE_VERSION}
23
+ ENV NODE_ENV=production
24
+ WORKDIR /usr/src/app
25
+
26
+ COPY package*.json ./
27
+
28
+ RUN set -eux; \
29
+ if [ "{{ package_manager }}" = "pnpm" ]; then \
30
+ corepack enable pnpm && pnpm install --prod --frozen-lockfile; \
31
+ elif [ "{{ package_manager }}" = "yarn" ]; then \
32
+ corepack enable yarn && yarn install --production --frozen-lockfile; \
33
+ else \
34
+ if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi; \
35
+ fi
36
+
37
+ COPY --from=builder /usr/src/app/dist ./dist
38
+
39
+
40
+ EXPOSE 8000
41
+ CMD ["node", "dist/main"]