@rashidee/co2 1.2.5

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 (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,468 @@
1
+ ---
2
+ name: depgen-k8s
3
+ model: claude-sonnet-4-6
4
+ effort: high
5
+ description: >
6
+ Generate a Dockerfile and Kubernetes manifests for an application targeting a single
7
+ environment. Supports Spring Boot (Java), Laravel (PHP), and Node.js application stacks.
8
+ Auto-detects the stack from project files (pom.xml, composer.json, package.json), reads
9
+ CLAUDE.md dependencies, SPECIFICATION.md tech stack, and the application's externalized
10
+ environment variables. Generates a Dockerfile in the application root folder and Kubernetes
11
+ manifest YAML files directly in `<app_folder>/k8s/` (no per-environment subfolders — the
12
+ k8s/ folder is gitignored, each machine maintains its own copy).
13
+ Standardized input: application name (mandatory), environment (optional).
14
+ Use this skill whenever the user asks to create deployment artifacts, Dockerfiles,
15
+ Kubernetes manifests, or containerize an application. Also trigger when the user says
16
+ things like "deploy this app", "containerize this", "create a Dockerfile",
17
+ "generate k8s manifests", or any request for deployment-related artifacts.
18
+ ---
19
+
20
+ # Deployment Artifact Generator — Kubernetes
21
+
22
+ This skill generates deployment artifacts for a custom application:
23
+
24
+ 1. **Dockerfile** — A production-ready, multi-stage container build file placed in the
25
+ application root folder.
26
+ 2. **Kubernetes manifests** — Manifest files stored directly in the application's own
27
+ `k8s/` folder for a **single target environment**. Since the `k8s/` folder is gitignored,
28
+ each machine maintains its own independent copy of the manifests with environment-specific
29
+ ConfigMap/Secret values (hostnames, credentials, resource limits).
30
+
31
+ **Note:** Only custom applications (from `# Custom Applications` in CLAUDE.md) are processed.
32
+ 3rd party supporting applications and external services are NOT containerized by this skill.
33
+
34
+ ## Inputs
35
+
36
+ ```
37
+ /depgen-k8s <application> [environment]
38
+ ```
39
+
40
+ | Argument | Required | Example | Description |
41
+ |----------|----------|---------|-------------|
42
+ | `<application>` | Yes | `hub_middleware` | Application name — must be a custom application from CLAUDE.md |
43
+ | `<environment>` | No | `home_server` | Target environment name — must match a Kubernetes environment in CLAUDE.md |
44
+
45
+ If `<environment>` is omitted:
46
+ - If CLAUDE.md defines exactly **one** Kubernetes environment → auto-select it.
47
+ - If CLAUDE.md defines **multiple** Kubernetes environments → list them and ask the user to specify.
48
+
49
+ The environment name is matched **case-insensitively** against the environment headings in CLAUDE.md, accepting snake_case, kebab-case, or title-case input (e.g., `home_server`, `home-server`, `Home Server`).
50
+
51
+ No `version:` or `module:` arguments — deployment is application-level.
52
+
53
+ ### Input Resolution
54
+
55
+ The application name is matched against root-level application folders:
56
+ 1. Strip any leading `<number>_` prefix from folder names (e.g., `1_hub_middleware` → `hub_middleware`)
57
+ 2. Match case-insensitively against the provided application name
58
+ 3. Accept snake_case, kebab-case, or title-case input
59
+ 4. Verify the application is listed under `# Custom Applications` in CLAUDE.md — reject if it is a 3rd party application or external service
60
+ 5. If no match found, list available custom applications and stop
61
+
62
+ ### Auto-Resolved Paths
63
+
64
+ | File | Resolved Path |
65
+ |------|---------------|
66
+ | CLAUDE.md | Project root `CLAUDE.md` |
67
+ | ENVIRONMENT.md | Project root `ENVIRONMENT.md` |
68
+ | SPECIFICATION.md | `<app_folder>/context/specification/SPECIFICATION.md` |
69
+ | Source code | `<app_folder>/` (pom.xml, composer.json, package.json, etc.) |
70
+ | Application config | Stack-dependent (see detection) |
71
+ | K8s manifests output | `<app_folder>/k8s/` (gitignored — each machine maintains its own copy) |
72
+
73
+ ## PRD.md Extended Sections
74
+
75
+ Before generating deployment artifacts, check PRD.md for the following extended sections:
76
+
77
+ ### Architecture Principle
78
+
79
+ If PRD.md contains an `# Architecture Principle` section, extract patterns that affect deployment:
80
+
81
+ | Pattern | Deployment Impact |
82
+ |---|---|
83
+ | "Stateless" | Deployment uses `RollingUpdate` strategy without sticky sessions; no PVC needed for session storage |
84
+ | "Container based deployment" | Validates this skill's applicability |
85
+ | "Scale out" / "horizontally scalable" | Generate HorizontalPodAutoscaler (HPA) manifest targeting CPU 70% |
86
+ | Specific resource constraints (e.g., "max 256Mi per pod") | Use as default resource limits in K8s manifests |
87
+ | "Message driven" / "message queue" | Readiness probe should verify message queue connectivity |
88
+
89
+ If absent, use defaults from SPECIFICATION.md and CLAUDE.md (existing behavior).
90
+
91
+ ### High Level Process Flow
92
+
93
+ If PRD.md contains a `# High Level Process Flow` section:
94
+ - Process flows reveal inter-system dependencies (message queues, external service endpoints)
95
+ - Validate that the environment variable list and ConfigMap/Secret values include all dependencies mentioned in process flows
96
+ - If a flow references an external system not in the `Depends on` list, log a warning
97
+
98
+ If absent, derive dependencies from CLAUDE.md only (existing behavior).
99
+
100
+ ---
101
+
102
+ ## Pre-Requisites
103
+
104
+ Before running this skill, the following must exist:
105
+ - **Source code** — The application must be implemented (not just context artifacts)
106
+ - **SPECIFICATION.md** — The technical specification must exist (generated by a `specgen-*` skill)
107
+ - **Externalized configuration** — The application config must use environment variables
108
+ (e.g., `${ENV_VAR:default}` in Spring Boot, `env('VAR')` in Laravel, `process.env.VAR` in Node.js)
109
+
110
+ If any are missing, stop and inform the user.
111
+
112
+ ## Workflow
113
+
114
+ ### Phase 0: Validate Inputs
115
+
116
+ 1. Resolve application folder from the provided name
117
+ 2. Verify the application is a custom application in CLAUDE.md (not 3rd party or external service)
118
+ 3. Verify source code exists (at least one of: `pom.xml`, `composer.json`, `package.json`)
119
+ 4. Verify `<app_folder>/context/specification/SPECIFICATION.md` exists
120
+ 5. Read `CLAUDE.md` (already in context) for project-level information
121
+ 6. **Detect environments** from CLAUDE.md's `# Environment` section:
122
+ - Each `## <Environment Name>` heading under `# Environment` is an environment
123
+ - For each environment, extract:
124
+ - Environment name (e.g., "Home Server")
125
+ - `Domain` field (e.g., `localhost`, `home.server`)
126
+ - `Deployment Type` field (e.g., `Manual`, `Kubernetes`)
127
+ - `IP` field (from SSH Configuration or direct IP field) if present — needed for `hostAliases` generation in Phase 3
128
+ - If no environments are defined, stop with error: "No environments found in CLAUDE.md `# Environment` section."
129
+
130
+ 7. **Select target environment** — filter to Kubernetes environments and select one:
131
+ 1. Filter environments to those with `Deployment Type: Kubernetes`.
132
+ 2. If no environment has `Deployment Type: Kubernetes`, stop with error: "No Kubernetes environments found in CLAUDE.md. This skill only generates K8s manifests for environments with Deployment Type: Kubernetes."
133
+ 3. If the user provided an `<environment>` argument, match it against the Kubernetes environments (case-insensitively, accepting snake_case, kebab-case, or title-case).
134
+ - If matched → use it as the target environment.
135
+ - If not matched → list available Kubernetes environments and stop.
136
+ 4. If no argument was provided:
137
+ - If exactly **one** Kubernetes environment exists → auto-select it.
138
+ - If **multiple** exist → list them and ask the user to specify.
139
+ 5. Record the selected environment's Domain and IP for Phase 3.
140
+
141
+ ### Phase 1: Detect Application Stack
142
+
143
+ #### Step 1 — Identify Stack
144
+
145
+ Check the application root for build files:
146
+
147
+ | File Found | Stack | Build Tool |
148
+ |---|---|---|
149
+ | `pom.xml` | `spring-boot` | Maven |
150
+ | `composer.json` | `laravel` | Composer |
151
+ | `package.json` (no `pom.xml` or `composer.json`) | `nodejs` | npm / pnpm |
152
+
153
+ If none found, stop with error: "Cannot determine application stack."
154
+
155
+ If multiple found (e.g., `pom.xml` + `package.json`), prioritize: `pom.xml` > `composer.json` > `package.json`
156
+ (the `package.json` is likely for frontend or E2E tests, not the main app).
157
+
158
+ #### Step 2 — Extract Stack-Specific Metadata
159
+
160
+ **If `spring-boot`:**
161
+ - Read `pom.xml`:
162
+ - `<java.version>` → JDK version for base image
163
+ - `<artifactId>` → image name
164
+ - `<version>` → image tag
165
+ - `frontend-maven-plugin` presence → has frontend build step
166
+ - `jte-maven-plugin` presence → has JTE precompilation
167
+ - `spring-boot-maven-plugin` → executable JAR packaging
168
+ - Read `src/main/resources/application.yml`:
169
+ - Extract all `${ENV_VAR:default}` patterns via regex `\$\{([^:}]+)(?::([^}]*))?\}`
170
+ - `server.port` default → exposed container port
171
+ - Read `.env` (if exists) → local dev values for reference
172
+
173
+ **If `laravel`:**
174
+ - Read `composer.json`:
175
+ - `require.php` → PHP version for base image
176
+ - `require.laravel/framework` → Laravel version
177
+ - `name` → image name
178
+ - Read `.env.example` or scan `config/*.php`:
179
+ - Extract all `env('VAR', 'default')` patterns via regex `env\('([^']+)'(?:,\s*'?([^')*]*)'?)?\)`
180
+ - `APP_PORT` or default `8000` → exposed container port
181
+ - Check for `vite.config.js` or `webpack.mix.js` → has frontend build step
182
+ - Check for `package.json` in app root → Node.js needed for frontend build
183
+
184
+ **If `nodejs`:**
185
+ - Read `package.json`:
186
+ - `engines.node` → Node.js version for base image
187
+ - `name` → image name
188
+ - `version` → image tag
189
+ - `scripts.build` → build command (e.g., `tsc`, `tsup`, `vite build`, `next build`)
190
+ - `scripts.start` → start command
191
+ - Scan source files or `.env` for `process.env.VAR` patterns
192
+ - Check for `tsconfig.json` → TypeScript build step
193
+ - Determine framework: Express, Fastify, NestJS, Next.js, etc.
194
+
195
+ #### Step 3 — Common Detection (All Stacks)
196
+
197
+ From `CLAUDE.md` → extract the "Depends on" list for the target application:
198
+
199
+ | Dependency Pattern | External Service |
200
+ |---|---|
201
+ | References MongoDB | MongoDB required |
202
+ | References MySQL / HC Database / SC Database | MySQL required |
203
+ | References PostgreSQL | PostgreSQL required |
204
+ | References Hub Single Sign On / Keycloak | Keycloak required |
205
+ | References RabbitMQ / Message Queue | RabbitMQ required |
206
+ | References Redis / Hub Cache | Redis required |
207
+ | References SMTP / Mailcatcher | SMTP service required |
208
+ | References Meilisearch / Hub Search Engine | Meilisearch required |
209
+
210
+ From `SPECIFICATION.md` → extract:
211
+ - Technology stack table (versions)
212
+ - Environment variable reference table (if present)
213
+
214
+ #### Step 4 — Classify Environment Variables
215
+
216
+ For each detected environment variable, classify as ConfigMap or Secret:
217
+
218
+ **Secret** (sensitive — stored in K8s Secret):
219
+ - Variable name contains `PASSWORD`, `SECRET`, `KEY`, `TOKEN`, `CREDENTIAL`
220
+ - Variable name contains `ADMIN_USERNAME` (admin-level credentials)
221
+ - Variable name is a database connection URI containing credentials
222
+
223
+ **ConfigMap** (non-sensitive — stored in K8s ConfigMap):
224
+ - Everything else: hostnames, ports, log levels, feature flags, queue names, CORS origins
225
+
226
+ #### Step 5 — Determine Health Check
227
+
228
+ | Stack | Default Health Endpoint | Condition |
229
+ |---|---|---|
230
+ | Spring Boot | `/actuator/health` | If `spring-boot-starter-actuator` in pom.xml |
231
+ | Spring Boot | `/` (HTTP 200/302 check) | If no actuator |
232
+ | Laravel | `/health` or `/` | Custom route or root |
233
+ | Node.js | `/health` or `/` | Custom route or root |
234
+
235
+ Check if a health endpoint is explicitly defined in the source code. If not, use the
236
+ default for the stack.
237
+
238
+ #### Step 6 — Present Detection Summary
239
+
240
+ Before generating, present findings to the user for confirmation:
241
+
242
+ ```
243
+ Application Stack Detection:
244
+ - Stack: Spring Boot 3.5.7 (Java 21)
245
+ - Artifact: hub-middleware:1.0.3
246
+ - Frontend Build: Yes (Vite via frontend-maven-plugin)
247
+ - JTE Precompile: Yes (jte-maven-plugin)
248
+ - Exposed Port: 8080
249
+ - Health Check: /actuator/health (actuator present)
250
+
251
+ External Services:
252
+ - MongoDB 7.0.6 (Hub Core Database)
253
+ - Keycloak 26.5.3 (Hub Single Sign On)
254
+ - RabbitMQ 3.11.9 (HC + SC Adapter Message Queues)
255
+ - Mailcatcher 0.8.1 (SMTP)
256
+
257
+ Environment Variables: 25 detected
258
+ - ConfigMap: 18 (hostnames, ports, log levels, queue names, flags)
259
+ - Secret: 7 (passwords, admin credentials)
260
+ ```
261
+
262
+ If the user disagrees, allow overrides before proceeding.
263
+
264
+ ### Phase 2: Generate or Update Dockerfile
265
+
266
+ 1. Check if `<app_folder>/Dockerfile` already exists
267
+ 2. **If it exists** (UPDATE mode):
268
+ - Read the existing Dockerfile
269
+ - Compare detected values against what the Dockerfile currently has:
270
+ - Base image version (e.g., Java version changed in pom.xml)
271
+ - Exposed port (e.g., server.port default changed)
272
+ - Environment variable defaults (e.g., new env vars added)
273
+ - Build commands (e.g., frontend build step added or removed)
274
+ - Preserve any lines marked with `# CUSTOM:` comments — these are manual overrides
275
+ by the user/DevOps that should not be replaced
276
+ - Update only the parts that changed, keeping the overall structure intact
277
+ - Log what was changed (e.g., "Updated Java version from 17 to 21",
278
+ "Added MAIL_HOST env var")
279
+ 3. **If it does not exist** (CREATE mode):
280
+ - Select the Dockerfile pattern from `references/dockerfile-<stack>.md`
281
+ - Substitute detected values (Java version, artifact name, port, etc.)
282
+ - Write the Dockerfile to `<app_folder>/Dockerfile`
283
+
284
+ The Dockerfile MUST:
285
+ - Use a **multi-stage build** (build stage + runtime stage)
286
+ - Use **specific version tags** for base images (not `latest`)
287
+ - Run as a **non-root user** in the runtime stage
288
+ - **NOT** copy `.env`, `context/`, `e2e/`, or test files into the image
289
+ - Include a `HEALTHCHECK` instruction if a health endpoint is available
290
+ - Include comments explaining each significant instruction
291
+ - Include a `LABEL version="{version}"` instruction using the application version extracted
292
+ from `pom.xml` (`<version>`), `composer.json` (`version`), or `package.json` (`version`)
293
+ - Include a build argument `ARG APP_VERSION={version}` that can be overridden at build time
294
+ via `docker build --build-arg APP_VERSION=1.0.3`
295
+
296
+ Read `references/dockerfile-spring-boot.md`, `references/dockerfile-laravel.md`, or
297
+ `references/dockerfile-nodejs.md` for the complete Dockerfile template per stack.
298
+
299
+ ### Phase 3: Generate or Update Kubernetes Manifests
300
+
301
+ Kubernetes manifests are stored directly in `<app_folder>/k8s/` (no per-environment
302
+ subfolders). Since the `k8s/` folder is gitignored, each machine maintains its own
303
+ independent copy with values specific to the target environment selected in Phase 0.
304
+
305
+ #### 3a. Ensure Folder Structure
306
+
307
+ 1. Check if `<app_folder>/k8s/` exists. If not, create it.
308
+
309
+ Example folder structure:
310
+ ```
311
+ hub_middleware/
312
+ Dockerfile
313
+ k8s/
314
+ namespace.yaml
315
+ configmap.yaml
316
+ secret.yaml
317
+ deployment.yaml
318
+ service.yaml
319
+ ingress.yaml (optional)
320
+ ```
321
+
322
+ #### 3b. Generate Manifests
323
+
324
+ Generate individual YAML files inside `<app_folder>/k8s/` using values from the target
325
+ environment selected in Phase 0:
326
+
327
+ 1. **`namespace.yaml`** — Namespace resource (use project code from CLAUDE.md in lowercase,
328
+ e.g., `urp`). Shared across all applications — identical content for every environment.
329
+ 2. **`configmap.yaml`** — Non-sensitive environment variables (from Step 4 classification).
330
+ Values may differ per environment — use defaults from `.env` for local development;
331
+ use `TODO` placeholders for other environments where values are not known.
332
+ 3. **`secret.yaml`** — Sensitive environment variables (from Step 4 classification),
333
+ base64-encoded. Values use `TODO` placeholders for all non-local environments.
334
+ 4. **`deployment.yaml`** — Container spec with:
335
+ - Versioned image tag (e.g., `image: hub-middleware:1.0.3`), NOT `latest`
336
+ - `envFrom` referencing ConfigMap and Secret
337
+ - Resource requests/limits (sensible defaults: 256Mi-512Mi memory, 250m-500m CPU)
338
+ - Readiness and liveness probes using the detected health endpoint
339
+ - Non-root `securityContext`
340
+ - `hostAliases` (conditional): If the environment's Domain from CLAUDE.md is referenced
341
+ in ConfigMap values AND is not `localhost`, add `hostAliases` mapping the Domain to
342
+ the environment's IP address. This is needed because K8s pod DNS cannot resolve custom
343
+ domains defined only in the host machine's `/etc/hosts`. See `references/k8s-patterns.md`.
344
+ 5. **`service.yaml`** — ClusterIP service exposing the application port
345
+ 6. **`ingress.yaml`** (optional) — if the application is a web application or API with
346
+ external access. Only generate if the environment's CLAUDE.md description suggests
347
+ external access (e.g., mentions domain, IP, or ingress).
348
+
349
+ Read `references/k8s-patterns.md` for the complete manifest templates per resource type.
350
+
351
+ #### 3c. Environment-Specific Values
352
+
353
+ When generating manifests for the target environment:
354
+
355
+ - **ConfigMap and Secret values**: Read `ENVIRONMENT.md` from the project root. If ENVIRONMENT.md
356
+ contains environment-specific credentials (organized by environment), use the matching
357
+ values for the target environment. If values are not found, use `TODO` as placeholder.
358
+ - **Resource limits**: Use sensible defaults. If the CLAUDE.md environment description
359
+ mentions resource constraints, adjust accordingly.
360
+ - **Ingress hostnames**: Derive from the target environment description in CLAUDE.md if
361
+ available (e.g., IP address, domain name). Use `TODO` if not specified.
362
+
363
+ #### 3d. Create or Update Logic
364
+
365
+ For the `<app_folder>/k8s/` folder:
366
+
367
+ 1. **If a manifest file does not exist** (CREATE mode):
368
+ - Generate the file from the detected values and templates
369
+ - Write to `<app_folder>/k8s/<resource>.yaml`
370
+
371
+ 2. **If it already exists** (UPDATE mode):
372
+ - Read the existing manifest
373
+ - Compare detected values against what the manifest currently has:
374
+ - New or removed environment variables → update configmap.yaml and secret.yaml
375
+ - Image version changed → update deployment.yaml image tag
376
+ - Port changed → update service.yaml and deployment.yaml container port
377
+ - Health endpoint changed → update probes in deployment.yaml
378
+ - New dependencies → add init containers or environment variables
379
+ - Preserve any lines or resources marked with `# CUSTOM:` comments
380
+ - Update only the parts that changed
381
+ - Log what was changed
382
+
383
+ #### 3e. Namespace Consistency
384
+
385
+ All manifests use the same namespace (derived from the project code in CLAUDE.md's
386
+ `# Project Detail` → Project Code in lowercase, e.g., `urp`).
387
+
388
+ #### 3f. Docker Build Reference
389
+
390
+ Include a comment block at the top of `deployment.yaml`:
391
+
392
+ ```yaml
393
+ # Application: <Application Name>
394
+ # Environment: <Target Environment Name>
395
+ # Build: docker build -t <image-name>:<version> <app_folder>/
396
+ # Tag: docker tag <image-name>:<version> <image-name>:latest
397
+ # Apply: kubectl apply -f <app_folder>/k8s/
398
+ ---
399
+ ```
400
+
401
+ #### 3g. Update `.gitignore`
402
+
403
+ The `k8s/` folder contains environment-specific manifests with ConfigMap values,
404
+ base64-encoded Secrets, hostnames, and credentials that MUST NOT be committed to
405
+ version control. After generating or updating the K8s manifests, ensure the
406
+ application's `.gitignore` excludes the `k8s/` folder:
407
+
408
+ 1. Read `<app_folder>/.gitignore`. If it does not exist, create it.
409
+ 2. Check if `k8s/` (or an equivalent pattern like `k8s/**`) is already listed.
410
+ 3. If **not already present**, append the following block:
411
+
412
+ ```gitignore
413
+
414
+ # Kubernetes manifests — environment-specific configs and secrets
415
+ k8s/
416
+ ```
417
+
418
+ 4. If already present, do nothing — do not duplicate the entry.
419
+ 5. Do NOT remove or modify any other entries in `.gitignore`.
420
+
421
+ ## Constraints
422
+
423
+ These constraints are non-negotiable:
424
+
425
+ 1. **Custom applications only** — This skill only processes applications listed under
426
+ `# Custom Applications` in CLAUDE.md. 3rd party applications and external services are
427
+ NOT containerized by this skill.
428
+
429
+ 2. **Stack-agnostic output** — The skill must work for Spring Boot, Laravel, and Node.js
430
+ applications. All stack-specific logic is gated behind the detection in Phase 1.
431
+
432
+ 3. **Real values from context** — Every manifest and Dockerfile must use the actual
433
+ application name, port, environment variables, and dependency versions from the project's
434
+ context files. No placeholder `<TODO>` values except for legitimately unknown production
435
+ values (registry URL, domain name).
436
+
437
+ 4. **ConfigMap/Secret separation** — Sensitive values must NEVER appear in ConfigMaps.
438
+ The classification logic in Phase 1 Step 4 must be applied consistently.
439
+
440
+ 5. **Non-root containers** — All Dockerfiles must run the application as a non-root user.
441
+
442
+ 6. **Multi-stage builds** — All Dockerfiles must use multi-stage builds to minimize the
443
+ runtime image size. Build tools (Maven, Composer, npm dev dependencies) must NOT be
444
+ in the final image.
445
+
446
+ 7. **No `.env` in containers** — The `.env` file is for local development only. Container
447
+ environments receive configuration via K8s ConfigMap + Secret injection.
448
+
449
+ 8. **Flat K8s manifest folder** — Kubernetes manifests live directly in `<app_folder>/k8s/`
450
+ with separate YAML files per resource type (no per-environment subfolders). The `k8s/`
451
+ folder is gitignored — each machine maintains its own copy with values specific to
452
+ the target environment. Run the skill once per environment on the target machine.
453
+
454
+ 9. **Idempotent — supports both create and update** — If Dockerfile or manifest already
455
+ exists, read it first, detect what changed, and update only the affected parts. Preserve
456
+ any manual customizations marked with `# CUSTOM:`. If the file does not exist, create
457
+ from scratch. Always log what was created or changed.
458
+
459
+ 10. **Single-pass execution** — This skill completes in one pass (Dockerfile + manifest).
460
+ No Ralph Loop needed.
461
+
462
+ 11. **Production-safe defaults** — Dockerfile and K8s manifests must default to
463
+ production-safe values. Dev-specific settings (JTE dev mode, DevTools, debug logging)
464
+ must be OFF by default in the container, overridden only via explicit environment
465
+ variables.
466
+
467
+ 12. **Versioned image tags** — Kubernetes Deployment manifests must reference versioned
468
+ image tags (e.g., `hub-middleware:1.0.3`), never `latest`.
@@ -0,0 +1,197 @@
1
+ # Dockerfile Pattern — Laravel
2
+
3
+ Multi-stage Dockerfile for Laravel applications with PHP-FPM and Nginx.
4
+
5
+ ## Detection Inputs
6
+
7
+ | Input | Source | Used For |
8
+ |---|---|---|
9
+ | PHP version | `composer.json` → `require.php` | Base image tag |
10
+ | Laravel version | `composer.json` → `require.laravel/framework` | Compatibility |
11
+ | Has frontend | `vite.config.js` or `webpack.mix.js` present | Include Node build stage |
12
+ | App port | `.env.example` → `APP_PORT` or default `8000` | EXPOSE / Nginx config |
13
+
14
+ ## Template — PHP-FPM + Nginx (Production)
15
+
16
+ ```dockerfile
17
+ # =============================================================================
18
+ # Stage 1: Frontend Build (if Vite/Mix is used)
19
+ # =============================================================================
20
+ FROM node:22-alpine AS frontend
21
+
22
+ WORKDIR /app
23
+
24
+ # Copy package files for dependency caching
25
+ COPY package.json package-lock.json ./
26
+ RUN npm ci --production=false
27
+
28
+ # Copy frontend source and build
29
+ COPY resources ./resources
30
+ COPY vite.config.js ./
31
+ COPY tailwind.config.js ./
32
+ COPY postcss.config.js ./
33
+ RUN npm run build
34
+
35
+ # =============================================================================
36
+ # Stage 2: Composer Dependencies
37
+ # =============================================================================
38
+ FROM composer:2 AS composer
39
+
40
+ WORKDIR /app
41
+
42
+ COPY composer.json composer.lock ./
43
+ RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist
44
+
45
+ COPY . .
46
+ RUN composer dump-autoload --optimize --no-dev
47
+
48
+ # =============================================================================
49
+ # Stage 3: Runtime (PHP-FPM + Nginx)
50
+ # =============================================================================
51
+ FROM php:{php_version}-fpm-alpine AS runtime
52
+
53
+ # Install required PHP extensions
54
+ RUN apk add --no-cache \
55
+ nginx \
56
+ supervisor \
57
+ && docker-php-ext-install pdo pdo_mysql opcache
58
+
59
+ # Configure PHP for production
60
+ COPY docker/php.ini /usr/local/etc/php/conf.d/app.ini
61
+ COPY docker/nginx.conf /etc/nginx/http.d/default.conf
62
+ COPY docker/supervisord.conf /etc/supervisord.conf
63
+
64
+ # Create non-root user
65
+ RUN addgroup -S appgroup && adduser -S appuser -G appgroup
66
+
67
+ WORKDIR /var/www/html
68
+
69
+ # Copy application from composer stage
70
+ COPY --from=composer /app .
71
+
72
+ # Copy built frontend assets from frontend stage
73
+ COPY --from=frontend /app/public/build ./public/build
74
+
75
+ # Set permissions
76
+ RUN chown -R appuser:appgroup /var/www/html \
77
+ && chmod -R 775 storage bootstrap/cache
78
+
79
+ # Expose port
80
+ EXPOSE {app_port}
81
+
82
+ # Health check
83
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
84
+ CMD wget --no-verbose --tries=1 --spider http://localhost:{app_port}/health || exit 1
85
+
86
+ # Production defaults
87
+ ENV APP_ENV=production
88
+ ENV APP_DEBUG=false
89
+ ENV LOG_CHANNEL=stderr
90
+
91
+ ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
92
+ ```
93
+
94
+ ## Supporting Files
95
+
96
+ The Dockerfile references supporting config files that should be documented in
97
+ DEPLOYMENT.md. These are placed in a `docker/` directory in the application root.
98
+
99
+ ### docker/php.ini
100
+
101
+ ```ini
102
+ [PHP]
103
+ display_errors = Off
104
+ log_errors = On
105
+ error_log = /dev/stderr
106
+ opcache.enable = 1
107
+ opcache.memory_consumption = 256
108
+ opcache.interned_strings_buffer = 16
109
+ opcache.max_accelerated_files = 20000
110
+ opcache.validate_timestamps = 0
111
+ upload_max_filesize = 50M
112
+ post_max_size = 50M
113
+ memory_limit = 512M
114
+ ```
115
+
116
+ ### docker/nginx.conf
117
+
118
+ ```nginx
119
+ server {
120
+ listen {app_port};
121
+ root /var/www/html/public;
122
+ index index.php;
123
+
124
+ location / {
125
+ try_files $uri $uri/ /index.php?$query_string;
126
+ }
127
+
128
+ location ~ \.php$ {
129
+ fastcgi_pass 127.0.0.1:9000;
130
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
131
+ include fastcgi_params;
132
+ }
133
+
134
+ location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
135
+ expires 1y;
136
+ add_header Cache-Control "public, immutable";
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### docker/supervisord.conf
142
+
143
+ ```ini
144
+ [supervisord]
145
+ nodaemon=true
146
+ logfile=/dev/stdout
147
+ logfile_maxbytes=0
148
+
149
+ [program:php-fpm]
150
+ command=/usr/local/sbin/php-fpm --nodaemonize
151
+ autostart=true
152
+ autorestart=true
153
+ stdout_logfile=/dev/stdout
154
+ stdout_logfile_maxbytes=0
155
+ stderr_logfile=/dev/stderr
156
+ stderr_logfile_maxbytes=0
157
+
158
+ [program:nginx]
159
+ command=/usr/sbin/nginx -g "daemon off;"
160
+ autostart=true
161
+ autorestart=true
162
+ stdout_logfile=/dev/stdout
163
+ stdout_logfile_maxbytes=0
164
+ stderr_logfile=/dev/stderr
165
+ stderr_logfile_maxbytes=0
166
+ ```
167
+
168
+ ## Template — Artisan Serve (Development/Simple)
169
+
170
+ For simpler setups without Nginx:
171
+
172
+ ```dockerfile
173
+ FROM php:{php_version}-cli-alpine AS runtime
174
+
175
+ RUN docker-php-ext-install pdo pdo_mysql
176
+
177
+ WORKDIR /var/www/html
178
+ COPY --from=composer /app .
179
+ COPY --from=frontend /app/public/build ./public/build
180
+
181
+ RUN addgroup -S appgroup && adduser -S appuser -G appgroup
182
+ RUN chown -R appuser:appgroup /var/www/html
183
+ USER appuser
184
+
185
+ EXPOSE {app_port}
186
+ CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port={app_port}"]
187
+ ```
188
+
189
+ ## Notes
190
+
191
+ - **Queue workers**: If the application uses Laravel queues (detected by `QUEUE_CONNECTION`
192
+ env var or `app/Jobs/` directory), document a separate Deployment for the queue worker
193
+ container using the same image but `CMD ["php", "artisan", "queue:work"]`.
194
+ - **Scheduler**: If `app/Console/Kernel.php` has scheduled tasks, document a CronJob
195
+ manifest using `CMD ["php", "artisan", "schedule:run"]`.
196
+ - **Storage**: Laravel's `storage/` directory needs a writable volume if file uploads
197
+ are used. Document a PersistentVolumeClaim in DEPLOYMENT.md.