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,179 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Workspai Project Generator
4
+ * This script is called by the Workspai CLI to generate projects
5
+ */
6
+
7
+ const fs = require('fs').promises;
8
+ const path = require('path');
9
+ const { execSync } = require('child_process');
10
+
11
+ const TEMPLATES_DIR = path.join(__dirname, 'templates');
12
+
13
+ async function generateProject(projectPath, template, useDefaults, skipGit, skipInstall) {
14
+ const projectName = path.basename(projectPath);
15
+ const templateDir = template === 'nestjs' ? 'nestjs-standard' : 'fastapi-standard';
16
+ const templatePath = path.join(TEMPLATES_DIR, templateDir);
17
+
18
+ // Check template exists
19
+ try {
20
+ await fs.access(templatePath);
21
+ } catch {
22
+ console.error('❌ Template not found:', templateDir);
23
+ process.exit(1);
24
+ }
25
+
26
+ // Create project directory
27
+ await fs.mkdir(projectPath, { recursive: true });
28
+
29
+ // Build context
30
+ const context = {
31
+ project_name: template === 'nestjs'
32
+ ? projectName.replace(/_/g, '-').toLowerCase()
33
+ : projectName.replace(/-/g, '_').toLowerCase(),
34
+ author: process.env.USER || 'Workspai User',
35
+ description: template === 'nestjs'
36
+ ? 'NestJS application generated with Workspai'
37
+ : 'FastAPI service generated with Workspai',
38
+ app_version: '0.1.0',
39
+ license: 'MIT',
40
+ package_manager: 'npm',
41
+ created_at: new Date().toISOString(),
42
+ rapidkit_version: require('./config.json').rapidkit_version || '0.12.0',
43
+ };
44
+
45
+ // Copy and render template files
46
+ await copyTemplateDir(templatePath, projectPath, context);
47
+
48
+ console.log('✅ Project files created!');
49
+
50
+ // Git initialization
51
+ if (!skipGit) {
52
+ try {
53
+ execSync('git init', { cwd: projectPath, stdio: 'pipe' });
54
+ execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
55
+ const commitMsg = 'Initial commit: ' + (template === 'nestjs' ? 'NestJS' : 'FastAPI') + ' project via Workspai';
56
+ execSync(`git commit -m "${commitMsg}"`, { cwd: projectPath, stdio: 'pipe' });
57
+ console.log('✅ Git repository initialized');
58
+ } catch (e) {
59
+ console.log('⚠️ Could not initialize git');
60
+ }
61
+ }
62
+
63
+ // Install dependencies
64
+ if (!skipInstall) {
65
+ if (template === 'nestjs') {
66
+ console.log('📦 Installing dependencies...');
67
+ try {
68
+ execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
69
+ console.log('✅ Dependencies installed');
70
+ } catch {
71
+ console.log('⚠️ Could not install dependencies. Run npm install manually.');
72
+ }
73
+ }
74
+ }
75
+
76
+ // Success message
77
+ const templateName = template === 'nestjs' ? 'NestJS' : 'FastAPI';
78
+ console.log('');
79
+ console.log('✨ ' + templateName + ' project created successfully!');
80
+ console.log('');
81
+ console.log('🚀 Get started:');
82
+ console.log(' cd ' + projectName);
83
+ if (template === 'fastapi') {
84
+ console.log(' workspai init # poetry install');
85
+ console.log(' workspai dev # Start dev server');
86
+ } else {
87
+ if (skipInstall) {
88
+ console.log(' workspai init # npm install');
89
+ }
90
+ console.log(' cp .env.example .env');
91
+ console.log(' workspai dev # Start dev server');
92
+ }
93
+ console.log('');
94
+ }
95
+
96
+ function renderTemplate(content, context) {
97
+ let result = content;
98
+
99
+ for (const [key, value] of Object.entries(context)) {
100
+ // Simple variable replacement: {{ key }}
101
+ const simpleRegex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g');
102
+ result = result.replace(simpleRegex, String(value));
103
+
104
+ // With replace filter: {{ key | replace('a', 'b') }}
105
+ const replaceRegex = new RegExp(
106
+ `\\{\\{\\s*${key}\\s*\\|\\s*replace\\s*\\(\\s*['"]([^'"]+)['"]\\s*,\\s*['"]([^'"]*)['"]\\s*\\)\\s*\\}\\}`,
107
+ 'g'
108
+ );
109
+ result = result.replace(replaceRegex, (match, from, to) => {
110
+ return String(value).replace(new RegExp(from, 'g'), to);
111
+ });
112
+
113
+ // With lower filter: {{ key | lower }}
114
+ const lowerRegex = new RegExp(`\\{\\{\\s*${key}\\s*\\|\\s*lower\\s*\\}\\}`, 'g');
115
+ result = result.replace(lowerRegex, String(value).toLowerCase());
116
+
117
+ // Combined: {{ key | replace('a', 'b') | lower }}
118
+ const combinedRegex = new RegExp(
119
+ `\\{\\{\\s*${key}\\s*\\|\\s*replace\\s*\\(\\s*['"]([^'"]+)['"]\\s*,\\s*['"]([^'"]*)['"]\\s*\\)\\s*\\|\\s*lower\\s*\\}\\}`,
120
+ 'g'
121
+ );
122
+ result = result.replace(combinedRegex, (match, from, to) => {
123
+ return String(value).replace(new RegExp(from, 'g'), to).toLowerCase();
124
+ });
125
+ }
126
+
127
+ return result;
128
+ }
129
+
130
+ async function copyTemplateDir(src, dest, context) {
131
+ const entries = await fs.readdir(src, { withFileTypes: true });
132
+
133
+ for (const entry of entries) {
134
+ const srcPath = path.join(src, entry.name);
135
+ const destName = entry.name.replace(/\.j2$/, '');
136
+ const destPath = path.join(dest, destName);
137
+
138
+ if (entry.isDirectory()) {
139
+ await fs.mkdir(destPath, { recursive: true });
140
+ await copyTemplateDir(srcPath, destPath, context);
141
+ } else {
142
+ let content = await fs.readFile(srcPath, 'utf-8');
143
+
144
+ // Render template if it's a .j2 file
145
+ if (entry.name.endsWith('.j2')) {
146
+ content = renderTemplate(content, context);
147
+ }
148
+
149
+ await fs.writeFile(destPath, content);
150
+
151
+ // Make scripts executable
152
+ if (
153
+ destName === 'rapidkit' ||
154
+ (destName.endsWith('.py') &&
155
+ (destPath.includes('.workspai') || destPath.includes('.rapidkit')))
156
+ ) {
157
+ await fs.chmod(destPath, 0o755);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ // Main
164
+ const args = process.argv.slice(2);
165
+ const projectPath = args[0];
166
+ const template = args[1] || 'fastapi';
167
+ const useDefaults = args.includes('--yes');
168
+ const skipGit = args.includes('--skip-git');
169
+ const skipInstall = args.includes('--skip-install');
170
+
171
+ if (!projectPath) {
172
+ console.error('Usage: node generator.js <project-path> <template> [--yes] [--skip-git] [--skip-install]');
173
+ process.exit(1);
174
+ }
175
+
176
+ generateProject(projectPath, template, useDefaults, skipGit, skipInstall).catch(err => {
177
+ console.error('Error:', err);
178
+ process.exit(1);
179
+ });
@@ -0,0 +1,117 @@
1
+ # {{ project_name }}
2
+
3
+ Domain-driven FastAPI service generated with Workspai. Python-backed module commands use [RapidKit Core](https://github.com/rapidkitlabs/rapidkit-core).
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ # Bootstrap dependencies (creates .venv + installs Poetry 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
+ # Type-check, lint, and test
15
+ make lint
16
+ make typecheck
17
+ make test
18
+
19
+ # Run the service
20
+ make dev
21
+
22
+ # Use the project-local RapidKit Core helpers when you prefer auto-detected environments
23
+ npx workspai dev
24
+ npx workspai lint
25
+ npx workspai test
26
+
27
+ # Run supply-chain and dependency audits
28
+ make audit
29
+ ```
30
+
31
+ > Re-run `npx workspai init` (or `make init`) when dependencies change. If you only need to refresh tooling/hooks, run `SKIP_INIT=1 make install` or rely on `./bootstrap.sh`.
32
+
33
+ > Lockfiles are generated automatically during scaffolding. Set `RAPIDKIT_SKIP_LOCKS=1` (or `RAPIDKIT_GENERATE_LOCKS=0`) before running `workspai create` if you want to skip the lockfile attempt.
34
+
35
+ > Need to see every project-local RapidKit Core command? Run `rapidkit --help` when RapidKit Core is installed, or use `npx workspai --help` for workspace-level commands.
36
+
37
+ ## Project structure
38
+
39
+ ```
40
+ src/
41
+ app/
42
+ application/ # Use cases and service contracts
43
+ domain/ # Aggregates, entities, and value objects
44
+ infrastructure/ # Adapters and repositories
45
+ presentation/ # API routers and dependencies
46
+ shared/ # Cross-cutting primitives (Result, types, etc.)
47
+ routing/ # Glue for module-provided routers
48
+ modules/ # RapidKit Core module bootstrapping
49
+ ```
50
+
51
+ ## Layer overview
52
+
53
+ - **Domain** – Pure business rules (`src/app/domain`).
54
+ - **Application** – Orchestrates use-cases and enforces boundaries (`src/app/application`).
55
+ - **Infrastructure** – Implements gateways and repositories (`src/app/infrastructure`).
56
+ - **Presentation** – Exposes the API using FastAPI (`src/app/presentation`).
57
+
58
+ Modules added via `rapidkit add module ...` integrate automatically using the preserved
59
+ injection anchors inside `src/main.py` and `src/routing/__init__.py`.
60
+
61
+ ## Example feature
62
+
63
+ An in-memory notes module is included under `/api/examples/notes` to demonstrate how the domain, application, infrastructure, and presentation layers collaborate:
64
+
65
+ ```bash
66
+ # Create a note
67
+ curl -s -X POST http://localhost:8000/api/examples/notes \
68
+ -H "Content-Type: application/json" \
69
+ -d '{"title":"design","body":"Describe your bounded context here."}'
70
+
71
+ # Retrieve the newly created note
72
+ curl -s http://localhost:8000/api/examples/notes/1 | jq
73
+ ```
74
+
75
+ Swap the in-memory repository with a RapidKit Core database module when you are ready for persistence.
76
+
77
+ ## Deployment
78
+
79
+ - `Dockerfile` packages the service for container hosting with production-ready defaults.
80
+ - `docker-compose.yml` provisions the FastAPI app plus dependencies (for example PostgreSQL) for local production parity.
81
+ - Run `docker compose up --build` to start the stack; copy `.env.example` to `.env` to configure credentials before deploying.
82
+ - CI/CD workflows under `.github/workflows/` demonstrate automated deployment pipelines.
83
+
84
+ ## Testing
85
+
86
+ ```bash
87
+ npx workspai test
88
+ ```
89
+
90
+ The scaffold ships with a smoke test (`tests/test_app_factory.py`) that validates the
91
+ health endpoint and the application factory.
92
+
93
+ ---
94
+
95
+ ## Local development
96
+
97
+ - `npx workspai init` bootstraps dependencies via the project-aware CLI.
98
+ - `make init` is an optional alias for `npx workspai init` when you prefer Make targets.
99
+ - `make install` re-runs `npx workspai init` unless `SKIP_INIT=1` is set and refreshes developer tooling such as pre-commit hooks.
100
+ - `./bootstrap.sh` copies `.env.example` to `.env` (when missing) and runs `SKIP_INIT=1 make install` for you.
101
+ - `make dev` or `npx workspai dev` starts the FastAPI server with hot-reload and ensures environment variables from `.env` are loaded.
102
+ - `make lint`, `make typecheck`, and `make test` (or `npx workspai lint` / `npx workspai test`) are thin wrappers around Ruff, mypy, and pytest for consistent CI parity.
103
+ - `make audit` runs `pip-audit` and `npm audit` to surface vulnerable dependencies early.
104
+
105
+ Pre-commit is configured with Ruff, Black, mypy, and hygiene hooks; `./bootstrap.sh` installs them automatically (or run `SKIP_INIT=1 make install`). Use the project-local launcher variants when you need auto-discovery of the right virtual environment inside automation scripts.
106
+
107
+ <!-- <<<inject:module-snippet>>> -->
108
+
109
+ ## 📄 License
110
+
111
+ This project is licensed under the **{{ license }}** License - see the `LICENSE` file included at the project root for details.
112
+
113
+ ## 🔒 Security & secrets
114
+
115
+ - Copy `.env.example` to `.env` and populate secrets (`SECRET_KEY`, DB credentials, etc.) before deploying.
116
+ - **Do not** commit real secrets or `.env` to git; `.gitignore` already contains `.env` to protect accidental commits.
117
+ - For production, tighten CORS and allowed hosts rather than using wildcard settings provided for convenience in dev.
@@ -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$|src/app/)"
@@ -0,0 +1,3 @@
1
+ """{{ project_name }} package."""
2
+
3
+ __all__ = ["app", "main"]
@@ -0,0 +1,11 @@
1
+ """Domain-driven application layers for {{ project_name }}."""
2
+
3
+ __all__ = [
4
+ "main",
5
+ "config",
6
+ "application",
7
+ "domain",
8
+ "infrastructure",
9
+ "presentation",
10
+ "shared",
11
+ ]
@@ -0,0 +1,5 @@
1
+ """Application layer orchestrating use cases and service contracts."""
2
+
3
+ from .interfaces import HealthStatusProvider, NoteRepository, ServiceContext
4
+
5
+ __all__ = ["HealthStatusProvider", "NoteRepository", "ServiceContext"]
@@ -0,0 +1,43 @@
1
+ """Contracts between the application layer and infrastructure adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Protocol
7
+
8
+ from src.app.domain.models import HealthStatus, Note, NoteDraft
9
+
10
+
11
+ class HealthStatusProvider(Protocol):
12
+ """Protocol describing how to obtain the current health status."""
13
+
14
+ def get_status(self) -> HealthStatus:
15
+ """Return the latest domain health status snapshot."""
16
+ ...
17
+
18
+
19
+ class NoteRepository(Protocol):
20
+ """Protocol for managing example notes."""
21
+
22
+ def create(self, draft: NoteDraft) -> Note:
23
+ """Persist a note draft and return the resulting entity."""
24
+ ...
25
+
26
+ def list(self) -> list[Note]:
27
+ """Return all available notes."""
28
+ ...
29
+
30
+ def get(self, note_id: int) -> Note | None:
31
+ """Return a single note or None if it does not exist."""
32
+ ...
33
+
34
+
35
+ @dataclass(slots=True)
36
+ class ServiceContext:
37
+ """Aggregates infrastructure adapters used by application use-cases."""
38
+
39
+ health_provider: HealthStatusProvider
40
+ note_repository: NoteRepository
41
+
42
+
43
+ __all__ = ["HealthStatusProvider", "NoteRepository", "ServiceContext"]
@@ -0,0 +1,6 @@
1
+ """Application use-cases exposed to delivery mechanisms."""
2
+
3
+ from .health import get_service_health
4
+ from .notes import create_note, get_note, list_notes
5
+
6
+ __all__ = ["get_service_health", "create_note", "get_note", "list_notes"]
@@ -0,0 +1,14 @@
1
+ """Example use-case exposing service health information."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from src.app.application.interfaces import ServiceContext
6
+ from src.app.domain.models import HealthStatus
7
+
8
+
9
+ def get_service_health(context: ServiceContext) -> dict[str, str]:
10
+ """Retrieve an immutable health snapshot for presentation layers."""
11
+ status = context.health_provider.get_status()
12
+ if isinstance(status, HealthStatus):
13
+ return status.to_dict()
14
+ return {"status": str(status)}
@@ -0,0 +1,24 @@
1
+ """Application use-cases orchestrating the example notes feature."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from src.app.application.interfaces import ServiceContext
6
+ from src.app.domain.models import Note, NoteDraft
7
+
8
+
9
+ def list_notes(context: ServiceContext) -> list[Note]:
10
+ """Return every note currently stored."""
11
+
12
+ return context.note_repository.list()
13
+
14
+
15
+ def create_note(context: ServiceContext, draft: NoteDraft) -> Note:
16
+ """Create a new note."""
17
+
18
+ return context.note_repository.create(draft)
19
+
20
+
21
+ def get_note(context: ServiceContext, note_id: int) -> Note | None:
22
+ """Retrieve a single note by identifier."""
23
+
24
+ return context.note_repository.get(note_id)
@@ -0,0 +1,16 @@
1
+ """Configuration primitives for the application layer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class AppConfig:
10
+ """Application-level configuration aggregated from RapidKit Core settings."""
11
+
12
+ environment: str = "development"
13
+ service_name: str = "{{ project_name }}"
14
+
15
+
16
+ __all__ = ["AppConfig"]
@@ -0,0 +1,3 @@
1
+ """Domain layer primitives for {{ project_name }}."""
2
+
3
+ __all__ = ["models"]
@@ -0,0 +1,6 @@
1
+ """Domain models and value objects."""
2
+
3
+ from .health import HealthStatus
4
+ from .note import Note, NoteDraft
5
+
6
+ __all__ = ["HealthStatus", "Note", "NoteDraft"]
@@ -0,0 +1,16 @@
1
+ """Domain entity representing system health."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class HealthStatus:
10
+ """Immutable representation of the service health state."""
11
+
12
+ status: str = "ok"
13
+
14
+ def to_dict(self) -> dict[str, str]:
15
+ """Serialize the status for transport layers."""
16
+ return {"status": self.status}
@@ -0,0 +1,27 @@
1
+ """Domain entity and value object for example notes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class NoteDraft:
10
+ """Value object describing the data needed to create a note."""
11
+
12
+ title: str
13
+ body: str
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class Note:
18
+ """Domain entity representing an immutable note."""
19
+
20
+ id: int
21
+ title: str
22
+ body: str
23
+
24
+ def to_dict(self) -> dict[str, str | int]:
25
+ """Serialize the note for presentation layers."""
26
+
27
+ return {"id": self.id, "title": self.title, "body": self.body}
@@ -0,0 +1,5 @@
1
+ """Infrastructure adapters for the application layer."""
2
+
3
+ from .repositories import StaticHealthRepository
4
+
5
+ __all__ = ["StaticHealthRepository"]
@@ -0,0 +1,6 @@
1
+ """Repository implementations bridging the domain and infrastructure."""
2
+
3
+ from .health import StaticHealthRepository
4
+ from .notes import InMemoryNoteRepository
5
+
6
+ __all__ = ["StaticHealthRepository", "InMemoryNoteRepository"]
@@ -0,0 +1,17 @@
1
+ """Infrastructure repository providing health status data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from src.app.domain.models import HealthStatus
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class StaticHealthRepository:
12
+ """Simple repository returning a constant health status."""
13
+
14
+ status: str = "ok"
15
+
16
+ def get_status(self) -> HealthStatus:
17
+ return HealthStatus(status=self.status)
@@ -0,0 +1,28 @@
1
+ """In-memory repository backing the example notes use-case."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Dict, List
7
+
8
+ from src.app.domain.models import Note, NoteDraft
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class InMemoryNoteRepository:
13
+ """Very small repository storing notes in process memory."""
14
+
15
+ _store: Dict[int, Note] = field(default_factory=dict)
16
+ _next_id: int = 1
17
+
18
+ def create(self, draft: NoteDraft) -> Note:
19
+ note = Note(id=self._next_id, title=draft.title, body=draft.body)
20
+ self._store[self._next_id] = note
21
+ self._next_id += 1
22
+ return note
23
+
24
+ def list(self) -> List[Note]:
25
+ return list(self._store.values())
26
+
27
+ def get(self, note_id: int) -> Note | None:
28
+ return self._store.get(note_id)