@flisk-sh/flisk 0.0.0-stage → 1.3.190

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 (440) hide show
  1. package/CHANGELOG.md +492 -0
  2. package/LICENSE +21 -0
  3. package/README.md +479 -2
  4. package/Tiltfile +151 -0
  5. package/cli/bin/flisk.js +42 -0
  6. package/cli/dist/agent-runtimes/custom.js +335 -0
  7. package/cli/dist/agent-runtimes/registry.js +34 -0
  8. package/cli/dist/backend-frameworks/elysia.js +38 -0
  9. package/cli/dist/backend-frameworks/express.js +49 -0
  10. package/cli/dist/backend-frameworks/fastify.js +43 -0
  11. package/cli/dist/backend-frameworks/h3.js +37 -0
  12. package/cli/dist/backend-frameworks/koa.js +47 -0
  13. package/cli/dist/backend-frameworks/mcp.js +86 -0
  14. package/cli/dist/backend-frameworks/nestjs.js +55 -0
  15. package/cli/dist/backend-frameworks/registry.js +134 -0
  16. package/cli/dist/backend-frameworks/types.js +1 -0
  17. package/cli/dist/backend-languages/bun.js +76 -0
  18. package/cli/dist/backend-languages/go.js +125 -0
  19. package/cli/dist/backend-languages/node.js +122 -0
  20. package/cli/dist/backend-languages/python.js +94 -0
  21. package/cli/dist/backend-languages/registry.js +37 -0
  22. package/cli/dist/backend-languages/rust.js +82 -0
  23. package/cli/dist/backend-languages/types.js +1 -0
  24. package/cli/dist/cli.js +29 -0
  25. package/cli/dist/commands/__tests__/project-e2e-helpers.js +49 -0
  26. package/cli/dist/commands/__tests__/ui-loading-screen.test.js +16 -0
  27. package/cli/dist/commands/agent.js +182 -0
  28. package/cli/dist/commands/completion-candidates.js +116 -0
  29. package/cli/dist/commands/completion.js +843 -0
  30. package/cli/dist/commands/config.js +362 -0
  31. package/cli/dist/commands/doctor.js +1504 -0
  32. package/cli/dist/commands/down.js +114 -0
  33. package/cli/dist/commands/eject.js +108 -0
  34. package/cli/dist/commands/help.js +152 -0
  35. package/cli/dist/commands/import.js +164 -0
  36. package/cli/dist/commands/logs.js +119 -0
  37. package/cli/dist/commands/maintainers.js +102 -0
  38. package/cli/dist/commands/mcp.js +28 -0
  39. package/cli/dist/commands/networks.js +306 -0
  40. package/cli/dist/commands/project.js +447 -0
  41. package/cli/dist/commands/projects.js +103 -0
  42. package/cli/dist/commands/registry.js +53 -0
  43. package/cli/dist/commands/resource.js +992 -0
  44. package/cli/dist/commands/resources.js +127 -0
  45. package/cli/dist/commands/restart.js +89 -0
  46. package/cli/dist/commands/runtime.js +75 -0
  47. package/cli/dist/commands/stack.js +171 -0
  48. package/cli/dist/commands/stacks.js +71 -0
  49. package/cli/dist/commands/status.js +211 -0
  50. package/cli/dist/commands/ui.js +725 -0
  51. package/cli/dist/commands/up.js +609 -0
  52. package/cli/dist/commands/upgrade.js +840 -0
  53. package/cli/dist/commands/version.js +10 -0
  54. package/cli/dist/components/Accessible.js +11 -0
  55. package/cli/dist/components/BaseTooltip.js +30 -0
  56. package/cli/dist/components/DetailPanel.js +33 -0
  57. package/cli/dist/components/FileTree.js +51 -0
  58. package/cli/dist/components/ResourceSelectInput.js +42 -0
  59. package/cli/dist/components/ResourceTable.js +35 -0
  60. package/cli/dist/components/ServiceIssues.js +13 -0
  61. package/cli/dist/components/TUIHeader.js +27 -0
  62. package/cli/dist/components/TabBar.js +27 -0
  63. package/cli/dist/components/Tooltip.js +13 -0
  64. package/cli/dist/components/index.js +11 -0
  65. package/cli/dist/components/ui-theme.js +120 -0
  66. package/cli/dist/config/platform-standards.js +182 -0
  67. package/cli/dist/frontend-frameworks/lit.js +64 -0
  68. package/cli/dist/frontend-frameworks/preact.js +62 -0
  69. package/cli/dist/frontend-frameworks/qwik.js +61 -0
  70. package/cli/dist/frontend-frameworks/react.js +69 -0
  71. package/cli/dist/frontend-frameworks/registry.js +83 -0
  72. package/cli/dist/frontend-frameworks/solid.js +62 -0
  73. package/cli/dist/frontend-frameworks/svelte.js +67 -0
  74. package/cli/dist/frontend-frameworks/tanstack-router.js +101 -0
  75. package/cli/dist/frontend-frameworks/types.js +1 -0
  76. package/cli/dist/frontend-frameworks/vanilla.js +59 -0
  77. package/cli/dist/frontend-frameworks/vue.js +72 -0
  78. package/cli/dist/generator/__tests__/golden-helpers.js +43 -0
  79. package/cli/dist/generator/extension-fetch.js +291 -0
  80. package/cli/dist/generator/resource-templates.js +133 -0
  81. package/cli/dist/generator/template-engine.js +801 -0
  82. package/cli/dist/index.js +10 -0
  83. package/cli/dist/mcp/server.js +101 -0
  84. package/cli/dist/mcp/tools.js +351 -0
  85. package/cli/dist/program.js +37 -0
  86. package/cli/dist/types/index.js +38 -0
  87. package/cli/dist/utils/agent-host.js +36 -0
  88. package/cli/dist/utils/atomic-write.js +36 -0
  89. package/cli/dist/utils/bun-floor.js +9 -0
  90. package/cli/dist/utils/bun-requirement.js +10 -0
  91. package/cli/dist/utils/cache.js +63 -0
  92. package/cli/dist/utils/cold-preflight.js +257 -0
  93. package/cli/dist/utils/command-helpers.js +57 -0
  94. package/cli/dist/utils/constants.js +160 -0
  95. package/cli/dist/utils/discovery-context.js +71 -0
  96. package/cli/dist/utils/discovery-paths.js +93 -0
  97. package/cli/dist/utils/docker.js +34 -0
  98. package/cli/dist/utils/doctor-crashloop.js +161 -0
  99. package/cli/dist/utils/doctor-fix.js +391 -0
  100. package/cli/dist/utils/doctor-host.js +66 -0
  101. package/cli/dist/utils/doctor-report.js +78 -0
  102. package/cli/dist/utils/doctor-runtime.js +880 -0
  103. package/cli/dist/utils/doctor-starlark.js +132 -0
  104. package/cli/dist/utils/doctor-wiring.js +698 -0
  105. package/cli/dist/utils/down-cleanup.js +125 -0
  106. package/cli/dist/utils/env-validator.js +266 -0
  107. package/cli/dist/utils/errors.js +175 -0
  108. package/cli/dist/utils/exec-async.js +31 -0
  109. package/cli/dist/utils/file-helpers.js +104 -0
  110. package/cli/dist/utils/formatting.js +165 -0
  111. package/cli/dist/utils/generated-paths.js +51 -0
  112. package/cli/dist/utils/help-command.js +11 -0
  113. package/cli/dist/utils/host-port-config.js +140 -0
  114. package/cli/dist/utils/host-port-plan.js +100 -0
  115. package/cli/dist/utils/json-output.js +29 -0
  116. package/cli/dist/utils/machine-output.js +33 -0
  117. package/cli/dist/utils/paths.js +43 -0
  118. package/cli/dist/utils/platform.js +58 -0
  119. package/cli/dist/utils/port-assignment.js +75 -0
  120. package/cli/dist/utils/port-conflict-fix.js +25 -0
  121. package/cli/dist/utils/port-fallback-notice.js +17 -0
  122. package/cli/dist/utils/port-listener-probe.js +20 -0
  123. package/cli/dist/utils/project-config-defaults.js +17 -0
  124. package/cli/dist/utils/project-features.js +91 -0
  125. package/cli/dist/utils/project-templates.js +32 -0
  126. package/cli/dist/utils/prompt.js +97 -0
  127. package/cli/dist/utils/resource-features.js +184 -0
  128. package/cli/dist/utils/resource-kind.js +9 -0
  129. package/cli/dist/utils/schema-version-gate.js +45 -0
  130. package/cli/dist/utils/search-status.js +12 -0
  131. package/cli/dist/utils/secret-redaction.js +73 -0
  132. package/cli/dist/utils/service-config-checks.js +189 -0
  133. package/cli/dist/utils/service-manifest.js +254 -0
  134. package/cli/dist/utils/service-runtime-state.js +66 -0
  135. package/cli/dist/utils/service-urls.js +112 -0
  136. package/cli/dist/utils/services.js +360 -0
  137. package/cli/dist/utils/shared-infra.js +45 -0
  138. package/cli/dist/utils/shared-platform-postgres.js +232 -0
  139. package/cli/dist/utils/single-flight.js +21 -0
  140. package/cli/dist/utils/smoke.js +401 -0
  141. package/cli/dist/utils/spinner.js +40 -0
  142. package/cli/dist/utils/stack-features.js +15 -0
  143. package/cli/dist/utils/stack-status.js +14 -0
  144. package/cli/dist/utils/startup-report.js +78 -0
  145. package/cli/dist/utils/status-message.js +21 -0
  146. package/cli/dist/utils/status-ports.js +33 -0
  147. package/cli/dist/utils/suggestions.js +63 -0
  148. package/cli/dist/utils/tar.js +46 -0
  149. package/cli/dist/utils/tdk-version.js +51 -0
  150. package/cli/dist/utils/terminal-layout.js +19 -0
  151. package/cli/dist/utils/tilt-events.js +194 -0
  152. package/cli/dist/utils/tilt-logs.js +61 -0
  153. package/cli/dist/utils/tilt-port-owner.js +63 -0
  154. package/cli/dist/utils/tilt-process.js +54 -0
  155. package/cli/dist/utils/tilt-resource-state.js +32 -0
  156. package/cli/dist/utils/tilt-startup.js +49 -0
  157. package/cli/dist/utils/tilt-unreachable-message.js +9 -0
  158. package/cli/dist/utils/tilt.js +104 -0
  159. package/cli/dist/utils/ui-keymap.js +129 -0
  160. package/cli/dist/utils/ui-service-state.js +34 -0
  161. package/cli/dist/utils/up-only.js +28 -0
  162. package/cli/dist/utils/up-readiness.js +118 -0
  163. package/cli/dist/utils/url-actions.js +103 -0
  164. package/cli/dist/utils/validation.js +62 -0
  165. package/cli/dist/utils/which.js +56 -0
  166. package/cli/dist/utils/windows-doctor.js +176 -0
  167. package/cli/dist-bundle/chunk-6EA4DGCY.js +2 -0
  168. package/cli/dist-bundle/chunk-BZ4H7MM4.js +3 -0
  169. package/cli/dist-bundle/chunk-CLCUIDBH.js +510 -0
  170. package/cli/dist-bundle/cli.js +2017 -0
  171. package/cli/dist-bundle/cold-preflight-SY26O2IG.js +2 -0
  172. package/cli/dist-bundle/devtools-ZUHXW33W.js +5 -0
  173. package/cli/dist-bundle/extension-fetch-J4FFH7TJ.js +2 -0
  174. package/cli/package.json +83 -0
  175. package/cli/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  176. package/cli/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  177. package/cli/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  178. package/cli/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  179. package/cli/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  180. package/cli/templates/.tiltignore.hbs +72 -0
  181. package/cli/templates/TILT_RESOURCE_DEFAULTS.star.hbs +220 -0
  182. package/cli/templates/TILT_TECH_STACK.star.hbs +149 -0
  183. package/cli/templates/Tiltfile.hbs +381 -0
  184. package/cli/templates/spec.master.hbs +192 -0
  185. package/discovery/AGENTS.md +362 -0
  186. package/discovery/TILT_DISCOVERY.star +429 -0
  187. package/discovery/Tiltfile +58 -0
  188. package/discovery/config.star +265 -0
  189. package/discovery/constants.star +167 -0
  190. package/discovery/deterministic.star +213 -0
  191. package/discovery/discovery_daemon.star +193 -0
  192. package/discovery/discovery_orchestrator.star +492 -0
  193. package/discovery/filtering.star +7 -0
  194. package/discovery/identity-management-backend/service.yaml +26 -0
  195. package/discovery/identity-management-frontend/service.yaml +29 -0
  196. package/discovery/identity-sdk/service.yaml +9 -0
  197. package/discovery/incremental_discovery.star +215 -0
  198. package/discovery/indexing.star +9 -0
  199. package/discovery/json_manifest_scanner.star +57 -0
  200. package/discovery/libraries.star +48 -0
  201. package/discovery/loading.star +25 -0
  202. package/discovery/manifest/constants.star +106 -0
  203. package/discovery/manifest/loading.star +418 -0
  204. package/discovery/manifest/normalize.star +289 -0
  205. package/discovery/manifest/validation.star +110 -0
  206. package/discovery/normalization.star +13 -0
  207. package/discovery/registry.star +562 -0
  208. package/discovery/resource_snapshot.py +384 -0
  209. package/discovery/resource_snapshot.star +145 -0
  210. package/discovery/schemas.star +3 -0
  211. package/discovery/services/platform/cdc/debezium/nats-connector/service.yaml +9 -0
  212. package/discovery/services/platform/cdc/nats-http-bridge/service.yaml +9 -0
  213. package/discovery/services/platform/orchestrator/orchestrator-glue-backend/service.yaml +26 -0
  214. package/discovery/services/platform/synthetic-monitor/service.yaml +9 -0
  215. package/discovery/test_resource_snapshot.py +253 -0
  216. package/discovery/validation.star +80 -0
  217. package/discovery/versioning.star +3 -0
  218. package/engine/AGENTS.md +706 -0
  219. package/engine/README.md +31 -0
  220. package/engine/REFACTORING_MANIFEST_SYSTEM.md +974 -0
  221. package/engine/Tiltfile +86 -0
  222. package/engine/assets/docker/golden-layers.Dockerfile +204 -0
  223. package/engine/assets/docker/wake-gateway/Dockerfile +28 -0
  224. package/engine/assets/docker/wake-gateway/decide.test.ts +177 -0
  225. package/engine/assets/docker/wake-gateway/decide.ts +135 -0
  226. package/engine/assets/docker/wake-gateway/server.ts +363 -0
  227. package/engine/assets/scripts/cleanup-completed-migrator-images.sh +39 -0
  228. package/engine/assets/scripts/cleanup-tilt-images.sh +122 -0
  229. package/engine/assets/scripts/mac-m1-resource-monitor.sh +123 -0
  230. package/engine/config.star +5 -0
  231. package/engine/entrypoint.star +101 -0
  232. package/engine/lifecycle/orchestrator.star +263 -0
  233. package/engine/paths.star +719 -0
  234. package/engine/schemas/project-schema.json +147 -0
  235. package/engine/schemas/service-schema.json +537 -0
  236. package/engine/scripts/README.md +89 -0
  237. package/engine/scripts/build-and-publish-lib.sh +207 -0
  238. package/engine/scripts/check-lib-publish.sh +87 -0
  239. package/engine/scripts/debug-container.sh +265 -0
  240. package/engine/scripts/generate-manifest.sh +200 -0
  241. package/engine/spec.master +702 -0
  242. package/engine/spec.master.example +341 -0
  243. package/engine/topologies/README.md +33 -0
  244. package/engine/topologies/platform/docker/AGENTS.md +59 -0
  245. package/engine/topologies/platform/docker/auth/auth.star +207 -0
  246. package/engine/topologies/platform/docker/build/frontend_build_plan.star +65 -0
  247. package/engine/topologies/platform/docker/build/golden_image.star +37 -0
  248. package/engine/topologies/platform/docker/build/golden_image_build.star +144 -0
  249. package/engine/topologies/platform/docker/build/golden_image_constants.star +100 -0
  250. package/engine/topologies/platform/docker/build/golden_image_dockerfile.star +263 -0
  251. package/engine/topologies/platform/docker/compose/compose.star +637 -0
  252. package/engine/topologies/platform/docker/compose/compose_helpers.star +54 -0
  253. package/engine/topologies/platform/docker/compose/traefik_standalone.star +171 -0
  254. package/engine/topologies/platform/docker/config/bunignore.star +144 -0
  255. package/engine/topologies/platform/docker/config/dockerignore.star +152 -0
  256. package/engine/topologies/platform/docker/config/healthcheck.star +58 -0
  257. package/engine/topologies/platform/docker/constants.star +211 -0
  258. package/engine/topologies/platform/docker/dockerfile/dockerfile.star +56 -0
  259. package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +264 -0
  260. package/engine/topologies/platform/docker/generators/golden_docker_generator_v2.star +76 -0
  261. package/engine/topologies/platform/docker/index.star +40 -0
  262. package/engine/topologies/platform/docker/layers/bun_helpers.star +25 -0
  263. package/engine/topologies/platform/docker/layers/infisical/index.star +42 -0
  264. package/engine/topologies/platform/docker/layers/infisical/infisical_docker.star +264 -0
  265. package/engine/topologies/platform/docker/layers/l1_base_layers.star +89 -0
  266. package/engine/topologies/platform/docker/layers/l2_dependency_layers.star +147 -0
  267. package/engine/topologies/platform/docker/layers/l3_builder_layers.star +229 -0
  268. package/engine/topologies/platform/docker/layers/l4_runtime_layers.star +282 -0
  269. package/engine/topologies/platform/docker/layers/prisma/constants.star +138 -0
  270. package/engine/topologies/platform/docker/layers/prisma/db_readiness.star +98 -0
  271. package/engine/topologies/platform/docker/layers/prisma/filesystem_setup.star +131 -0
  272. package/engine/topologies/platform/docker/layers/prisma/migrate_script.star +100 -0
  273. package/engine/topologies/platform/docker/layers/prisma/prisma_build.star +128 -0
  274. package/engine/topologies/platform/docker/layers/prisma/prisma_runtime.star +205 -0
  275. package/engine/topologies/platform/docker/layers/prisma/secret_resolution.star +121 -0
  276. package/engine/topologies/platform/docker/networking/api_path_constants.star +206 -0
  277. package/engine/topologies/platform/docker/networking/sablier_container_cycle.star +31 -0
  278. package/engine/topologies/platform/docker/networking/traefik.star +266 -0
  279. package/engine/topologies/platform/docker/networking/traefik_constants.star +148 -0
  280. package/engine/topologies/platform/docker/networking/traefik_helpers.star +162 -0
  281. package/engine/topologies/platform/docker/networking/traefik_static_routes.star +165 -0
  282. package/engine/topologies/platform/docker/secrets/env_secrets.star +74 -0
  283. package/engine/topologies/platform/docker/secrets/infisical.star +109 -0
  284. package/engine/topologies/platform/net/AGENTS.md +64 -0
  285. package/engine/topologies/platform/net/proxy.star +6 -0
  286. package/engine/topologies/platform/registries/local.star +6 -0
  287. package/engine/topologies/platform/registries/verdaccio_loader.star +20 -0
  288. package/engine/topologies/platform/security/AGENTS.md +49 -0
  289. package/engine/topologies/platform/security/secrets.star +241 -0
  290. package/engine/topologies/platform/state/AGENTS.md +64 -0
  291. package/engine/topologies/platform/state/relational.star +6 -0
  292. package/engine/topologies/tilt/common/AGENTS.md +59 -0
  293. package/engine/topologies/tilt/common/utils.star +409 -0
  294. package/engine/topologies/tilt/common/utils_debug.star +71 -0
  295. package/engine/topologies/tilt/common/utils_deterministic.star +170 -0
  296. package/engine/topologies/tilt/common/utils_docker_networks.star +54 -0
  297. package/engine/topologies/tilt/common/utils_env.star +92 -0
  298. package/engine/topologies/tilt/common/utils_output.star +99 -0
  299. package/engine/topologies/tilt/config/AGENTS.md +68 -0
  300. package/engine/topologies/tilt/config/defaults.star +5 -0
  301. package/engine/topologies/tilt/config/feature_flags.star +12 -0
  302. package/engine/topologies/tilt/config/global.star +57 -0
  303. package/engine/topologies/tilt/config/profiles.star +398 -0
  304. package/engine/topologies/tilt/generators/agents_md.star +64 -0
  305. package/engine/topologies/tilt/generators/api_gateway.star +147 -0
  306. package/engine/topologies/tilt/generators/index.star +126 -0
  307. package/engine/topologies/tilt/generators/infisical/README.md +424 -0
  308. package/engine/topologies/tilt/generators/infisical/examples.star +314 -0
  309. package/engine/topologies/tilt/generators/infisical/index.star +59 -0
  310. package/engine/topologies/tilt/generators/infisical/machine_identity.star +422 -0
  311. package/engine/topologies/tilt/generators/infisical/organization.star +536 -0
  312. package/engine/topologies/tilt/generators/infisical/path_manager.star +478 -0
  313. package/engine/topologies/tilt/generators/infisical/secrets_generator.star +422 -0
  314. package/engine/topologies/tilt/generators/nginx_conf.star +145 -0
  315. package/engine/topologies/tilt/generators/npmrc.star +132 -0
  316. package/engine/topologies/tilt/generators/tsconfig.star +25 -0
  317. package/engine/topologies/tilt/generators/typescript/backend_library_tsconfig.star +73 -0
  318. package/engine/topologies/tilt/generators/typescript/backend_tsconfig.star +137 -0
  319. package/engine/topologies/tilt/generators/typescript/common.star +9 -0
  320. package/engine/topologies/tilt/generators/typescript/frontend_library_tsconfig.star +78 -0
  321. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +140 -0
  322. package/engine/topologies/tilt/generators/typescript/include.star +25 -0
  323. package/engine/topologies/tilt/generators/typescript/playwright_config.star +42 -0
  324. package/engine/topologies/tilt/generators/typescript/prisma_config.star +51 -0
  325. package/engine/topologies/tilt/generators/validators.star +868 -0
  326. package/engine/topologies/tilt/generators/vite/backend.star +80 -0
  327. package/engine/topologies/tilt/generators/vite/frameworks/lit.star +92 -0
  328. package/engine/topologies/tilt/generators/vite/frameworks/preact.star +94 -0
  329. package/engine/topologies/tilt/generators/vite/frameworks/qwik.star +94 -0
  330. package/engine/topologies/tilt/generators/vite/frameworks/react.star +128 -0
  331. package/engine/topologies/tilt/generators/vite/frameworks/solid.star +94 -0
  332. package/engine/topologies/tilt/generators/vite/frameworks/svelte.star +94 -0
  333. package/engine/topologies/tilt/generators/vite/frameworks/vanilla.star +92 -0
  334. package/engine/topologies/tilt/generators/vite/frameworks/vue.star +94 -0
  335. package/engine/topologies/tilt/generators/vite/frontend.star +153 -0
  336. package/engine/topologies/tilt/generators/vite/generators.star +45 -0
  337. package/engine/topologies/tilt/generators/vite/helpers.star +256 -0
  338. package/engine/topologies/tilt/generators/vite/library.star +67 -0
  339. package/engine/topologies/tilt/generators/vite/sdk.star +75 -0
  340. package/engine/topologies/tilt/generators/vite/templates.star +460 -0
  341. package/engine/topologies/tilt/generators/vite_config.star +24 -0
  342. package/engine/topologies/tilt/manifest/AGENTS.md +61 -0
  343. package/engine/topologies/tilt/manifest/README.md +679 -0
  344. package/engine/topologies/tilt/manifest/__init__.star +220 -0
  345. package/engine/topologies/tilt/manifest/constants.star +262 -0
  346. package/engine/topologies/tilt/manifest/errors.star +268 -0
  347. package/engine/topologies/tilt/manifest/integration.star +348 -0
  348. package/engine/topologies/tilt/manifest/json_to_yaml.py +61 -0
  349. package/engine/topologies/tilt/manifest/loader.star +239 -0
  350. package/engine/topologies/tilt/manifest/mapper.star +229 -0
  351. package/engine/topologies/tilt/manifest/parser.star +490 -0
  352. package/engine/topologies/tilt/manifest/schema.star +502 -0
  353. package/engine/topologies/tilt/manifest/tests/test_integration.star +278 -0
  354. package/engine/topologies/tilt/manifest/tests/test_loader_parser.star +475 -0
  355. package/engine/topologies/tilt/manifest/tests/test_manifest.star +319 -0
  356. package/engine/topologies/tilt/manifest/tests/test_validator.star +472 -0
  357. package/engine/topologies/tilt/manifest/validator.star +744 -0
  358. package/engine/topologies/tilt/manifest/yaml_to_json.py +20 -0
  359. package/engine/topologies/tilt/resources/AGENTS.md +71 -0
  360. package/engine/topologies/tilt/resources/build_concurrency.star +56 -0
  361. package/engine/topologies/tilt/resources/conditions.star +5 -0
  362. package/engine/topologies/tilt/resources/databases.star +246 -0
  363. package/engine/topologies/tilt/resources/declaration.star +5 -0
  364. package/engine/topologies/tilt/resources/deps.star +5 -0
  365. package/engine/topologies/tilt/resources/infra-loader.star +504 -0
  366. package/engine/topologies/tilt/resources/libs.star +32 -0
  367. package/engine/topologies/tilt/resources/libs_discovery.star +104 -0
  368. package/engine/topologies/tilt/resources/libs_paths.star +81 -0
  369. package/engine/topologies/tilt/resources/libs_resource_definition.star +153 -0
  370. package/engine/topologies/tilt/resources/libs_setup.star +166 -0
  371. package/engine/topologies/tilt/resources/libs_tsconfig.star +39 -0
  372. package/engine/topologies/tilt/resources/orchestrator/apply.star +79 -0
  373. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +864 -0
  374. package/engine/topologies/tilt/resources/orchestrator/apply_manifest_orchestration.star +90 -0
  375. package/engine/topologies/tilt/resources/orchestrator/apply_migrator_orchestration.star +150 -0
  376. package/engine/topologies/tilt/resources/orchestrator/apply_resource_validation.star +38 -0
  377. package/engine/topologies/tilt/resources/orchestrator/apply_runtime_flags.star +46 -0
  378. package/engine/topologies/tilt/resources/orchestrator/builders/typescript.star +193 -0
  379. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/AGENTS.md +75 -0
  380. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/index.star +23 -0
  381. package/engine/topologies/tilt/resources/orchestrator/generators/dependencies/resolver.star +136 -0
  382. package/engine/topologies/tilt/resources/orchestrator/generators/env.star +219 -0
  383. package/engine/topologies/tilt/resources/orchestrator/generators/frontend.star +172 -0
  384. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +577 -0
  385. package/engine/topologies/tilt/resources/orchestrator/helpers.star +35 -0
  386. package/engine/topologies/tilt/resources/orchestrator.star +56 -0
  387. package/engine/topologies/tilt/resources/ordering.star +5 -0
  388. package/engine/topologies/tilt/resources/shared-platform-postgres.star +30 -0
  389. package/engine/topologies/tilt/resources/triggers.star +5 -0
  390. package/engine/topologies/tilt/resources/watchers.star +304 -0
  391. package/engine/where-things-live.star +719 -0
  392. package/examples/tdk-example/LICENSE +21 -0
  393. package/examples/tdk-example/README.md +23 -0
  394. package/examples/tdk-example/TILT_SERVICE_DEFAULTS.star +42 -0
  395. package/examples/tdk-example/TILT_TECH_STACK.star +67 -0
  396. package/examples/tdk-example/Tiltfile +25 -0
  397. package/examples/tdk-example/services/platform/messaging/docker-compose.yml +26 -0
  398. package/examples/tdk-example/services/shop/orders-api/package.json +20 -0
  399. package/examples/tdk-example/services/shop/orders-api/service.json +14 -0
  400. package/examples/tdk-example/services/shop/orders-api/src/index.ts +81 -0
  401. package/examples/tdk-example/services/shop/orders-app/index.html +12 -0
  402. package/examples/tdk-example/services/shop/orders-app/package.json +23 -0
  403. package/examples/tdk-example/services/shop/orders-app/public/.gitkeep +0 -0
  404. package/examples/tdk-example/services/shop/orders-app/service.json +9 -0
  405. package/examples/tdk-example/services/shop/orders-app/src/main.tsx +9 -0
  406. package/examples/tdk-example/services/shop/orders-app/src/orders.tsx +67 -0
  407. package/examples/tdk-example/services/shop/orders-app/src/style.css +15 -0
  408. package/examples/tdk-example/services/shop/orders-worker/package.json +18 -0
  409. package/examples/tdk-example/services/shop/orders-worker/service.json +13 -0
  410. package/examples/tdk-example/services/shop/orders-worker/src/index.ts +33 -0
  411. package/ext/Tiltfile +66 -0
  412. package/ext/ide-components/README.md +221 -0
  413. package/ext/ide-components/code_viewer/server.py +208 -0
  414. package/ext/ide-components/config_inspector/server.py +1052 -0
  415. package/ext/ide-components/file_browser/server.py +287 -0
  416. package/ext/ide-components/shared/config_service.py +471 -0
  417. package/ext/ide-components/shared/file_utils.py +300 -0
  418. package/ext/ide-components/shared/http_utils.py +234 -0
  419. package/ext/ide-components/shared/static/styles.css +1334 -0
  420. package/ext/ide-components/shared/syntax_highlighter.py +142 -0
  421. package/ext/ide-components/shared/template_engine.py +125 -0
  422. package/ext/ide-components/shared/templates/base.html +121 -0
  423. package/ext/ide-components/shared/test_config_service.py +328 -0
  424. package/ext/ide-components/shared/tilt_integration.py +160 -0
  425. package/ext/ui-enhancements/cron-status-server.sh +51 -0
  426. package/ext/ui-enhancements/cron-status-tracker.sh +148 -0
  427. package/ext/ui-enhancements/extension.yaml +36 -0
  428. package/ext/ui-enhancements/tiltfile.py +101 -0
  429. package/ext/ui-enhancements/ui/cron-jobs-tab.css +396 -0
  430. package/ext/ui-enhancements/ui/help-panel.css +279 -0
  431. package/ext/ui-enhancements/ui/icons.css +125 -0
  432. package/ext/ui-enhancements/ui/tooltips.css +114 -0
  433. package/package.json +94 -4
  434. package/shared-platform-engineering/docker-templates/bun-hoisted-symlink-fix.sh +25 -0
  435. package/shared-platform-engineering/docker-templates/infisical-entrypoint.sh +466 -0
  436. package/shared-platform-engineering/docker-templates/install-deps.sh +154 -0
  437. package/shared-platform-engineering/docker-templates/prisma-bun-client-link-fix.sh +14 -0
  438. package/shared-platform-engineering/docker-templates/prisma-normalize-client.sh +58 -0
  439. package/specs/specs/TILT_SERVICE_DEFAULTS.star +207 -0
  440. package/specs/specs/TILT_TECH_STACK.star +161 -0
@@ -0,0 +1,843 @@
1
+ // Copyright (c) 2026 Flisk Landscape contributors
2
+ // SPDX-License-Identifier: MIT
3
+ /**
4
+ * The shell completion scripts.
5
+ *
6
+ * The scripts are rendered from `commands/completion-candidates.ts`, which reads the registered
7
+ * commands, their flags and the framework, language and resource-type registries. A new command or
8
+ * a new registry entry therefore shows up in all four shells without editing a list here.
9
+ */
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { basename, join } from "node:path";
13
+ import chalk from "chalk";
14
+ import { Command } from "commander";
15
+ import { showError } from "../utils/errors.js";
16
+ import { buildCompletionCandidates, } from "./completion-candidates.js";
17
+ const SUPPORTED_SHELLS = ["bash", "zsh", "fish", "powershell"];
18
+ function toEntries(commands, parent = "") {
19
+ return commands.map((command) => {
20
+ const path = parent ? `${parent} ${command.name}` : command.name;
21
+ return {
22
+ path,
23
+ name: command.name,
24
+ aliases: command.aliases,
25
+ description: command.description,
26
+ options: command.options,
27
+ subcommands: toEntries(command.subcommands, path),
28
+ parent,
29
+ ...(command.positional ? { positional: command.positional } : {}),
30
+ ...(command.argument ? { argument: command.argument } : {}),
31
+ ...(command.variadic ? { variadic: true } : {}),
32
+ ...(command.optionValues ? { optionValues: command.optionValues } : {}),
33
+ };
34
+ });
35
+ }
36
+ /** Every command and sub-command, flattened. */
37
+ function flatten(entries) {
38
+ return entries.flatMap((entry) => [entry, ...flatten(entry.subcommands)]);
39
+ }
40
+ /** The words that start a command: the name and every alias. */
41
+ function commandWords(entries) {
42
+ return entries.flatMap((entry) => [entry.name, ...entry.aliases]);
43
+ }
44
+ /** A short description, without the characters the shell literals cannot hold. */
45
+ function shortDescription(description) {
46
+ const text = description.replace(/[[\]'":]/g, "").trim();
47
+ return text.length > 60 ? `${text.slice(0, 57)}...` : text;
48
+ }
49
+ function flagList(options) {
50
+ return options.map((option) => option.flag).join(" ");
51
+ }
52
+ /** The values of a flag, for the command that is being completed. */
53
+ function valuesOf(flag, candidates, entry) {
54
+ return entry?.optionValues?.[flag] ?? candidates.values[flag];
55
+ }
56
+ function typeValueArms(values) {
57
+ const arms = Object.entries(values.byType).map(([type, list]) => ({
58
+ types: [type],
59
+ values: [...list],
60
+ }));
61
+ if (values.fallback.length === 0)
62
+ return arms;
63
+ const same = arms.find((arm) => arm.values.join(" ") === values.fallback.join(" "));
64
+ if (same) {
65
+ same.types.push("");
66
+ }
67
+ else {
68
+ arms.push({ types: [""], values: [...values.fallback] });
69
+ }
70
+ return arms;
71
+ }
72
+ /** The condition of one `--type` arm, in the syntax of bash and zsh. */
73
+ function shellTypeCondition(types) {
74
+ const parts = types
75
+ .filter((type) => type !== "")
76
+ .map((type) => `"$${TYPE_VARIABLE}" = "${type}"`);
77
+ if (types.includes(""))
78
+ parts.push(`-z "\${${TYPE_VARIABLE}}"`);
79
+ return parts.join(" || ");
80
+ }
81
+ /** The name of the variable that holds the `--type` of the command line. */
82
+ const TYPE_VARIABLE = "flisk_type";
83
+ /** The stacks of the current project, read from the machine-readable CLI output. */
84
+ const BASH_STACKS = `flisk stacks --json 2>/dev/null | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"$//'`;
85
+ const BASH_RESOURCES = `flisk resources --json 2>/dev/null | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"$//'`;
86
+ const FISH_STACKS = `flisk stacks --json 2>/dev/null | string match -a -r "\\"name\\":\\"[^\\"]*\\"" | string replace -r "\\"name\\":\\"([^\\"]*)\\"" "\\$1"`;
87
+ const FISH_RESOURCES = `flisk resources --json 2>/dev/null | string match -a -r "\\"name\\":\\"[^\\"]*\\"" | string replace -r "\\"name\\":\\"([^\\"]*)\\"" "\\$1"`;
88
+ /** The `--type` of the current command line, in bash. */
89
+ const BASH_TYPE = ` ${TYPE_VARIABLE}=""
90
+ local i
91
+ for (( i=1; i < COMP_CWORD; i++ )); do
92
+ if [[ "\${COMP_WORDS[i]}" = "--type" ]]; then
93
+ ${TYPE_VARIABLE}="\${COMP_WORDS[i+1]}"
94
+ elif [[ "\${COMP_WORDS[i]}" = "--type="* ]]; then
95
+ ${TYPE_VARIABLE}="\${COMP_WORDS[i]#--type=}"
96
+ fi
97
+ done`;
98
+ /** The `--type` of the current command line, in zsh. */
99
+ const ZSH_TYPE = ` local ${TYPE_VARIABLE}=""
100
+ local i
101
+ for (( i = 2; i < CURRENT; i++ )); do
102
+ if [[ "\${words[i]}" = "--type" ]]; then
103
+ ${TYPE_VARIABLE}="\${words[i+1]}"
104
+ elif [[ "\${words[i]}" = "--type="* ]]; then
105
+ ${TYPE_VARIABLE}="\${words[i]#--type=}"
106
+ fi
107
+ done`;
108
+ /** The `--type` of the current command line, in fish. */
109
+ const FISH_TYPE = `function __flisk_resource_type
110
+ set -l tokens (commandline -opc)
111
+ set -l ${TYPE_VARIABLE} ""
112
+ set -l token_count (count $tokens)
113
+ for i in (seq $token_count)
114
+ if test "$tokens[$i]" = "--type"
115
+ if test $i -lt $token_count
116
+ set next_index (math $i + 1)
117
+ set ${TYPE_VARIABLE} "$tokens[$next_index]"
118
+ end
119
+ else if string match -q -- "--type=*" "$tokens[$i]"
120
+ set ${TYPE_VARIABLE} (string replace -- "--type=" "" "$tokens[$i]")
121
+ end
122
+ end
123
+ echo $${TYPE_VARIABLE}
124
+ end`;
125
+ /** The `--type` of the current command line, in PowerShell. */
126
+ const POWERSHELL_TYPE = ` $${TYPE_VARIABLE} = ''
127
+ for ($i = 0; $i -lt $typed.Count; $i++) {
128
+ if ($typed[$i] -eq '--type' -and ($i + 1) -lt $typed.Count) { $${TYPE_VARIABLE} = $typed[$i + 1] }
129
+ elseif ($typed[$i] -like '--type=*') { $${TYPE_VARIABLE} = $typed[$i].Substring(7) }
130
+ }`;
131
+ /** The values of a flag that depend on the `--type`, in bash. */
132
+ function bashTypeValues(values) {
133
+ const lines = [` local ${TYPE_VARIABLE}_values=""`];
134
+ typeValueArms(values).forEach((arm, index) => {
135
+ lines.push(` ${index === 0 ? "if" : "elif"} [[ ${shellTypeCondition(arm.types)} ]]; then`);
136
+ lines.push(` ${TYPE_VARIABLE}_values="${arm.values.join(" ")}"`);
137
+ });
138
+ lines.push(" fi");
139
+ lines.push(` COMPREPLY=( $(compgen -W "\${${TYPE_VARIABLE}_values}" -- \${cur}) )`);
140
+ return lines.join("\n");
141
+ }
142
+ /** The values of a flag that depend on the `--type`, in zsh. */
143
+ function zshTypeValues(flag, values) {
144
+ const lines = [
145
+ ` local -a ${TYPE_VARIABLE}_values`,
146
+ ` if [[ ${shellTypeCondition(typeValueArms(values)[0].types)} ]]; then`,
147
+ ` ${TYPE_VARIABLE}_values=(${typeValueArms(values)[0].values.join(" ")})`,
148
+ ];
149
+ for (const arm of typeValueArms(values).slice(1)) {
150
+ lines.push(` elif [[ ${shellTypeCondition(arm.types)} ]]; then`);
151
+ lines.push(` ${TYPE_VARIABLE}_values=(${arm.values.join(" ")})`);
152
+ }
153
+ lines.push(" fi");
154
+ lines.push(` (( $#${TYPE_VARIABLE}_values )) && _describe -t values '${flag.slice(2)}' ${TYPE_VARIABLE}_values`);
155
+ return lines.join("\n");
156
+ }
157
+ /** The values of a flag that depend on the `--type`, in fish. */
158
+ function fishTypeValues(values) {
159
+ const lines = [" switch (__flisk_resource_type)"];
160
+ for (const arm of typeValueArms(values)) {
161
+ lines.push(` case ${arm.types.map((type) => (type === "" ? "''" : type)).join(" ")}`);
162
+ lines.push(` printf '%s\\n' ${arm.values.join(" ")}`);
163
+ }
164
+ lines.push(" end");
165
+ return lines.join("\n");
166
+ }
167
+ /** The values of a flag that depend on the `--type`, in PowerShell. */
168
+ function powershellTypeValues(flag, values) {
169
+ const arms = typeValueArms(values);
170
+ const lines = [` if ($previous -eq '${flag}') {`, " $values = @()"];
171
+ arms.forEach((arm, index) => {
172
+ const conditions = arm.types
173
+ .filter((type) => type !== "")
174
+ .map((type) => `$${TYPE_VARIABLE} -eq '${type}'`);
175
+ if (arm.types.includes(""))
176
+ conditions.push(`$${TYPE_VARIABLE} -eq ''`);
177
+ lines.push(` ${index === 0 ? "if" : "elseif"} (${conditions.join(" -or ")}) { $values = @(${arm.values.map((value) => `'${value}'`).join(", ")}) }`);
178
+ });
179
+ lines.push(' return $values | Where-Object { $_ -like "$wordToComplete*" }');
180
+ lines.push(" }");
181
+ return lines.join("\n");
182
+ }
183
+ function renderBash(candidates) {
184
+ const entries = toEntries(candidates.commands);
185
+ const words = commandWords(entries);
186
+ const globalFlags = flagList(candidates.globalOptions);
187
+ const aliasCases = entries
188
+ .flatMap((entry) => entry.aliases.map((alias) => ` ${alias}) cmd="${entry.name}" ;;`))
189
+ .join("\n");
190
+ const commandCases = entries
191
+ .filter((entry) => entry.options.length > 0)
192
+ .map((entry) => ` ${entry.name}) options="${flagList(entry.options)}" ;;`)
193
+ .join("\n");
194
+ const subCommandOptionCases = flatten(entries)
195
+ .filter((entry) => entry.parent !== "" && entry.options.length > 0)
196
+ .map((entry) => ` "${entry.path}") options="${flagList(entry.options)}" ;;`)
197
+ .join("\n");
198
+ // The values of the flags of the current command, when the command accepts its own set.
199
+ const commandValueCases = flatten(entries)
200
+ .flatMap((entry) => Object.entries(entry.optionValues ?? {}).map(([flag, values]) => ` "${entry.path} ${flag}")\n COMPREPLY=( $(compgen -W "${values.join(" ")}" -- \${cur}) )\n return 0\n ;;`))
201
+ .join("\n");
202
+ const valueCases = Object.entries(candidates.values)
203
+ .map(([flag, values]) => ` ${flag})\n COMPREPLY=( $(compgen -W "${values.join(" ")}" -- \${cur}) )\n return 0\n ;;`)
204
+ .join("\n");
205
+ // The values of the flags that depend on the `--type` of the command line.
206
+ const typeValueCases = Object.entries(candidates.typeValues)
207
+ .map(([flag, values]) => ` ${flag})\n${bashTypeValues(values)}\n return 0\n ;;`)
208
+ .join("\n");
209
+ const subCommandCases = entries
210
+ .filter((entry) => entry.subcommands.length > 0)
211
+ .map((entry) => ` ${entry.name}) words="${commandWords(entry.subcommands).join(" ")}" ;;`)
212
+ .join("\n");
213
+ const positionalCases = entries
214
+ .filter((entry) => entry.positional)
215
+ .map((entry) => {
216
+ switch (entry.positional) {
217
+ case "stacks":
218
+ return ` ${entry.name}) words="\${stacks}" ;;`;
219
+ case "resources":
220
+ return ` ${entry.name}) words="\${resources}" ;;`;
221
+ case "stacks-and-resources":
222
+ return ` ${entry.name}) words="\${stacks} \${resources}" ;;`;
223
+ case "commands":
224
+ return ` ${entry.name}) words="\${commands}" ;;`;
225
+ default:
226
+ return "";
227
+ }
228
+ })
229
+ .join("\n");
230
+ // The positional argument of a sub-command, for example the resource of `flisk agent sessions`.
231
+ const subCommandPositionalCases = flatten(entries)
232
+ .filter((entry) => entry.parent !== "" && entry.positional)
233
+ .map((entry) => {
234
+ switch (entry.positional) {
235
+ case "stacks":
236
+ return ` "${entry.path}") words="\${stacks}" ;;`;
237
+ case "resources":
238
+ return ` "${entry.path}") words="\${resources}" ;;`;
239
+ case "stacks-and-resources":
240
+ return ` "${entry.path}") words="\${stacks} \${resources}" ;;`;
241
+ case "commands":
242
+ return ` "${entry.path}") words="\${commands}" ;;`;
243
+ default:
244
+ return "";
245
+ }
246
+ })
247
+ .join("\n");
248
+ // The path of the command the cursor is in, for example "config verify".
249
+ const subCommandPathCases = flatten(entries)
250
+ .filter((entry) => entry.parent !== "")
251
+ .map((entry) => ` "${entry.path}") path="${entry.path}" ;;`)
252
+ .join("\n");
253
+ return `# Flisk CLI Bash Completion
254
+ # Generated by "flisk completion bash". The command and flag lists come from the CLI itself.
255
+ _flisk_completions() {
256
+ local cur prev cmd sub options words commands stacks resources path ${TYPE_VARIABLE} ${TYPE_VARIABLE}_values
257
+ COMPREPLY=()
258
+ cur="\${COMP_WORDS[COMP_CWORD]}"
259
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
260
+ cmd="\${COMP_WORDS[1]}"
261
+ sub="\${COMP_WORDS[2]}"
262
+
263
+ # The path of the command the cursor is in, for example "config verify".
264
+ path="\${cmd}"
265
+ case "\${cmd} \${sub}" in
266
+ ${subCommandPathCases}
267
+ esac
268
+
269
+ commands="${words.join(" ")}"
270
+
271
+ # The stacks and the resources of the current project.
272
+ stacks="$(${BASH_STACKS})"
273
+ resources="$(${BASH_RESOURCES})"
274
+
275
+ ${BASH_TYPE}
276
+
277
+ # The values of the flags of the current command, when the command accepts its own set.
278
+ case "\${path} \${prev}" in
279
+ ${commandValueCases}
280
+ esac
281
+
282
+ # The values of the flags that take a fixed list.
283
+ case "\${prev}" in
284
+ ${valueCases}
285
+ ${typeValueCases}
286
+ --stack)
287
+ COMPREPLY=( $(compgen -W "\${stacks}" -- \${cur}) )
288
+ return 0
289
+ ;;
290
+ --only)
291
+ COMPREPLY=( $(compgen -W "\${resources}" -- \${cur}) )
292
+ return 0
293
+ ;;
294
+ esac
295
+
296
+ # The aliases are the same commands under a different name.
297
+ case "\${cmd}" in
298
+ ${aliasCases}
299
+ esac
300
+
301
+ # The flags of the current command.
302
+ if [[ \${cur} == -* ]]; then
303
+ options="${globalFlags}"
304
+ case "\${cmd}" in
305
+ ${commandCases}
306
+ esac
307
+ if [[ -n "\${sub}" ]]; then
308
+ case "\${cmd} \${sub}" in
309
+ ${subCommandOptionCases}
310
+ esac
311
+ fi
312
+ COMPREPLY=( $(compgen -W "\${options}" -- \${cur}) )
313
+ return 0
314
+ fi
315
+
316
+ # Inside a sub-command the flags and the positional argument of the sub-command are left.
317
+ if [[ -n "\${sub}" ]]; then
318
+ words=""
319
+ case "\${cmd} \${sub}" in
320
+ ${subCommandPositionalCases}
321
+ esac
322
+ if [[ -n "\${words}" ]]; then
323
+ COMPREPLY=( $(compgen -W "\${words}" -- \${cur}) )
324
+ fi
325
+ return 0
326
+ fi
327
+
328
+ words="\${commands}"
329
+ case "\${cmd}" in
330
+ ${subCommandCases}
331
+ ${positionalCases}
332
+ esac
333
+
334
+ COMPREPLY=( $(compgen -W "\${words}" -- \${cur}) )
335
+ return 0
336
+ }
337
+
338
+ complete -F _flisk_completions flisk
339
+ `;
340
+ }
341
+ /** The `_arguments` spec of the positional argument of a command. */
342
+ function zshPositionalSpecs(entry) {
343
+ // A command with sub-commands completes the name of the sub-command, not a value.
344
+ if (entry.subcommands.length > 0) {
345
+ return [`'1: :_flisk_${entry.name}_subcommands'`];
346
+ }
347
+ switch (entry.positional) {
348
+ case "stacks":
349
+ return ["'1:stack:_flisk_stacks'"];
350
+ case "resources":
351
+ return ["'1:resource:_flisk_resources'"];
352
+ case "stacks-and-resources":
353
+ return ["'1:target:_flisk_stacks'"];
354
+ case "commands":
355
+ return ["'1:command:_flisk_commands'"];
356
+ default:
357
+ break;
358
+ }
359
+ // A command with a positional argument still needs the spec, otherwise the value of a flag that
360
+ // follows the argument is not completed.
361
+ if (entry.argument) {
362
+ return [entry.variadic ? `'*:${entry.argument}:'` : `'1:${entry.argument}:'`];
363
+ }
364
+ return [];
365
+ }
366
+ function renderZsh(candidates) {
367
+ const entries = toEntries(candidates.commands);
368
+ const globalFlags = candidates.globalOptions
369
+ .map((option) => `'${option.flag}[${shortDescription(option.description)}]'`)
370
+ .join(" \\\n ");
371
+ const commandBranches = entries
372
+ .map((entry) => {
373
+ const specs = [
374
+ ...entry.options.map((option) => zshOptionSpec(option, candidates, entry)),
375
+ ...zshPositionalSpecs(entry),
376
+ ];
377
+ if (entry.subcommands.length > 0) {
378
+ specs.push(`'*:: :->${entry.name}_args'`);
379
+ }
380
+ if (specs.length === 0)
381
+ return "";
382
+ // The aliases are the same command under a different name.
383
+ const words = [entry.name, ...entry.aliases].join("|");
384
+ return ` ${words})\n _arguments \\\n ${specs.join(" \\\n ")}\n ;;`;
385
+ })
386
+ .filter((branch) => branch !== "")
387
+ .join("\n");
388
+ const subCommandStates = entries
389
+ .filter((entry) => entry.subcommands.length > 0)
390
+ .map((entry) => {
391
+ const branches = entry.subcommands
392
+ .map((subcommand) => {
393
+ const specs = [
394
+ ...subcommand.options.map((option) => zshOptionSpec(option, candidates, subcommand)),
395
+ ...zshPositionalSpecs(subcommand),
396
+ ];
397
+ if (specs.length === 0)
398
+ return "";
399
+ return ` ${subcommand.name})\n _arguments \\\n ${specs.join(" \\\n ")}\n ;;`;
400
+ })
401
+ .filter((branch) => branch !== "")
402
+ .join("\n");
403
+ return ` ${entry.name}_args)\n case "$line[1]" in\n${branches}\n esac\n ;;`;
404
+ })
405
+ .join("\n");
406
+ const commandList = entries
407
+ .flatMap((entry) => [
408
+ ` '${entry.name}:${shortDescription(entry.description)}'`,
409
+ ...entry.aliases.map((alias) => ` '${alias}:${shortDescription(entry.description)}'`),
410
+ ])
411
+ .join("\n");
412
+ const subCommandLists = entries
413
+ .filter((entry) => entry.subcommands.length > 0)
414
+ .map((entry) => `_flisk_${entry.name}_subcommands() {\n local commands=(\n${entry.subcommands
415
+ .map((subcommand) => ` '${subcommand.name}:${shortDescription(subcommand.description)}'`)
416
+ .join("\n")}\n )\n _describe -t commands '${entry.name} command' commands\n}`)
417
+ .join("\n\n");
418
+ const typeValueFunctions = Object.entries(candidates.typeValues)
419
+ .map(([flag, values]) => `_flisk_${flag.slice(2)}_values() {\n${ZSH_TYPE}\n${zshTypeValues(flag, values)}\n}`)
420
+ .join("\n\n");
421
+ return `#compdef flisk
422
+
423
+ # Flisk CLI Zsh Completion
424
+ # Generated by "flisk completion zsh". The command and flag lists come from the CLI itself.
425
+
426
+ _flisk() {
427
+ local curcontext="$curcontext" state line
428
+ typeset -A opt_args
429
+
430
+ _arguments -C \\
431
+ ${globalFlags} \\
432
+ '1: :_flisk_commands' \\
433
+ '*:: :->args'
434
+
435
+ case "$line[1]" in
436
+ ${commandBranches}
437
+ esac
438
+
439
+ case "$state" in
440
+ ${subCommandStates}
441
+ esac
442
+ }
443
+
444
+ _flisk_commands() {
445
+ local commands=(
446
+ ${commandList}
447
+ )
448
+ _describe -t commands 'flisk command' commands
449
+ }
450
+
451
+ _flisk_stacks() {
452
+ local stacks
453
+ stacks=($(flisk stacks --json 2>/dev/null | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"$//'))
454
+ _describe -t stacks 'stack' stacks
455
+ }
456
+
457
+ _flisk_resources() {
458
+ local resources
459
+ resources=($(flisk resources --json 2>/dev/null | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"$//'))
460
+ _describe -t resources 'resource' resources
461
+ }
462
+
463
+ ${subCommandLists}
464
+
465
+ ${typeValueFunctions}
466
+ compdef _flisk flisk
467
+ `;
468
+ }
469
+ /** The `_arguments` spec of one flag, with the values it completes when it has them. */
470
+ function zshOptionSpec(option, candidates, entry) {
471
+ const description = shortDescription(option.description);
472
+ if (candidates.typeValues[option.flag]) {
473
+ return `'${option.flag}[${description}]:${option.flag.slice(2)}:_flisk_${option.flag.slice(2)}_values'`;
474
+ }
475
+ const values = valuesOf(option.flag, candidates, entry);
476
+ if (values) {
477
+ return `'${option.flag}[${description}]:${option.flag.slice(2)}:(${values.join(" ")})'`;
478
+ }
479
+ if (option.flag === "--stack") {
480
+ return `'${option.flag}[${description}]:stack:_flisk_stacks'`;
481
+ }
482
+ if (option.flag === "--only") {
483
+ return `'${option.flag}[${description}]:service:_flisk_resources'`;
484
+ }
485
+ return option.takesValue
486
+ ? `'${option.flag}[${description}]:${option.flag.slice(2)}:'`
487
+ : `'${option.flag}[${description}]'`;
488
+ }
489
+ function renderFish(candidates) {
490
+ const entries = toEntries(candidates.commands);
491
+ const commandLines = entries.map((entry) => `complete -c flisk -f -n '__fish_use_subcommand' -a '${entry.name}' -d '${shortDescription(entry.description)}'`);
492
+ const aliasLines = entries.flatMap((entry) => entry.aliases.map((alias) => `complete -c flisk -f -n '__fish_use_subcommand' -a '${alias}' -d '${shortDescription(entry.description)}'`));
493
+ const subCommandLines = entries.flatMap((entry) => entry.subcommands.map((subcommand) => `complete -c flisk -f -n '__fish_seen_subcommand_from ${entry.name}' -a '${subcommand.name}' -d '${shortDescription(subcommand.description)}'`));
494
+ const optionLines = flatten(entries)
495
+ .map((entry) => {
496
+ const condition = fishCondition(entry);
497
+ return entry.options
498
+ .filter((option) => option.flag !== "--stack" && option.flag !== "--only")
499
+ .map((option) => {
500
+ // `-r` tells fish that the flag takes a value, so it offers the values after it.
501
+ const args = fishValueArgument(option, candidates, entry);
502
+ return `complete -c flisk -f -n '${condition}' -l ${option.flag.slice(2)} -d '${shortDescription(option.description)}'${args}`;
503
+ })
504
+ .join("\n");
505
+ })
506
+ .filter((line) => line !== "");
507
+ // The flags whose values come from the current project.
508
+ const projectFlagLines = flatten(entries).flatMap((entry) => {
509
+ const condition = fishCondition(entry);
510
+ return entry.options
511
+ .filter((option) => option.flag === "--stack" || option.flag === "--only")
512
+ .map((option) => {
513
+ const values = option.flag === "--stack" ? `(${FISH_STACKS})` : `(${FISH_RESOURCES})`;
514
+ return `complete -c flisk -f -n '${condition}' -l ${option.flag.slice(2)} -r -d '${shortDescription(option.description)}' -a '${values}'`;
515
+ });
516
+ });
517
+ const globalLines = candidates.globalOptions.map((option) => `complete -c flisk -f -l ${option.flag.slice(2)} -d '${shortDescription(option.description)}'`);
518
+ const stackLines = flatten(entries)
519
+ .filter((entry) => entry.positional === "stacks" || entry.positional === "stacks-and-resources")
520
+ .map((entry) => `complete -c flisk -f -n '${fishCondition(entry)}' -a '(${FISH_STACKS})'`);
521
+ const resourceLines = flatten(entries)
522
+ .filter((entry) => entry.positional === "resources" || entry.positional === "stacks-and-resources")
523
+ .map((entry) => `complete -c flisk -f -n '${fishCondition(entry)}' -a '(${FISH_RESOURCES})'`);
524
+ // The names of the other commands, for `flisk help <command>`.
525
+ const commandNameLines = flatten(entries)
526
+ .filter((entry) => entry.positional === "commands")
527
+ .map((entry) => `complete -c flisk -f -n '${fishCondition(entry)}' -a '${commandWords(entries).join(" ")}'`);
528
+ const typeValueFunctions = Object.entries(candidates.typeValues)
529
+ .map(([flag, values]) => `function __flisk_${flag.slice(2)}_values\n${fishTypeValues(values)}\nend`)
530
+ .join("\n\n");
531
+ return `# Flisk CLI Fish Completion
532
+ # Generated by "flisk completion fish". The command and flag lists come from the CLI itself.
533
+
534
+ # Disable file completions for most commands
535
+ complete -c flisk -f
536
+
537
+ # Commands
538
+ ${commandLines.join("\n")}
539
+
540
+ # Command aliases
541
+ ${aliasLines.join("\n")}
542
+
543
+ # Sub-commands
544
+ ${subCommandLines.join("\n")}
545
+
546
+ # Global options
547
+ ${globalLines.join("\n")}
548
+
549
+ # Command options
550
+ ${optionLines.join("\n")}
551
+
552
+ # Flags whose values come from the current project
553
+ ${projectFlagLines.join("\n")}
554
+
555
+ # Project values
556
+ ${[...stackLines, ...resourceLines, ...commandNameLines].join("\n")}
557
+
558
+ # The type of the current command line, for the flags whose values depend on it
559
+ ${FISH_TYPE}
560
+
561
+ # The values of the flags that depend on the type
562
+ ${typeValueFunctions}
563
+ `;
564
+ }
565
+ /** The fish condition that holds while the user is in a command. */
566
+ function fishCondition(entry) {
567
+ return entry.parent === ""
568
+ ? `__fish_seen_subcommand_from ${entry.name}`
569
+ : `__fish_seen_subcommand_from ${entry.parent}; and __fish_seen_subcommand_from ${entry.name}`;
570
+ }
571
+ /** The fish argument that lists the values of a flag. */
572
+ function fishValueArgument(option, candidates, entry) {
573
+ if (candidates.typeValues[option.flag]) {
574
+ return ` -r -a '(__flisk_${option.flag.slice(2)}_values)'`;
575
+ }
576
+ const values = valuesOf(option.flag, candidates, entry);
577
+ return values ? ` -r -a '${values.join(" ")}'` : "";
578
+ }
579
+ function renderPowerShell(candidates) {
580
+ const entries = toEntries(candidates.commands);
581
+ const commandLines = entries.map((entry) => ` '${entry.name}' = '${shortDescription(entry.description)}'`);
582
+ const aliasDescriptionLines = entries.flatMap((entry) => entry.aliases.map((alias) => ` '${alias}' = '${shortDescription(entry.description)}'`));
583
+ const subCommandLines = entries.flatMap((entry) => entry.subcommands.map((subcommand) => ` '${entry.name} ${subcommand.name}' = '${shortDescription(subcommand.description)}'`));
584
+ const optionLines = flatten(entries).map((entry) => {
585
+ const flags = entry.options.map((option) => `'${option.flag}'`).join(", ");
586
+ return ` '${entry.path}' = @(${flags})`;
587
+ });
588
+ const valueLines = Object.entries(candidates.values).map(([flag, values]) => ` '${flag}' = @(${values.map((value) => `'${value}'`).join(", ")})`);
589
+ // The values of the flags of a command, when the command accepts its own set.
590
+ const commandValueLines = flatten(entries).flatMap((entry) => Object.entries(entry.optionValues ?? {}).map(([flag, values]) => ` '${entry.path} ${flag}' = @(${values.map((value) => `'${value}'`).join(", ")})`));
591
+ const globalLines = candidates.globalOptions.map((option) => `'${option.flag}'`).join(", ");
592
+ const positionalLines = flatten(entries)
593
+ .filter((entry) => entry.positional)
594
+ .map((entry) => ` '${entry.path}' = '${entry.positional}'`);
595
+ const aliasLines = entries.flatMap((entry) => entry.aliases.map((alias) => ` '${alias}' = '${entry.name}'`));
596
+ const typeValueBlocks = Object.entries(candidates.typeValues)
597
+ .map(([flag, values]) => powershellTypeValues(flag, values))
598
+ .join("\n");
599
+ return `Register-ArgumentCompleter -Native -CommandName flisk -ScriptBlock {
600
+ param($wordToComplete, $commandAst, $cursorPosition)
601
+
602
+ # Generated by "flisk completion powershell". The command and flag lists come from the CLI itself.
603
+ $commands = [ordered]@{
604
+ ${commandLines.join("\n")}
605
+ ${aliasDescriptionLines.join("\n")}
606
+ }
607
+
608
+ $aliases = [ordered]@{
609
+ ${aliasLines.join("\n")}
610
+ }
611
+
612
+ $subcommands = [ordered]@{
613
+ ${subCommandLines.join("\n")}
614
+ }
615
+
616
+ $commandOptions = [ordered]@{
617
+ ${optionLines.join("\n")}
618
+ }
619
+
620
+ $optionValues = [ordered]@{
621
+ ${valueLines.join("\n")}
622
+ }
623
+
624
+ $commandOptionValues = [ordered]@{
625
+ ${commandValueLines.join("\n")}
626
+ }
627
+
628
+ $positional = [ordered]@{
629
+ ${positionalLines.join("\n")}
630
+ }
631
+
632
+ $globalOptions = @(${globalLines})
633
+
634
+ $tokens = @()
635
+ foreach ($element in $commandAst.CommandElements) { $tokens += $element.Extent.Text }
636
+
637
+ # The words the user already typed, without the command name.
638
+ $typed = @()
639
+ if ($tokens.Count -gt 1) { $typed = $tokens[1..($tokens.Count - 1)] }
640
+
641
+ $previous = ''
642
+ if ($typed.Count -ge 2 -and $wordToComplete -ne '') { $previous = $typed[$typed.Count - 2] }
643
+ elseif ($typed.Count -ge 1) { $previous = $typed[$typed.Count - 1] }
644
+
645
+ # The command path the cursor is in, for example "config verify".
646
+ $path = @()
647
+ foreach ($token in $typed) {
648
+ if ($token -like '-*') { continue }
649
+ $name = $token
650
+ if ($aliases.Contains($name)) { $name = $aliases[$name] }
651
+ if ($path.Count -eq 0 -and $commands.Contains($name)) { $path += $name; continue }
652
+ if ($path.Count -eq 1 -and $subcommands.Contains(($path[0] + ' ' + $name))) { $path += $name }
653
+ }
654
+ $commandPath = $path -join ' '
655
+
656
+ ${POWERSHELL_TYPE}
657
+
658
+ # The value of the flag before the cursor.
659
+ $valueKey = $commandPath + ' ' + $previous
660
+ if ($commandOptionValues.Contains($valueKey)) {
661
+ return $commandOptionValues[$valueKey] | Where-Object { $_ -like "$wordToComplete*" }
662
+ }
663
+ if ($optionValues.Contains($previous)) {
664
+ return $optionValues[$previous] | Where-Object { $_ -like "$wordToComplete*" }
665
+ }
666
+
667
+ ${typeValueBlocks}
668
+
669
+ # The stacks and the resources of the current project.
670
+ $stacks = @()
671
+ $resources = @()
672
+ if ($previous -eq '--stack' -or $previous -eq '--only' -or $positional.Contains($commandPath)) {
673
+ try { $stacks = (flisk stacks --json | ConvertFrom-Json).data.stacks.name } catch { }
674
+ try { $resources = (flisk resources --json | ConvertFrom-Json).data.resources.name } catch { }
675
+ }
676
+
677
+ # The stacks and the resources behind the flags of the current project.
678
+ if ($previous -eq '--stack') {
679
+ return $stacks | Where-Object { $_ -like "$wordToComplete*" }
680
+ }
681
+ if ($previous -eq '--only') {
682
+ return $resources | Where-Object { $_ -like "$wordToComplete*" }
683
+ }
684
+
685
+ # The flags of the current command.
686
+ if ($wordToComplete -like '-*') {
687
+ $flags = $globalOptions
688
+ if ($commandPath -ne '' -and $commandOptions.Contains($commandPath)) {
689
+ $flags = $flags + $commandOptions[$commandPath]
690
+ }
691
+ return $flags | Where-Object { $_ -like "$wordToComplete*" } | Sort-Object -Unique
692
+ }
693
+
694
+ # The sub-command names of the current command.
695
+ if ($commandPath -ne '') {
696
+ $names = @()
697
+ foreach ($key in $subcommands.Keys) {
698
+ if ($key.StartsWith($commandPath + ' ')) { $names += $key.Split(' ')[-1] }
699
+ }
700
+ if ($names.Count -gt 0) { return $names | Where-Object { $_ -like "$wordToComplete*" } }
701
+ }
702
+
703
+ # The project values of the positional argument.
704
+ if ($positional.Contains($commandPath)) {
705
+ switch ($positional[$commandPath]) {
706
+ 'stacks' { return $stacks | Where-Object { $_ -like "$wordToComplete*" } }
707
+ 'resources' { return $resources | Where-Object { $_ -like "$wordToComplete*" } }
708
+ 'stacks-and-resources' { return ($stacks + $resources) | Where-Object { $_ -like "$wordToComplete*" } }
709
+ 'commands' { return $commands.Keys | Where-Object { $_ -like "$wordToComplete*" } }
710
+ }
711
+ }
712
+
713
+ # The command names.
714
+ return $commands.Keys | Where-Object { $_ -like "$wordToComplete*" }
715
+ }`;
716
+ }
717
+ /** Renders the completion script of one shell. */
718
+ export function renderCompletionScript(shell, candidates) {
719
+ switch (shell) {
720
+ case "bash":
721
+ return renderBash(candidates);
722
+ case "zsh":
723
+ return renderZsh(candidates);
724
+ case "fish":
725
+ return renderFish(candidates);
726
+ case "powershell":
727
+ return renderPowerShell(candidates);
728
+ }
729
+ }
730
+ const POWERSHELL_START = "# FLISK-CLI-COMPLETION-START";
731
+ const POWERSHELL_END = "# FLISK-CLI-COMPLETION-END";
732
+ export const completionCommand = new Command("completion")
733
+ .argument("[shell]", "Target shell (bash, zsh, fish, powershell)")
734
+ .description("Generate shell completion scripts")
735
+ .option("-s, --shell <shell>", "Target shell (bash, zsh, fish, powershell)")
736
+ .option("-o, --output <path>", "Output file path (default: stdout)")
737
+ .option("--install", "Install to shell config automatically")
738
+ .action((shellArgument, options, command) => {
739
+ const requestedShell = options.shell ?? shellArgument;
740
+ const environmentShell = basename(process.env.SHELL ?? "")
741
+ .replace(/\.exe$/i, "")
742
+ .toLowerCase();
743
+ const defaultShell = SUPPORTED_SHELLS.includes(environmentShell)
744
+ ? environmentShell
745
+ : "bash";
746
+ const shell = (requestedShell ?? defaultShell).toLowerCase();
747
+ if (requestedShell === undefined) {
748
+ console.error(chalk.gray(`Using ${shell} completion`));
749
+ }
750
+ if (!SUPPORTED_SHELLS.includes(shell)) {
751
+ showError(`Unsupported shell: ${shell}`, "Supported shells: bash, zsh, fish, powershell");
752
+ process.exit(1);
753
+ }
754
+ // The candidates come from the program this command is registered on, so the completions and
755
+ // the CLI always offer the same commands and flags.
756
+ const program = command.parent;
757
+ if (!program) {
758
+ console.error(chalk.red("❌ The completion command must be registered on the flisk program."));
759
+ process.exit(1);
760
+ }
761
+ const candidates = buildCompletionCandidates(program);
762
+ const completionScript = renderCompletionScript(shell, candidates);
763
+ const filename = shell === "bash"
764
+ ? "flisk-completion.bash"
765
+ : shell === "zsh"
766
+ ? "_flisk"
767
+ : shell === "fish"
768
+ ? "flisk.fish"
769
+ : "Microsoft.PowerShell_profile.ps1";
770
+ if (options.install) {
771
+ const home = homedir();
772
+ let installPath;
773
+ let installInstructions;
774
+ switch (shell) {
775
+ case "powershell": {
776
+ const docs = join(home, "Documents");
777
+ const ps7 = process.env.PSModulePath?.includes(join("Documents", "PowerShell", "Modules"));
778
+ const profileDir = join(docs, ps7 ? "PowerShell" : "WindowsPowerShell");
779
+ installPath = join(profileDir, filename);
780
+ mkdirSync(profileDir, { recursive: true });
781
+ const existing = existsSync(installPath) ? readFileSync(installPath, "utf-8") : "";
782
+ const block = `${POWERSHELL_START}\n${completionScript}\n${POWERSHELL_END}`;
783
+ // The marked block is replaced with the new script, without a regular expression.
784
+ const blockStart = existing.indexOf(POWERSHELL_START);
785
+ const blockEnd = existing.indexOf(POWERSHELL_END, blockStart);
786
+ const updated = blockStart >= 0 && blockEnd > blockStart
787
+ ? existing.slice(0, blockStart) +
788
+ block +
789
+ existing.slice(blockEnd + POWERSHELL_END.length)
790
+ : `${existing}${existing && !existing.endsWith("\n") ? "\n" : ""}${block}\n`;
791
+ writeFileSync(installPath, updated, "utf-8");
792
+ installInstructions = "\n# Close and reopen PowerShell to load the completion.";
793
+ break;
794
+ }
795
+ case "bash": {
796
+ installPath = join(home, ".bash_completion.d", filename);
797
+ const bashDir = join(home, ".bash_completion.d");
798
+ if (!existsSync(bashDir)) {
799
+ mkdirSync(bashDir, { recursive: true });
800
+ }
801
+ installInstructions = `\n# Add to ~/.bashrc:\nsource ~/.bash_completion.d/${filename}`;
802
+ break;
803
+ }
804
+ case "zsh": {
805
+ installPath = join(home, ".zsh", "completions", filename);
806
+ const zshDir = join(home, ".zsh", "completions");
807
+ if (!existsSync(zshDir)) {
808
+ mkdirSync(zshDir, { recursive: true });
809
+ }
810
+ installInstructions =
811
+ "\n# Add to ~/.zshrc:\nfpath+=(~/.zsh/completions)\nautoload -U compinit && compinit";
812
+ break;
813
+ }
814
+ case "fish": {
815
+ installPath = join(home, ".config", "fish", "completions", filename);
816
+ const fishDir = join(home, ".config", "fish", "completions");
817
+ if (!existsSync(fishDir)) {
818
+ mkdirSync(fishDir, { recursive: true });
819
+ }
820
+ installInstructions = "Fish completions loaded automatically";
821
+ break;
822
+ }
823
+ default:
824
+ installPath = "";
825
+ installInstructions = "";
826
+ }
827
+ // PowerShell already wrote the merged profile above; a plain write here would drop the user's other profile lines.
828
+ if (shell !== "powershell") {
829
+ writeFileSync(installPath, completionScript, "utf-8");
830
+ }
831
+ console.log(chalk.green(`✅ Installed ${shell} completion to:`));
832
+ console.log(chalk.cyan(` ${installPath}`));
833
+ console.log(chalk.yellow(installInstructions));
834
+ }
835
+ else if (options.output) {
836
+ writeFileSync(options.output, completionScript, "utf-8");
837
+ console.log(chalk.green(`✅ Written ${shell} completion to:`));
838
+ console.log(chalk.cyan(` ${options.output}`));
839
+ }
840
+ else {
841
+ console.log(completionScript);
842
+ }
843
+ });