@basaltbytes/odoo-agentic-dev 0.1.0-beta.1

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 (134) hide show
  1. package/README.md +464 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +73 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/compose.d.ts +19 -0
  6. package/dist/commands/compose.js +29 -0
  7. package/dist/commands/compose.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +35 -0
  9. package/dist/commands/doctor.js +209 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/down.d.ts +23 -0
  12. package/dist/commands/down.js +46 -0
  13. package/dist/commands/down.js.map +1 -0
  14. package/dist/commands/info.d.ts +10 -0
  15. package/dist/commands/info.js +42 -0
  16. package/dist/commands/info.js.map +1 -0
  17. package/dist/commands/json-report.d.ts +45 -0
  18. package/dist/commands/json-report.js +55 -0
  19. package/dist/commands/json-report.js.map +1 -0
  20. package/dist/commands/link-source.d.ts +33 -0
  21. package/dist/commands/link-source.js +116 -0
  22. package/dist/commands/link-source.js.map +1 -0
  23. package/dist/commands/list.d.ts +29 -0
  24. package/dist/commands/list.js +117 -0
  25. package/dist/commands/list.js.map +1 -0
  26. package/dist/commands/logs.d.ts +17 -0
  27. package/dist/commands/logs.js +28 -0
  28. package/dist/commands/logs.js.map +1 -0
  29. package/dist/commands/prune.d.ts +54 -0
  30. package/dist/commands/prune.js +118 -0
  31. package/dist/commands/prune.js.map +1 -0
  32. package/dist/commands/psql.d.ts +8 -0
  33. package/dist/commands/psql.js +24 -0
  34. package/dist/commands/psql.js.map +1 -0
  35. package/dist/commands/reset-db.d.ts +34 -0
  36. package/dist/commands/reset-db.js +86 -0
  37. package/dist/commands/reset-db.js.map +1 -0
  38. package/dist/commands/resolve-context.d.ts +17 -0
  39. package/dist/commands/resolve-context.js +30 -0
  40. package/dist/commands/resolve-context.js.map +1 -0
  41. package/dist/commands/run.d.ts +31 -0
  42. package/dist/commands/run.js +83 -0
  43. package/dist/commands/run.js.map +1 -0
  44. package/dist/commands/setup.d.ts +46 -0
  45. package/dist/commands/setup.js +93 -0
  46. package/dist/commands/setup.js.map +1 -0
  47. package/dist/commands/shell.d.ts +20 -0
  48. package/dist/commands/shell.js +46 -0
  49. package/dist/commands/shell.js.map +1 -0
  50. package/dist/commands/state-hooks.d.ts +28 -0
  51. package/dist/commands/state-hooks.js +86 -0
  52. package/dist/commands/state-hooks.js.map +1 -0
  53. package/dist/commands/test.d.ts +24 -0
  54. package/dist/commands/test.js +70 -0
  55. package/dist/commands/test.js.map +1 -0
  56. package/dist/commands/trailing-args.d.ts +10 -0
  57. package/dist/commands/trailing-args.js +14 -0
  58. package/dist/commands/trailing-args.js.map +1 -0
  59. package/dist/commands/up.d.ts +23 -0
  60. package/dist/commands/up.js +62 -0
  61. package/dist/commands/up.js.map +1 -0
  62. package/dist/commands/update.d.ts +7 -0
  63. package/dist/commands/update.js +31 -0
  64. package/dist/commands/update.js.map +1 -0
  65. package/dist/commands/worktree.d.ts +97 -0
  66. package/dist/commands/worktree.js +355 -0
  67. package/dist/commands/worktree.js.map +1 -0
  68. package/dist/config/load-recipe.d.ts +14 -0
  69. package/dist/config/load-recipe.js +75 -0
  70. package/dist/config/load-recipe.js.map +1 -0
  71. package/dist/config/schema.d.ts +9 -0
  72. package/dist/config/schema.js +190 -0
  73. package/dist/config/schema.js.map +1 -0
  74. package/dist/core/command-plan.d.ts +34 -0
  75. package/dist/core/command-plan.js +112 -0
  76. package/dist/core/command-plan.js.map +1 -0
  77. package/dist/core/compose-model.d.ts +18 -0
  78. package/dist/core/compose-model.js +80 -0
  79. package/dist/core/compose-model.js.map +1 -0
  80. package/dist/core/compose-project.d.ts +3 -0
  81. package/dist/core/compose-project.js +15 -0
  82. package/dist/core/compose-project.js.map +1 -0
  83. package/dist/core/database-name.d.ts +15 -0
  84. package/dist/core/database-name.js +59 -0
  85. package/dist/core/database-name.js.map +1 -0
  86. package/dist/core/environment.d.ts +84 -0
  87. package/dist/core/environment.js +78 -0
  88. package/dist/core/environment.js.map +1 -0
  89. package/dist/core/port-allocator.d.ts +30 -0
  90. package/dist/core/port-allocator.js +58 -0
  91. package/dist/core/port-allocator.js.map +1 -0
  92. package/dist/core/project-recipe.d.ts +163 -0
  93. package/dist/core/project-recipe.js +13 -0
  94. package/dist/core/project-recipe.js.map +1 -0
  95. package/dist/core/safety.d.ts +11 -0
  96. package/dist/core/safety.js +16 -0
  97. package/dist/core/safety.js.map +1 -0
  98. package/dist/core/worktree-context.d.ts +32 -0
  99. package/dist/core/worktree-context.js +94 -0
  100. package/dist/core/worktree-context.js.map +1 -0
  101. package/dist/errors/errors.d.ts +124 -0
  102. package/dist/errors/errors.js +129 -0
  103. package/dist/errors/errors.js.map +1 -0
  104. package/dist/index.d.ts +3 -0
  105. package/dist/index.js +2 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/platform/command-runner.d.ts +30 -0
  108. package/dist/platform/command-runner.js +65 -0
  109. package/dist/platform/command-runner.js.map +1 -0
  110. package/dist/platform/docker-compose.d.ts +69 -0
  111. package/dist/platform/docker-compose.js +239 -0
  112. package/dist/platform/docker-compose.js.map +1 -0
  113. package/dist/platform/git.d.ts +10 -0
  114. package/dist/platform/git.js +35 -0
  115. package/dist/platform/git.js.map +1 -0
  116. package/dist/platform/odoo-lifecycle.d.ts +30 -0
  117. package/dist/platform/odoo-lifecycle.js +109 -0
  118. package/dist/platform/odoo-lifecycle.js.map +1 -0
  119. package/dist/platform/port-probe.d.ts +7 -0
  120. package/dist/platform/port-probe.js +13 -0
  121. package/dist/platform/port-probe.js.map +1 -0
  122. package/dist/platform/process-supervisor.d.ts +16 -0
  123. package/dist/platform/process-supervisor.js +23 -0
  124. package/dist/platform/process-supervisor.js.map +1 -0
  125. package/dist/platform/state-store.d.ts +37 -0
  126. package/dist/platform/state-store.js +122 -0
  127. package/dist/platform/state-store.js.map +1 -0
  128. package/dist/suppress-sqlite-warning.d.ts +1 -0
  129. package/dist/suppress-sqlite-warning.js +18 -0
  130. package/dist/suppress-sqlite-warning.js.map +1 -0
  131. package/dist/testing/fake-adapters.d.ts +34 -0
  132. package/dist/testing/fake-adapters.js +90 -0
  133. package/dist/testing/fake-adapters.js.map +1 -0
  134. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,464 @@
1
+ # @basaltbytes/odoo-agentic-dev
2
+
3
+ `odoo-agentic-dev` (short alias: `oad`) is an agent-friendly local Odoo development runtime. From the current Git worktree it derives a deterministic database name, deterministic ports, and a namespaced Docker Compose project, then drives the full lifecycle around them: set up a fresh checkout, start Odoo and PostgreSQL, reset and re-initialize the database, update modules, run tests, and start optional companion apps such as a Vite PWA — each branch, agent session, or bug reproduction gets its own isolated stack that cannot collide with the others.
4
+
5
+ Projects describe themselves with one typed recipe file (`odoo-agentic-dev.config.ts`) instead of copied shell scripts and Compose files. The CLI is built for non-interactive use: flags replace prompts, output is deterministic, `info --json` exposes the whole resolved context to tooling, and destructive actions are guarded so a coding agent (or a tired human) cannot accidentally delete a shared database.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 22.15 or newer (the state registry uses the built-in `node:sqlite` — no native dependency)
10
+ - [pnpm](https://pnpm.io)
11
+ - Docker (Docker Desktop on macOS, Docker Engine on Linux) — `info` works without it
12
+ - Windows: **WSL2 only**. Native PowerShell / `cmd.exe` execution is not supported in v1.
13
+
14
+ WSL2 guidance:
15
+
16
+ - clone repositories inside the WSL filesystem, not under `/mnt/c`
17
+ - install Node, package manager, Git, and Docker CLI inside WSL
18
+ - enable Docker Desktop WSL integration
19
+ - pass paths as Linux paths
20
+
21
+ ## Quickstart
22
+
23
+ ```bash
24
+ pnpm add -D @basaltbytes/odoo-agentic-dev
25
+ ```
26
+
27
+ Create `odoo-agentic-dev.config.ts` at the project root (also accepted: `.mts`, `.js`, `.mjs` — `.ts` configs load directly, no precompilation needed). For an Odoo-only project:
28
+
29
+ ```ts
30
+ import { defineConfig } from "@basaltbytes/odoo-agentic-dev";
31
+
32
+ export default defineConfig({
33
+ project: {
34
+ id: "billing-odoo",
35
+ dbPrefix: "billing",
36
+ sharedDatabase: "billing_dev",
37
+ sharedBranches: ["main", "develop"]
38
+ },
39
+
40
+ odoo: {
41
+ version: "18.0",
42
+ dockerfile: "Dockerfile",
43
+ configFile: "config/odoo.conf",
44
+ addons: [
45
+ { host: "addons", container: "/mnt/extra-addons/custom" }
46
+ ]
47
+ },
48
+
49
+ database: {
50
+ initialModules: ["billing_core"]
51
+ }
52
+ });
53
+ ```
54
+
55
+ For a KRISS LAURE-like monorepo with a frontend companion app:
56
+
57
+ ```ts
58
+ import { defineConfig } from "@basaltbytes/odoo-agentic-dev";
59
+
60
+ export default defineConfig({
61
+ project: {
62
+ id: "kriss-laure",
63
+ dbPrefix: "kl",
64
+ sharedDatabase: "kl_e2e_demo",
65
+ sharedBranches: ["main", "master", "dev", "develop", "development"]
66
+ },
67
+
68
+ ports: {
69
+ odooBase: 18069,
70
+ companionBase: 28028,
71
+ range: 1000,
72
+ hashAlgorithm: "posix-cksum"
73
+ },
74
+
75
+ odoo: {
76
+ version: "18.0-20250606",
77
+ serviceName: "odoo",
78
+ databaseServiceName: "db",
79
+ configFile: "config/odoo.worktree.conf",
80
+ dockerfile: "Dockerfile.odoo",
81
+ imageName: "krisslaure-odoo-agentic-dev",
82
+ addons: [
83
+ { host: "backend/addons/Custom", container: "/mnt/extra-addons/Custom" },
84
+ { host: "backend/addons/OCA", container: "/mnt/extra-addons/OCA" }
85
+ ]
86
+ },
87
+
88
+ database: {
89
+ initialModules: ["KL_setup", "KL_payment_demo"],
90
+ withoutDemo: "all",
91
+ postInit: [
92
+ { type: "odoo-shell-file", file: "scripts/odoo-agentic-dev/post-init.py" }
93
+ ]
94
+ },
95
+
96
+ setup: {
97
+ submodules: true,
98
+ packageManagers: [
99
+ { cwd: ".", command: "pnpm", args: ["install"] },
100
+ { cwd: "frontend", command: "pnpm", args: ["install"] }
101
+ ]
102
+ },
103
+
104
+ worktree: {
105
+ copyFiles: [".env.e2e"], // project-root files copied into a fresh worktree when present
106
+ branchPrefix: "worktree-" // branch name for `worktree create <name>` (default)
107
+ },
108
+
109
+ companionApps: [
110
+ {
111
+ name: "pwa",
112
+ cwd: "frontend",
113
+ command: "pnpm",
114
+ args: ["dev", "--host", "0.0.0.0", "--strictPort"],
115
+ portEnv: "PWA_PORT",
116
+ urlEnv: "E2E_BASE_URL",
117
+ env: {
118
+ VITE_SERVICE_API_URL: "",
119
+ VITE_ODOO_DATA_BASE_NAME: "$ODOO_DATABASE",
120
+ VITE_E2E_PROXY_API_URL: "$ODOO_BASE_URL"
121
+ }
122
+ }
123
+ ]
124
+ });
125
+ ```
126
+
127
+ Ports are derived from a hash of the database name modulo `ports.range`. The default hash is FNV-1a 32-bit (`hashAlgorithm: "fnv1a32"`); set `hashAlgorithm: "posix-cksum"` to reproduce the POSIX `cksum` CRC so a project migrating from bash tooling that derives ports via `cksum <<< "$db"` keeps the exact same port per database.
128
+
129
+ Database names are derived from the branch: at most **one** leading type segment (`feature`, `feat`, `bugfix`, `bug`, `hotfix`, `fix`, `chore`, `task`) is stripped — `feature/fix/x` becomes `<dbPrefix>_fix_x` — then the remainder is sanitized and prefixed with `project.dbPrefix`. Set `project.stripBranchPrefixes` (for example `["release"]`) to replace the built-in list.
130
+
131
+ Then:
132
+
133
+ ```bash
134
+ pnpm exec odoo-agentic-dev setup # prepare the worktree (deps, image, database)
135
+ pnpm exec odoo-agentic-dev up # start Odoo + companion apps
136
+ pnpm exec odoo-agentic-dev info # inspect the derived context at any time
137
+ ```
138
+
139
+ ## Commands
140
+
141
+ Every command accepts `--config <path>` to point at an explicit recipe file; otherwise the recipe is discovered from the current working directory upward.
142
+
143
+ ### `odoo-agentic-dev info`
144
+
145
+ Print the resolved worktree context (worktree name, database, Compose project, Odoo URL, companion URLs) without starting containers. Works without Docker running and is deterministic for the same branch and recipe.
146
+
147
+ | Flag | Meaning |
148
+ | --- | --- |
149
+ | `--json` | print machine-readable JSON |
150
+ | `--env` | print `KEY=value` env lines |
151
+ | `--config <path>` | explicit config file path |
152
+
153
+ ### `odoo-agentic-dev setup`
154
+
155
+ Prepare a new worktree: initialize Git submodules (if the recipe asks for it), run the recipe's package manager install steps, ensure the Docker image builds, reset and initialize the current worktree database, run post-init hooks, and print URLs. It prints the resolved database before destructive work and never deletes a shared database by default. The database step honors the same template fast-reset semantics as `reset-db` (see below).
156
+
157
+ | Flag | Meaning |
158
+ | --- | --- |
159
+ | `--skip-install` | skip submodule + package manager steps |
160
+ | `--skip-db` | skip database reset/initialization |
161
+ | `--allow-shared` | permit acting on the shared database |
162
+ | `--no-template` | full init even when a template snapshot exists (template kept) |
163
+ | `--refresh-template` | full init and take a fresh template snapshot |
164
+ | `--json` | suppress decorative output; print one final JSON report line |
165
+ | `--config <path>` | explicit config file path |
166
+
167
+ ### `odoo-agentic-dev up`
168
+
169
+ Start Odoo (and PostgreSQL) on the derived port, then the configured companion apps with context-derived env injected (`ODOO_DATABASE`, `ODOO_BASE_URL`, per-app ports). In attached mode, Ctrl-C stops all child processes and the first failing process is reported.
170
+
171
+ Before starting anything, `up` probes the derived Odoo port. If it is busy and the holder is not this worktree's own already-running stack (idempotent `up`), it fails fast with a `PortConflictError` naming the holder stack when the state registry knows it — set `ODOO_HTTP_PORT` to override, or `prune` stale environments.
172
+
173
+ | Flag | Meaning |
174
+ | --- | --- |
175
+ | `--odoo-only` | skip companion apps |
176
+ | `--no-build` | start containers without rebuilding the image |
177
+ | `--logs` | follow Odoo logs after start |
178
+ | `--detach` | start containers and return |
179
+ | `--json` | suppress decorative output; print one final JSON report line |
180
+ | `--config <path>` | explicit config file path |
181
+
182
+ ### `odoo-agentic-dev down`
183
+
184
+ Stop the current worktree stack. Uses the derived Compose project name, so other worktrees are never affected. A plain `down` keeps the environment in the state registry (only refreshing its last-used time); `down --volumes` removes the registry row along with the volumes.
185
+
186
+ | Flag | Meaning |
187
+ | --- | --- |
188
+ | `--volumes` | also remove this worktree's volumes (guarded for shared databases) |
189
+ | `--allow-shared` | permit acting on the shared database |
190
+ | `--json` | suppress decorative output; print one final JSON report line |
191
+ | `--config <path>` | explicit config file path |
192
+
193
+ ### `odoo-agentic-dev reset-db`
194
+
195
+ Delete and recreate the current worktree database and filestore, terminate active database sessions first, install the initial modules, and run post-init hooks. Refuses unsafe database names.
196
+
197
+ | Flag | Meaning |
198
+ | --- | --- |
199
+ | `--allow-shared` | permit resetting the shared database |
200
+ | `--modules <list>` | comma-separated module list (defaults to the recipe's `initialModules`) |
201
+ | `--without-demo <mode>` | demo-data mode passed straight to Odoo's `--without-demo` |
202
+ | `--no-template` | full init even when a template snapshot exists (template kept) |
203
+ | `--refresh-template` | full init and take a fresh template snapshot |
204
+ | `--json` | suppress decorative output; print one final JSON report line |
205
+ | `--config <path>` | explicit config file path |
206
+
207
+ Demo data is controlled at database initialization time, not per test run. The recipe's `database.withoutDemo` sets the default: a string mode (for example `"all"`) is passed to Odoo's `--without-demo`, while `withoutDemo: false` omits the flag entirely so Odoo installs demo data. The `--without-demo <mode>` flag overrides the recipe for one reset, passing the mode string straight through to Odoo.
208
+
209
+ #### Template fast reset
210
+
211
+ The first successful full init (from `setup` or `reset-db`) is snapshotted — after the post-init hooks — as a PostgreSQL template database named `<database>__tpl` plus a filestore copy. Subsequent `reset-db` runs restore from that template in seconds instead of re-running module installation; post-init hooks are **not** re-run on restore because their effects are baked into the snapshot.
212
+
213
+ Snapshots carry a key derived from `initialModules`, `withoutDemo`, `odoo.version`, and the `postInit` hooks. Changing any of these invalidates the template: the next `reset-db` automatically falls back to a full init and takes a fresh snapshot. One-off `--modules`/`--without-demo` overrides always force a full init without touching the stored template.
214
+
215
+ - `--no-template` forces a full init for one run, keeping the existing snapshot.
216
+ - `--refresh-template` forces a full init and replaces the snapshot.
217
+ - PostgreSQL is per-stack, so templates only accelerate resets within the same worktree.
218
+ - Database names are budgeted so `<database>__tpl` fits PostgreSQL's 63-char identifier limit (derived names are capped at 58 chars); an explicitly overridden name longer than 58 chars simply skips snapshotting.
219
+
220
+ ### `odoo-agentic-dev update <modules>`
221
+
222
+ Update modules in the current worktree database. Starts PostgreSQL if needed, stops Odoo before the update when needed, and restarts Odoo after a successful update unless `--no-restart` is passed.
223
+
224
+ ```bash
225
+ pnpm exec odoo-agentic-dev update KL_setup
226
+ pnpm exec odoo-agentic-dev update KL_base,KL_sale,KL_stock
227
+ ```
228
+
229
+ | Flag | Meaning |
230
+ | --- | --- |
231
+ | `--no-restart` | do not restart Odoo after the update |
232
+ | `--json` | suppress decorative output; print one final JSON report line |
233
+ | `--config <path>` | explicit config file path |
234
+
235
+ ### `odoo-agentic-dev test`
236
+
237
+ Run Odoo tests against the current worktree database. Options map to Odoo CLI flags, the exit code is non-zero on test failure, and recipes may define reusable test profiles (`test.profiles`) selected with `--profile`.
238
+
239
+ | Flag | Meaning |
240
+ | --- | --- |
241
+ | `--tags <tags>` | Odoo `--test-tags` value |
242
+ | `--file <path>` | Odoo `--test-file` value |
243
+ | `--module <name>` | restrict tests to a module |
244
+ | `--log-level <level>` | Odoo log level |
245
+ | `--profile <name>` | recipe-defined test profile (extra Odoo args) |
246
+ | `--include-demo` | accepted for compatibility; demo data is controlled at database init in v1 (see `reset-db`) |
247
+ | `--json` | suppress decorative output; print one final JSON report line (includes `exitCode`) |
248
+ | `--config <path>` | explicit config file path |
249
+
250
+ ### `odoo-agentic-dev link-source`
251
+
252
+ Create or refresh a local Odoo source pointer such as `.odoo` (a symlink on macOS/Linux/WSL2). Resolution order: `--target`, then the recipe's `odoo.source` (unless it is `"docker-only"`) — both used as-is without validation — then discovery: the conventional `../odoo` sibling checkout, then `<dirname>/odoo` next to every git worktree of the project. A discovered candidate counts only if it looks like an Odoo source checkout (contains `odoo-bin`, `odoo/` and `addons/`); when none qualifies the error lists every candidate checked. It refuses to overwrite anything that is not a symlink, and replaces an existing symlink only with `--force`. The runtime never requires this link; it exists for IDE navigation and direct source inspection.
253
+
254
+ | Flag | Meaning |
255
+ | --- | --- |
256
+ | `--target <path>` | explicit source checkout path (absolute or relative to the project root) |
257
+ | `--name <link-name>` | link name (default `.odoo`) |
258
+ | `--force` | replace an existing symlink |
259
+ | `--config <path>` | explicit config file path |
260
+
261
+ ### `odoo-agentic-dev list`
262
+
263
+ List the environments this machine's state registry knows about, reconciled against Docker reality: status is `running`, `stopped`, or `vanished` (row exists, Docker stack is gone). Labeled Docker stacks that are missing from the registry are adopted into it on sight. Output columns: worktree, database, port, status, relative last-used time, and `shared`/`template` markers. Listing never modifies or removes environments.
264
+
265
+ | Flag | Meaning |
266
+ | --- | --- |
267
+ | `--all-projects` | every project in the registry (works without a config) |
268
+ | `--json` | print the full rows + status as JSON |
269
+ | `--config <path>` | explicit config file path |
270
+
271
+ ### `odoo-agentic-dev prune`
272
+
273
+ Garbage-collect dead environments. By default only clearly-dead targets are candidates: `gone-branch` (root dir exists, is a repo, the recorded branch was deleted), `gone-rootdir` (the worktree directory no longer exists), and `vanished` (registry row with no Docker stack — row-only cleanup). Age alone never makes a candidate unless you pass `--older-than <days>`.
274
+
275
+ The safety contract:
276
+
277
+ - Without `--yes`, `prune` is a dry run: it prints the kill list (stack, database, age, reason) and exits 1 when candidates exist, 0 when there are none. Nothing is removed.
278
+ - With `--yes`, teardown is label-based (`docker rm -f` + `docker volume rm` by Compose project label), so it works even when the original compose file is gone; the registry row is removed last.
279
+ - Shared environments are always skipped unless `--allow-shared` is passed.
280
+ - The environment a command is currently running in is never an auto-clean candidate.
281
+
282
+ | Flag | Meaning |
283
+ | --- | --- |
284
+ | `--older-than <days>` | also prune environments unused for more than `<days>` |
285
+ | `--all-projects` | every project in the registry (works without a config) |
286
+ | `--yes` | actually remove (without it: dry run, exit 1 when candidates exist) |
287
+ | `--allow-shared` | permit pruning shared environments |
288
+ | `--json` | print the candidates/removals report as JSON |
289
+ | `--config <path>` | explicit config file path |
290
+
291
+ ### `odoo-agentic-dev doctor`
292
+
293
+ Environment health report (`✓`/`✗` per check, or `--json`): Docker daemon responsive, Compose is v2, Node >= 22.15, config discovery + validation (soft when absent), context derivation, Odoo port free or its holder identified, port collisions among known stacks, state registry openable and writable, git on PATH, WSL2 detection with setup guidance, and the current prune-candidate count. Exits 1 if any hard check fails.
294
+
295
+ | Flag | Meaning |
296
+ | --- | --- |
297
+ | `--json` | print the checks array as JSON |
298
+ | `--config <path>` | explicit config file path |
299
+
300
+ ### `odoo-agentic-dev logs [service]`
301
+
302
+ Stream `docker compose logs` for one service of the current worktree stack (default: the Odoo service).
303
+
304
+ | Flag | Meaning |
305
+ | --- | --- |
306
+ | `--follow` | follow log output |
307
+ | `--config <path>` | explicit config file path |
308
+
309
+ ### `odoo-agentic-dev shell`
310
+
311
+ Open an interactive `odoo shell` against the current worktree database (`docker compose run --rm <odoo> odoo shell -d <database>`), with full TTY passthrough. The child's exit code becomes the command's exit code.
312
+
313
+ ### `odoo-agentic-dev psql [-- <args>]`
314
+
315
+ Open `psql` inside the database container connected to the current worktree database. Extra `psql` arguments pass through after `--`:
316
+
317
+ ```bash
318
+ pnpm exec odoo-agentic-dev psql -- -c 'SELECT count(*) FROM res_partner;'
319
+ ```
320
+
321
+ ### `odoo-agentic-dev run [--env-file <path>]... -- <command> [args...]`
322
+
323
+ Execute any host command with the worktree environment injected — the assembled context env (`ODOO_DATABASE`, `ODOO_HTTP_PORT`, companion ports/URLs, aliases) layered over the parent environment. Each `--env-file` (repeatable) is a dotenv-style file (`KEY=value` lines, `#` comments, optional surrounding quotes stripped) whose pairs are explicit overrides: parent env < context env < env files, later files win. The child runs with full TTY passthrough and its exit code becomes the command's exit code.
324
+
325
+ ```bash
326
+ pnpm exec odoo-agentic-dev run -- pnpm test:e2e
327
+ pnpm exec odoo-agentic-dev run --env-file .env.e2e -- playwright test
328
+ ```
329
+
330
+ | Flag | Meaning |
331
+ | --- | --- |
332
+ | `--env-file <path>` | dotenv-style overrides, repeatable (later files win; missing file = error) |
333
+ | `--config <path>` | explicit config file path |
334
+
335
+ ### `odoo-agentic-dev compose -- <compose-args...>`
336
+
337
+ `docker compose` passthrough scoped to this worktree's stack: the canonical preamble (`-p <project>`, `-f <compose-file>`, `--project-directory`, context env) is prepended and the trailing arguments land verbatim, with full stdio inheritance — `logs -f` streams, interactive `exec` works. The child's exit code becomes the command's exit code.
338
+
339
+ ```bash
340
+ pnpm exec odoo-agentic-dev compose -- ps
341
+ pnpm exec odoo-agentic-dev compose -- logs -f --tail 100 odoo
342
+ pnpm exec odoo-agentic-dev compose -- exec db bash
343
+ ```
344
+
345
+ ### `odoo-agentic-dev worktree create <name>`
346
+
347
+ Create a git worktree and run the full `setup` flow inside it: best-effort `git fetch origin`; base ref from `--base`, else `ODOO_WORKTREE_BASE_REF`, else origin's HEAD, else `HEAD`; `git worktree add -b <branchPrefix><name> <path> <base>`; copy the recipe's `worktree.copyFiles` that exist in the project root; then deps + image + database against the worktree as project root. Prints the worktree path when done.
348
+
349
+ With `--hook-json` (the Claude Code WorktreeCreate hook contract) the `{worktree_name, worktree_path}` payload is read from stdin, all human output goes to stderr, and stdout carries exactly one line — the final worktree path. If setup fails in hook mode the half-made worktree is force-removed and the command exits non-zero so the hook aborts the creation.
350
+
351
+ | Flag | Meaning |
352
+ | --- | --- |
353
+ | `--path <path>` | worktree directory (default: sibling `<repo-basename>-<name>`) |
354
+ | `--base <ref>` | base ref for the new branch |
355
+ | `--hook-json` | Claude Code hook mode (payload on stdin, path-only stdout) |
356
+ | `--config <path>` | explicit config file path |
357
+
358
+ ### `odoo-agentic-dev worktree remove <path>`
359
+
360
+ Tear down a worktree's environment. When the directory still has a discoverable config, its own context is resolved and the stack goes down with volumes (`down --volumes` semantics) before the registry row is removed; a shared database is never torn down without `--allow-shared` (logged and skipped instead). When the directory is already gone, the identity is rebuilt from the directory name against the current project root and the teardown is label-based — the same machinery `prune` uses, no compose file needed. The directory itself is not deleted (git owns that).
361
+
362
+ With `--hook-json` (the WorktreeRemove hook contract) the `{worktree_path}` payload is read from stdin and the command **always exits 0** — a removal hook cannot block — logging each step to `--log-file` (directory created) when given, stderr otherwise.
363
+
364
+ | Flag | Meaning |
365
+ | --- | --- |
366
+ | `--hook-json` | Claude Code hook mode (payload on stdin, always exit 0) |
367
+ | `--allow-shared` | permit tearing down the shared database |
368
+ | `--log-file <path>` | append step logs to this file |
369
+ | `--config <path>` | explicit config file path |
370
+
371
+ ## Machine-Readable Output (`--json`)
372
+
373
+ `info`, `list`, `doctor`, and `prune` have dedicated JSON shapes and keep stdout pure JSON (warnings go to stderr).
374
+
375
+ The lifecycle commands — `setup`, `up`, `down`, `reset-db`, `update`, `test` — accept `--json` too: decorative output is suppressed and one final single-line JSON object is printed to stdout:
376
+
377
+ ```json
378
+ { "ok": true, "command": "reset-db", "database": "kl_feature_x", "composeProject": "kl_kl_feature_x", "odooUrl": "http://127.0.0.1:18119/web?db=kl_feature_x", "actions": ["restore-from-template"], "durationMs": 4180, "exitCode": 0 }
379
+ ```
380
+
381
+ `actions` records what the command did (for `reset-db`/`setup` it includes `restore-from-template` or `full-init` + `snapshot-template`, so tooling can tell which reset path ran); `exitCode` appears when the command ran a child to completion (`test`). Streamed child output (image builds, Odoo logs) may still precede the report, so parse the **last line** of stdout (`tail -n 1 | jq`). On failure the object is emitted with `"ok": false` before the error is rendered on stderr.
382
+
383
+ ## State Registry
384
+
385
+ Every lifecycle command records its environment (compose project, database, root dir, branch, port, timestamps, template metadata) in a machine-global SQLite registry at `${XDG_DATA_HOME:-~/.local/share}/odoo-agentic-dev/state.db`, overridable via the `ODOO_AGENTIC_DEV_STATE_DB` environment variable. It needs no setup and no external dependency (built-in `node:sqlite`, hence Node >= 22.15).
386
+
387
+ The registry is an index — Docker is the truth. Generated compose files stamp `dev.basaltbytes.oad.*` identity labels on services and volumes so `list`/`prune`/`doctor` can reconcile rows against reality and re-adopt stacks whose rows were lost. Project-supplied compose files (recipe `compose.file`) are not labeled; those environments are tracked by their registry rows alone.
388
+
389
+ ## Automatic Cleanup
390
+
391
+ ```ts
392
+ cleanup: {
393
+ maxAgeDays: 30, // staleness threshold used by the auto/warn hook
394
+ auto: false // false (default): warn only; true: prune automatically
395
+ }
396
+ ```
397
+
398
+ At the end of `up` and `setup`, the registry is checked for dead environments of the current project. With `auto: false` (the default) a one-line warning is printed when candidates exist (`N stale environment(s) — run odoo-agentic-dev prune`). With `auto: true` the prune routine runs immediately for gone/vanished environments and those unused for more than `maxAgeDays`, never touching shared environments or the environment the command is running in, and prints what it removed.
399
+
400
+ ## Network Exposure
401
+
402
+ The generated Compose file binds Odoo to the loopback interface only (`127.0.0.1:<port>:8069`): another machine on your LAN can never reach a dev Odoo by default. To expose a stack deliberately, supply your own compose file via the recipe's `compose.file` with the port mapping you want (for example `"0.0.0.0:8069:8069"`) — deliberate exposure is a project decision, not a CLI flag.
403
+
404
+ Every compose subprocess runs with the full context env exported (the same variables `info --env` prints, merged over the parent environment), so a project-supplied compose file can interpolate `${ODOO_DATABASE:?}` or `${ODOO_HTTP_PORT:?}` directly.
405
+
406
+ ## Environment Variables
407
+
408
+ | Variable | Meaning |
409
+ | --- | --- |
410
+ | `ODOO_DATABASE` | Current worktree Odoo database |
411
+ | `E2E_ODOO_DB` | Alias for test tooling |
412
+ | `ODOO_BASE_URL` | Odoo HTTP origin |
413
+ | `ODOO_HTTP_PORT` | Host port for Odoo |
414
+ | `ODOO_COMPOSE_PROJECT_NAME` | Docker Compose project |
415
+ | `ODOO_WORKTREE_NAME` | Override for derived worktree name |
416
+ | `ODOO_WORKTREE_CONFIG` | Config file path override |
417
+ | `ODOO_AGENTIC_DEV_STATE_DB` | State registry path override (default `${XDG_DATA_HOME:-~/.local/share}/odoo-agentic-dev/state.db`) |
418
+
419
+ Explicit env overrides win over derived values, but `E2E_ODOO_DB` and `ODOO_DATABASE` must not disagree.
420
+
421
+ Companion apps contribute their own variables to the context env: `portEnv` receives the allocated port and `urlEnv` receives `http://localhost:<port>`; both show up in `info --env` and `info --json`. Compatibility aliases for existing projects (for example `KL_WORKTREE_DB_NAME`) can be declared in the recipe via `envAliases`: an alias may target **any** assembled env key — canonical or companion-provided (`E2E_PWA_PORT: "PWA_PORT"` works) — and an alias targeting an unknown key fails validation listing the available keys.
422
+
423
+ ## Safety Rules
424
+
425
+ - A shared database (the recipe's `project.sharedDatabase`, used by `project.sharedBranches`) is never deleted without `--allow-shared`.
426
+ - Database names outside the safe pattern `^[a-z][a-z0-9_]*$` (max 63 chars) are rejected.
427
+ - Destructive actions never run with an empty Compose project name.
428
+ - `link-source` never overwrites a non-symlink path.
429
+ - The resolved database and Compose project are printed before destructive work.
430
+ - Config validation failures fail closed: no command proceeds on an invalid recipe.
431
+ - Confirmations are flags-only — there are no interactive prompts, so non-interactive agents never hang waiting for input.
432
+ - `prune` is a dry run unless `--yes` is passed, skips shared environments unless `--allow-shared`, and only ages out environments when `--older-than` is given.
433
+ - `up` fails fast on a port conflict instead of silently probing for another port — same branch, same port, always.
434
+ - Generated stacks bind Odoo to `127.0.0.1` only; LAN exposure requires a deliberate project-supplied compose file.
435
+
436
+ ## Post-Init Hooks
437
+
438
+ `database.postInit` hooks run after the initial modules are installed, in the order they are declared:
439
+
440
+ ```ts
441
+ type PostInitHook =
442
+ | { type: "odoo-shell-file"; file: string } // run a Python file in `odoo shell`
443
+ | { type: "odoo-shell-inline"; code: string } // run inline Python in `odoo shell`
444
+ | { type: "set-ir-config-parameter"; key: string; value: string }
445
+ | { type: "command"; command: string; args: ReadonlyArray<string>; cwd?: string };
446
+ ```
447
+
448
+ Hook files resolve relative to the project root. Only `set-ir-config-parameter` commits automatically (it runs `env.cr.commit()` for you); `odoo-shell-file` and `odoo-shell-inline` scripts must call `env.cr.commit()` themselves or their changes are rolled back when the shell exits. Prefer `odoo-shell-file` and `set-ir-config-parameter`; inline code is harder to review.
449
+
450
+ ## Development
451
+
452
+ ```bash
453
+ pnpm install
454
+ pnpm build # compile to dist/
455
+ pnpm test # unit + e2e tests (e2e runs against dist/cli.js when built)
456
+ pnpm typecheck # tsc --noEmit over src + test
457
+ pnpm lint # oxlint
458
+ pnpm format # oxfmt --write
459
+ pnpm format:check # oxfmt --check
460
+ ```
461
+
462
+ The test suite never requires Docker, Git state, or a network connection — adapters are faked, and every state-touching test pins `ODOO_AGENTIC_DEV_STATE_DB` to a temp file so your real registry is never touched. `scripts/docker-integration.sh` is a separate minimal Docker integration check (compose file generation + validation against a real Docker) used by the Linux CI job; it is not part of `pnpm test`.
463
+
464
+ CI runs lint/typecheck/build/test on Linux, macOS, and Windows across Node 22 and 24 (the Windows job exercises the dry-run unit suite only — no Docker), plus the Docker integration job on Linux. A nightly workflow (`.github/workflows/nightly.yml`, also runnable via `workflow_dispatch`) exercises the real `odoo:18` + `postgres:16` lifecycle end to end: `setup` with a template snapshot, `reset-db` down the template restore path, `update base`, and a fully clean `down --volumes`.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "./suppress-sqlite-warning.js";
package/dist/cli.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ // must stay first: installs the node:sqlite ExperimentalWarning filter before
3
+ // any import that could transitively load node:sqlite (imports hoist)
4
+ import "./suppress-sqlite-warning.js";
5
+ import { Cause, Console, Effect, Layer } from "effect";
6
+ import { CliError, Command } from "effect/unstable/cli";
7
+ import { NodeRuntime, NodeServices } from "@effect/platform-node";
8
+ import { infoCommand } from "./commands/info.js";
9
+ import { setupCommand } from "./commands/setup.js";
10
+ import { upCommand } from "./commands/up.js";
11
+ import { downCommand } from "./commands/down.js";
12
+ import { resetDbCommand } from "./commands/reset-db.js";
13
+ import { updateCommand } from "./commands/update.js";
14
+ import { testCommand } from "./commands/test.js";
15
+ import { linkSourceCommand } from "./commands/link-source.js";
16
+ import { listCommand } from "./commands/list.js";
17
+ import { pruneCommand } from "./commands/prune.js";
18
+ import { doctorCommand } from "./commands/doctor.js";
19
+ import { logsCommand } from "./commands/logs.js";
20
+ import { shellCommand } from "./commands/shell.js";
21
+ import { psqlCommand } from "./commands/psql.js";
22
+ import { runCommand } from "./commands/run.js";
23
+ import { composeCommand } from "./commands/compose.js";
24
+ import { worktreeCommand } from "./commands/worktree.js";
25
+ import { CommandRunnerLive } from "./platform/command-runner.js";
26
+ import { GitLive } from "./platform/git.js";
27
+ import { DockerComposeLive } from "./platform/docker-compose.js";
28
+ import { OdooLifecycleLive } from "./platform/odoo-lifecycle.js";
29
+ import { ProcessSupervisorLive } from "./platform/process-supervisor.js";
30
+ import { StateStoreLive } from "./platform/state-store.js";
31
+ import { PortProbeLive } from "./platform/port-probe.js";
32
+ import { isRuntimeError, renderError } from "./errors/errors.js";
33
+ const root = Command.make("odoo-agentic-dev").pipe(Command.withDescription("Agent-friendly local Odoo development runtime"), Command.withSubcommands([
34
+ infoCommand,
35
+ setupCommand,
36
+ upCommand,
37
+ downCommand,
38
+ resetDbCommand,
39
+ updateCommand,
40
+ testCommand,
41
+ linkSourceCommand,
42
+ listCommand,
43
+ pruneCommand,
44
+ doctorCommand,
45
+ logsCommand,
46
+ shellCommand,
47
+ psqlCommand,
48
+ runCommand,
49
+ composeCommand,
50
+ worktreeCommand,
51
+ ]));
52
+ const services = Layer.mergeAll(GitLive, OdooLifecycleLive, ProcessSupervisorLive, StateStoreLive, PortProbeLive).pipe(Layer.provideMerge(DockerComposeLive), Layer.provideMerge(CommandRunnerLive), Layer.provideMerge(NodeServices.layer));
53
+ const program = Command.runWith(root, { version: "0.1.0-beta.1" })(process.argv.slice(2)).pipe(Effect.provide(services), Effect.catch((error) => Effect.gen(function* () {
54
+ // ShowHelp is the cli library's control-flow signal: by the time it
55
+ // reaches us the help text is already printed (bare invocation or
56
+ // unknown subcommand), so the error itself must not be rendered.
57
+ const helpRequested = CliError.isCliError(error) && error._tag === "ShowHelp";
58
+ if (!helpRequested) {
59
+ yield* Console.error(isRuntimeError(error) ? renderError(error) : String(error));
60
+ }
61
+ process.exitCode = 1;
62
+ })),
63
+ // Defects (Die causes, thrown TypeErrors, ...) are not on the typed channel
64
+ // above and runMain's reporting is disabled, so render them ourselves
65
+ // instead of exiting silently. Placed after Effect.provide so defects raised
66
+ // during layer construction are caught too.
67
+ Effect.catchDefect((defect) => Effect.gen(function* () {
68
+ yield* Console.error("Unexpected internal error — this is a bug in odoo-agentic-dev, please report it:");
69
+ yield* Console.error(Cause.pretty(Cause.die(defect)));
70
+ process.exitCode = 1;
71
+ })));
72
+ NodeRuntime.runMain(program, { disableErrorReporting: true });
73
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { Effect } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { OdooAgenticDevConfig } from "../core/project-recipe.js";
4
+ import type { WorktreeContext } from "../core/worktree-context.js";
5
+ import type { RuntimeError } from "../errors/errors.js";
6
+ import type { CommandRunnerApi } from "../platform/command-runner.js";
7
+ import type { DockerComposeApi } from "../platform/docker-compose.js";
8
+ import type { StateStoreApi } from "../platform/state-store.js";
9
+ /**
10
+ * `docker compose <trailing args>` against this worktree's stack: the
11
+ * canonical preamble (-p/-f/--project-directory + context env) is prepended,
12
+ * the trailing args land verbatim. Full stdio inheritance so `logs -f`
13
+ * streams and interactive `exec` works; the child's exit code is propagated.
14
+ */
15
+ export declare const runCompose: (recipe: OdooAgenticDevConfig, ctx: WorktreeContext, args: ReadonlyArray<string>) => Effect.Effect<number, RuntimeError, DockerComposeApi | CommandRunnerApi | StateStoreApi>;
16
+ export declare const composeCommand: Command.Command<"compose", {
17
+ readonly args: readonly string[];
18
+ readonly config: import("effect/Option").Option<string>;
19
+ }, {}, RuntimeError, CommandRunnerApi | import("../platform/git.js").GitApi | DockerComposeApi | StateStoreApi>;
@@ -0,0 +1,29 @@
1
+ import { Effect } from "effect";
2
+ import { Argument, Command, Flag } from "effect/unstable/cli";
3
+ import { ConfigValidationError } from "../errors/errors.js";
4
+ import { trailingOperands } from "./trailing-args.js";
5
+ import { runInteractivePassthrough } from "./shell.js";
6
+ import { resolveContext } from "./resolve-context.js";
7
+ /**
8
+ * `docker compose <trailing args>` against this worktree's stack: the
9
+ * canonical preamble (-p/-f/--project-directory + context env) is prepended,
10
+ * the trailing args land verbatim. Full stdio inheritance so `logs -f`
11
+ * streams and interactive `exec` works; the child's exit code is propagated.
12
+ */
13
+ export const runCompose = (recipe, ctx, args) => runInteractivePassthrough(recipe, ctx, args);
14
+ export const composeCommand = Command.make("compose", {
15
+ args: Argument.string("args").pipe(Argument.variadic(), Argument.withDescription("docker compose arguments (pass them after --)")),
16
+ config: Flag.string("config").pipe(Flag.optional),
17
+ }, (flags) => Effect.gen(function* () {
18
+ const args = [...flags.args, ...trailingOperands()];
19
+ if (args.length === 0) {
20
+ return yield* Effect.fail(new ConfigValidationError({
21
+ issues: [
22
+ "compose requires docker compose arguments, e.g. `odoo-agentic-dev compose -- logs -f`",
23
+ ],
24
+ }));
25
+ }
26
+ const { ctx, recipe } = yield* resolveContext(flags.config);
27
+ yield* runCompose(recipe, ctx, args);
28
+ })).pipe(Command.withDescription("docker compose passthrough scoped to this worktree's compose project"));
29
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/commands/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAItD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,MAA4B,EAC5B,GAAoB,EACpB,IAA2B,EAC+D,EAAE,CAC5F,yBAAyB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CACxC,SAAS,EACT;IACE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAChC,QAAQ,CAAC,QAAQ,EAAE,EACnB,QAAQ,CAAC,eAAe,CAAC,+CAA+C,CAAC,CAC1E;IACD,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;CAClD,EACD,CAAC,KAAK,EAAE,EAAE,CACR,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,gBAAgB,EAAE,CAAC,CAAC;IACpD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,qBAAqB,CAAC;YACxB,MAAM,EAAE;gBACN,uFAAuF;aACxF;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5D,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC,CAAC,CACL,CAAC,IAAI,CACJ,OAAO,CAAC,eAAe,CAAC,sEAAsE,CAAC,CAChG,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { Effect, Option } from "effect";
2
+ import { Command } from "effect/unstable/cli";
3
+ import type { CommandRunnerApi } from "../platform/command-runner.js";
4
+ import type { DockerComposeApi } from "../platform/docker-compose.js";
5
+ import type { GitApi } from "../platform/git.js";
6
+ import type { PortProbeApi } from "../platform/port-probe.js";
7
+ import type { StateStoreApi } from "../platform/state-store.js";
8
+ export type DoctorCheck = {
9
+ readonly name: string;
10
+ readonly ok: boolean;
11
+ /** hard failures drive the exit code; soft ones are informational */
12
+ readonly hard: boolean;
13
+ readonly detail: string;
14
+ };
15
+ /** Engines floor: Node >= 22.15 (first unflagged node:sqlite). */
16
+ export declare const nodeVersionOk: (version: string) => boolean;
17
+ /**
18
+ * "Compose v2" means the Go `docker compose` plugin (the python v1
19
+ * `docker-compose` is unsupported). Plugin releases moved past v2 numbering
20
+ * (v5.x today), so accept any major >= 2 instead of grepping for "v2".
21
+ */
22
+ export declare const composeVersionOk: (stdout: string) => boolean;
23
+ export declare const detectWsl: (procVersion: string | null) => boolean;
24
+ export declare const hasHardFailure: (checks: ReadonlyArray<DoctorCheck>) => boolean;
25
+ export declare const formatDoctorReport: (checks: ReadonlyArray<DoctorCheck>) => string;
26
+ /**
27
+ * Run every doctor probe and fold each outcome — success or failure — into a
28
+ * check row. This never fails: a broken docker/state/config is a finding, not
29
+ * an error. The exit-code decision belongs to the command via hasHardFailure.
30
+ */
31
+ export declare const collectDoctorChecks: (explicitConfigPath: string | undefined) => Effect.Effect<ReadonlyArray<DoctorCheck>, never, CommandRunnerApi | DockerComposeApi | StateStoreApi | PortProbeApi | GitApi>;
32
+ export declare const doctorCommand: Command.Command<"doctor", {
33
+ readonly json: boolean;
34
+ readonly config: Option.Option<string>;
35
+ }, {}, never, CommandRunnerApi | GitApi | DockerComposeApi | StateStoreApi | PortProbeApi>;