From cd89bac2e917266a9a6bfdb376a3933d9a14a987 Mon Sep 17 00:00:00 2001 From: Philippe L'ATTENTION Date: Fri, 11 Sep 2026 12:03:37 +0400 Subject: [PATCH 1/2] Pin Effect release candidates to restore fresh npm installs Floating prerelease ranges pulled incompatible CLI APIs and a platform adapter whose Effect peer has not been published. Pin all three Effect packages to the tested rc.112 release and prepare beta.20. --- README.md | 2 ++ package.json | 7 ++++--- pnpm-lock.yaml | 7 +++++-- scripts/npm-smoke.sh | 2 ++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9c7fc40..2e24fc9 100644 --- a/README.md +++ b/README.md @@ -688,4 +688,6 @@ pnpm format:check # oxfmt --check 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`. `pnpm package:smoke` installs the packed tarball with npm in a temporary project, so it may need registry access to resolve runtime dependencies. +The Effect release candidates (`effect`, `@effect/platform-node`, and `@effect/platform-node-shared`) are pinned to the same tested version. The shared platform package is a direct dependency to constrain the Node adapter's transitive dependency during consumer installs. Upgrade all three together and run `pnpm package:smoke`: release candidates can change CLI APIs, and consumer installs resolve dependencies independently of this repository's lockfile. + 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 Docker integration and packed npm install smoke jobs 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`. diff --git a/package.json b/package.json index 40df0c4..8d616c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@basaltbytes/odoo-agentic-dev", - "version": "0.1.0-beta.19", + "version": "0.1.0-beta.20", "description": "Agent-friendly local Odoo development runtime: worktree-isolated databases, ports, and Docker Compose stacks", "keywords": [ "agentic", @@ -56,8 +56,9 @@ "prepublishOnly": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm package:smoke" }, "dependencies": { - "@effect/platform-node": "^4.0.0-rc.112", - "effect": "^4.0.0-rc.112", + "@effect/platform-node": "4.0.0-rc.112", + "@effect/platform-node-shared": "4.0.0-rc.112", + "effect": "4.0.0-rc.112", "jiti": "^2.7.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5cc989..66cd8e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -208,10 +208,13 @@ importers: .: dependencies: '@effect/platform-node': - specifier: ^4.0.0-rc.112 + specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + '@effect/platform-node-shared': + specifier: 4.0.0-rc.112 + version: 4.0.0-rc.112(effect@4.0.0-rc.112) effect: - specifier: ^4.0.0-rc.112 + specifier: 4.0.0-rc.112 version: 4.0.0-rc.112 jiti: specifier: ^2.7.0 diff --git a/scripts/npm-smoke.sh b/scripts/npm-smoke.sh index 8ab8b89..ab5a0d4 100755 --- a/scripts/npm-smoke.sh +++ b/scripts/npm-smoke.sh @@ -37,6 +37,8 @@ cd "$PROJECT" npm init -y >/dev/null npm install --ignore-scripts --no-audit --no-fund "$TARBALL" >/dev/null +# Show the fresh resolution and reject invalid peers before exercising the CLI. +npm ls --all effect @effect/platform-node @effect/platform-node-shared BIN="$PROJECT/node_modules/.bin/oad" From b4a0fffbd21cd9df71b93c76bc1e711c8c5b73a5 Mon Sep 17 00:00:00 2001 From: Philippe L'ATTENTION Date: Fri, 11 Sep 2026 16:12:22 +0400 Subject: [PATCH 2/2] Upgrade to Effect rc.113 and migrate CLI constructors --- package.json | 6 +- pnpm-lock.yaml | 152 ++++++------------------------------ src/commands/compose.ts | 4 +- src/commands/doctor.ts | 6 +- src/commands/down.ts | 8 +- src/commands/eject.ts | 14 ++-- src/commands/info.ts | 6 +- src/commands/init.ts | 10 +-- src/commands/link-source.ts | 10 +-- src/commands/list.ts | 6 +- src/commands/logs.ts | 6 +- src/commands/prune.ts | 16 ++-- src/commands/psql.ts | 4 +- src/commands/reset-db.ts | 16 ++-- src/commands/restart.ts | 8 +- src/commands/run.ts | 6 +- src/commands/setup.ts | 16 ++-- src/commands/shell.ts | 2 +- src/commands/test.ts | 18 ++--- src/commands/up.ts | 14 ++-- src/commands/update.ts | 10 +-- src/commands/worktree.ts | 20 ++--- test/smoke.test.ts | 4 +- 23 files changed, 129 insertions(+), 233 deletions(-) diff --git a/package.json b/package.json index 8d616c4..a85ed63 100644 --- a/package.json +++ b/package.json @@ -56,9 +56,9 @@ "prepublishOnly": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm package:smoke" }, "dependencies": { - "@effect/platform-node": "4.0.0-rc.112", - "@effect/platform-node-shared": "4.0.0-rc.112", - "effect": "4.0.0-rc.112", + "@effect/platform-node": "4.0.0-rc.113", + "@effect/platform-node-shared": "4.0.0-rc.113", + "effect": "4.0.0-rc.113", "jiti": "^2.7.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 66cd8e7..06e150b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -208,14 +208,14 @@ importers: .: dependencies: '@effect/platform-node': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + specifier: 4.0.0-rc.113 + version: 4.0.0-rc.113(effect@4.0.0-rc.113)(redis@6.2.1) '@effect/platform-node-shared': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112) + specifier: 4.0.0-rc.113 + version: 4.0.0-rc.113(effect@4.0.0-rc.113) effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.0-rc.113 + version: 4.0.0-rc.113 jiti: specifier: ^2.7.0 version: 2.7.0 @@ -241,17 +241,17 @@ importers: packages: - '@effect/platform-node-shared@4.0.0-rc.112': - resolution: {integrity: sha512-ttjz0xKamFN7vL8pNDYVwddJLjZvqKePc05djlz2VcdaKbLsnYbtMnL1rbOfHgEnIUSHGh7FkjaN4DM1Ov81sQ==} + '@effect/platform-node-shared@4.0.0-rc.113': + resolution: {integrity: sha512-EPCfjvKlHmNDgqIlT85P1OcgNUNvRxNtBpTFOz1UGHwNJe6Ap7rP93Rue7kzFUeplQMy6EfoZEH4CJR4N0nJ1g==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.0-rc.113 - '@effect/platform-node@4.0.0-rc.112': - resolution: {integrity: sha512-/BMAcdNGQQskLmI0Zoa95KfTZkr9HV9N4NSxaSrusG6GeW6Ulp9KvZ+Rlaiw8lnOt43CXjFLdfll5/k5rxL4hQ==} + '@effect/platform-node@4.0.0-rc.113': + resolution: {integrity: sha512-8luSE2JcGVsALQ6cZOWh5jdZu+VgcUKIKPfhN/MjX+ZzIuK/krm3jZBHYZdhqkXjM9s8aYGBxNMHGF0WzF+Gpg==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.0-rc.113 redis: '>=5.0.0 <7.0.0' '@emnapi/core@1.10.0': @@ -266,36 +266,6 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} - cpu: [x64] - os: [win32] - '@napi-rs/wasm-runtime@1.1.5': resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: @@ -752,8 +722,8 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - effect@4.0.0-rc.112: - resolution: {integrity: sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==} + effect@4.0.0-rc.113: + resolution: {integrity: sha512-uZ4Bh7KSlw2sdVdNDHPLSNjLGH+MWap7cheIHZoB/1T/ZJ3pvI4KiN3kUp50MpFKXhygOMmReJJOwWMQPwpPAg==} es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} @@ -765,10 +735,6 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - fast-check@4.9.0: - resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} - engines: {node: '>=12.17.0'} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -864,27 +830,11 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - mime@4.1.0: - resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} - engines: {node: '>=16'} - hasBin: true - - msgpackr-extract@3.0.4: - resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} - hasBin: true - - msgpackr@2.0.6: - resolution: {integrity: sha512-plGul/tqjt9vqWFR9zyqyLZls6gb5KTLvnsRO2B9+TZ8tNiXiVI/CR8LiDWlAX4IUeVkQ9gsmzKA6voC2QOciw==} - nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - obug@2.1.2: resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} engines: {node: '>=12.20.0'} @@ -929,9 +879,6 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} - pure-rand@8.4.2: - resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} - redis@6.2.1: resolution: {integrity: sha512-Z9VHtgYs48PiQC77X9O2Er8Hj4T+5BtFjT91/vi5Is1D04N72cA946ZslM1ImJw8ZctFBZWAVjM7S5wJNeHMpg==} engines: {node: '>= 20.0.0'} @@ -984,8 +931,8 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@8.10.0: - resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} + undici@8.10.2: + resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} engines: {node: '>=22.19.0'} vite@8.0.16: @@ -1096,22 +1043,21 @@ packages: snapshots: - '@effect/platform-node-shared@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/platform-node-shared@4.0.0-rc.113(effect@4.0.0-rc.113)': dependencies: '@types/ws': 8.18.1 - effect: 4.0.0-rc.112 + effect: 4.0.0-rc.113 ws: 8.21.3 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1)': + '@effect/platform-node@4.0.0-rc.113(effect@4.0.0-rc.113)(redis@6.2.1)': dependencies: - '@effect/platform-node-shared': 4.0.0-rc.112(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 - mime: 4.1.0 + '@effect/platform-node-shared': 4.0.0-rc.113(effect@4.0.0-rc.113) + effect: 4.0.0-rc.113 redis: 6.2.1 - undici: 8.10.0 + undici: 8.10.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -1134,24 +1080,6 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - optional: true - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -1421,10 +1349,7 @@ snapshots: detect-libc@2.1.2: {} - effect@4.0.0-rc.112: - dependencies: - fast-check: 4.9.0 - msgpackr: 2.0.6 + effect@4.0.0-rc.113: {} es-module-lexer@2.1.0: {} @@ -1434,10 +1359,6 @@ snapshots: expect-type@1.3.0: {} - fast-check@4.9.0: - dependencies: - pure-rand: 8.4.2 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -1500,31 +1421,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - mime@4.1.0: {} - - msgpackr-extract@3.0.4: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 - optional: true - - msgpackr@2.0.6: - optionalDependencies: - msgpackr-extract: 3.0.4 - nanoid@3.3.12: {} - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.1.2 - optional: true - obug@2.1.2: {} oxfmt@0.54.0: @@ -1585,8 +1483,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - pure-rand@8.4.2: {} - redis@6.2.1: dependencies: '@redis/bloom': 6.2.1(@redis/client@6.2.1) @@ -1647,7 +1543,7 @@ snapshots: undici-types@7.24.6: {} - undici@8.10.0: {} + undici@8.10.2: {} vite@8.0.16(@types/node@25.9.2)(jiti@2.7.0)(yaml@2.9.0): dependencies: diff --git a/src/commands/compose.ts b/src/commands/compose.ts index 2e34132..deacd15 100644 --- a/src/commands/compose.ts +++ b/src/commands/compose.ts @@ -27,11 +27,11 @@ export const runCompose = ( export const composeCommand = Command.make( "compose", { - args: Argument.string("args").pipe( + args: Argument.String("args").pipe( Argument.variadic(), Argument.withDescription("docker compose arguments (pass them after --)"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index 3472372..cc97e9a 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -542,15 +542,15 @@ export const collectDoctorChecks = ( export const doctorCommand = Command.make( "doctor", { - deep: Flag.boolean("deep").pipe( + deep: Flag.Boolean("deep").pipe( Flag.withDefault(false), Flag.withDescription("run slower container probes such as browser-test dependency checks"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/down.ts b/src/commands/down.ts index b6e463e..0eeef52 100644 --- a/src/commands/down.ts +++ b/src/commands/down.ts @@ -76,16 +76,16 @@ export const finalizeDownState = ( export const downCommand = Command.make( "down", { - volumes: Flag.boolean("volumes").pipe( + volumes: Flag.Boolean("volumes").pipe( Flag.withDefault(false), Flag.withDescription("also remove this worktree's volumes"), ), - allowShared: Flag.boolean("allow-shared").pipe(Flag.withDefault(false)), - json: Flag.boolean("json").pipe( + allowShared: Flag.Boolean("allow-shared").pipe(Flag.withDefault(false)), + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("down", flags.json, (report) => diff --git a/src/commands/eject.ts b/src/commands/eject.ts index ff538f3..eb85802 100644 --- a/src/commands/eject.ts +++ b/src/commands/eject.ts @@ -410,31 +410,31 @@ const parseTarget = (raw: string | undefined): Effect.Effect export const infoCommand = Command.make( "info", { - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), - env: Flag.boolean("env").pipe( + env: Flag.Boolean("env").pipe( Flag.withDefault(false), Flag.withDescription("print KEY=value env lines"), ), - config: Flag.string("config").pipe( + config: Flag.String("config").pipe( Flag.optional, Flag.withDescription("explicit config file path"), ), diff --git a/src/commands/init.ts b/src/commands/init.ts index f58db7b..b0c6329 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -162,23 +162,23 @@ const NEXT_STEPS: ReadonlyArray = [ export const initCommand = Command.make( "init", { - id: Flag.string("id").pipe( + id: Flag.String("id").pipe( Flag.optional, Flag.withDescription("project id (default: derived from the folder name)"), ), - dbPrefix: Flag.string("db-prefix").pipe( + dbPrefix: Flag.String("db-prefix").pipe( Flag.optional, Flag.withDescription("database prefix (default: derived from the project id)"), ), - odooVersion: Flag.string("odoo-version").pipe( + odooVersion: Flag.String("odoo-version").pipe( Flag.withDefault(DEFAULT_ODOO_VERSION), Flag.withDescription(`Odoo version (default ${DEFAULT_ODOO_VERSION})`), ), - force: Flag.boolean("force").pipe( + force: Flag.Boolean("force").pipe( Flag.withDefault(false), Flag.withDescription("overwrite an existing config in the current directory"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), diff --git a/src/commands/link-source.ts b/src/commands/link-source.ts index 87fdbfc..6444e09 100644 --- a/src/commands/link-source.ts +++ b/src/commands/link-source.ts @@ -148,14 +148,14 @@ export const performLinkSource = (options: { export const linkSourceCommand = Command.make( "link-source", { - target: Flag.string("target").pipe(Flag.optional), - name: Flag.string("name").pipe(Flag.withDefault(".odoo")), - force: Flag.boolean("force").pipe(Flag.withDefault(false)), - json: Flag.boolean("json").pipe( + target: Flag.String("target").pipe(Flag.optional), + name: Flag.String("name").pipe(Flag.withDefault(".odoo")), + force: Flag.Boolean("force").pipe(Flag.withDefault(false)), + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("link-source", flags.json, (report) => diff --git a/src/commands/list.ts b/src/commands/list.ts index e789b5c..5047f53 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -135,15 +135,15 @@ export const collectListEntries = ( export const listCommand = Command.make( "list", { - allProjects: Flag.boolean("all-projects").pipe( + allProjects: Flag.Boolean("all-projects").pipe( Flag.withDefault(false), Flag.withDescription("every project in the registry (works without a config)"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/logs.ts b/src/commands/logs.ts index bdbdd4f..4236d0c 100644 --- a/src/commands/logs.ts +++ b/src/commands/logs.ts @@ -32,15 +32,15 @@ export const runLogs = ( export const logsCommand = Command.make( "logs", { - service: Argument.string("service").pipe( + service: Argument.String("service").pipe( Argument.optional, Argument.withDescription("compose service (defaults to the odoo service)"), ), - follow: Flag.boolean("follow").pipe( + follow: Flag.Boolean("follow").pipe( Flag.withDefault(false), Flag.withDescription("follow log output"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/prune.ts b/src/commands/prune.ts index ef676f6..c993273 100644 --- a/src/commands/prune.ts +++ b/src/commands/prune.ts @@ -204,32 +204,32 @@ export const reportPrune = ( export const pruneCommand = Command.make( "prune", { - olderThan: Flag.integer("older-than").pipe( + olderThan: Flag.Int("older-than").pipe( Flag.optional, Flag.withDescription("also prune environments unused for more than "), ), - allProjects: Flag.boolean("all-projects").pipe( + allProjects: Flag.Boolean("all-projects").pipe( Flag.withDefault(false), Flag.withDescription("every project in the registry (works without a config)"), ), - yes: Flag.boolean("yes").pipe( + yes: Flag.Boolean("yes").pipe( Flag.withDefault(false), Flag.withDescription("actually remove (without it: dry run, exit 1 when candidates exist)"), ), - buildCache: Flag.boolean("build-cache").pipe( + buildCache: Flag.Boolean("build-cache").pipe( Flag.withDefault(false), Flag.withDescription("also prune the Docker build cache down to --keep-storage (with --yes)"), ), - keepStorage: Flag.string("keep-storage").pipe( + keepStorage: Flag.String("keep-storage").pipe( Flag.optional, Flag.withDescription("build-cache size floor to keep (default 10GB)"), ), - allowShared: Flag.boolean("allow-shared").pipe(Flag.withDefault(false)), - json: Flag.boolean("json").pipe( + allowShared: Flag.Boolean("allow-shared").pipe(Flag.withDefault(false)), + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("print machine-readable JSON"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/psql.ts b/src/commands/psql.ts index f709d37..5a78dcc 100644 --- a/src/commands/psql.ts +++ b/src/commands/psql.ts @@ -24,11 +24,11 @@ export const buildPsqlArgs = ( export const psqlCommand = Command.make( "psql", { - args: Argument.string("args").pipe( + args: Argument.String("args").pipe( Argument.variadic(), Argument.withDescription("extra psql arguments (pass them after --)"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/reset-db.ts b/src/commands/reset-db.ts index abd0fba..c5abb08 100644 --- a/src/commands/reset-db.ts +++ b/src/commands/reset-db.ts @@ -184,29 +184,29 @@ export const ensureFreshTemplateForTests = ( export const resetDbCommand = Command.make( "reset-db", { - allowShared: Flag.boolean("allow-shared").pipe(Flag.withDefault(false)), - build: Flag.boolean("build").pipe( + allowShared: Flag.Boolean("allow-shared").pipe(Flag.withDefault(false)), + build: Flag.Boolean("build").pipe( Flag.withDefault(false), Flag.withDescription("rebuild the Odoo image before running reset containers"), ), - modules: Flag.string("modules").pipe( + modules: Flag.String("modules").pipe( Flag.optional, Flag.withDescription("comma-separated module list (defaults to recipe initialModules)"), ), - withoutDemo: Flag.string("without-demo").pipe(Flag.optional), - noTemplate: Flag.boolean("no-template").pipe( + withoutDemo: Flag.String("without-demo").pipe(Flag.optional), + noTemplate: Flag.Boolean("no-template").pipe( Flag.withDefault(false), Flag.withDescription("full init even when a template snapshot exists (template kept)"), ), - refreshTemplate: Flag.boolean("refresh-template").pipe( + refreshTemplate: Flag.Boolean("refresh-template").pipe( Flag.withDefault(false), Flag.withDescription("full init and take a fresh template snapshot"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("reset-db", flags.json, (report) => diff --git a/src/commands/restart.ts b/src/commands/restart.ts index 2dcdf23..bd84290 100644 --- a/src/commands/restart.ts +++ b/src/commands/restart.ts @@ -131,19 +131,19 @@ export const runRestart = ( export const restartCommand = Command.make( "restart", { - rebuild: Flag.boolean("rebuild").pipe( + rebuild: Flag.Boolean("rebuild").pipe( Flag.withDefault(false), Flag.withDescription("rebuild the Odoo image, remove the Odoo container, and recreate it"), ), - logs: Flag.boolean("logs").pipe( + logs: Flag.Boolean("logs").pipe( Flag.withDefault(false), Flag.withDescription("follow Odoo logs after restart"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("restart", flags.json, (report) => diff --git a/src/commands/run.ts b/src/commands/run.ts index e848d3e..3f8185d 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -88,17 +88,17 @@ export const runHostCommand = ( export const runCommand = Command.make( "run", { - argv: Argument.string("command").pipe( + argv: Argument.String("command").pipe( Argument.variadic(), Argument.withDescription("host command and its arguments (pass them after --)"), ), - envFile: Flag.string("env-file").pipe( + envFile: Flag.String("env-file").pipe( Flag.atLeast(0), Flag.withDescription( "dotenv-style file layered over the worktree env (repeatable; later files win)", ), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 2319eb6..4a3da5f 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -133,26 +133,26 @@ export const runSetup = ( export const setupCommand = Command.make( "setup", { - skipInstall: Flag.boolean("skip-install").pipe(Flag.withDefault(false)), - skipDb: Flag.boolean("skip-db").pipe(Flag.withDefault(false)), - allowShared: Flag.boolean("allow-shared").pipe(Flag.withDefault(false)), - noTemplate: Flag.boolean("no-template").pipe( + skipInstall: Flag.Boolean("skip-install").pipe(Flag.withDefault(false)), + skipDb: Flag.Boolean("skip-db").pipe(Flag.withDefault(false)), + allowShared: Flag.Boolean("allow-shared").pipe(Flag.withDefault(false)), + noTemplate: Flag.Boolean("no-template").pipe( Flag.withDefault(false), Flag.withDescription("full init even when a template snapshot exists (template kept)"), ), - refreshTemplate: Flag.boolean("refresh-template").pipe( + refreshTemplate: Flag.Boolean("refresh-template").pipe( Flag.withDefault(false), Flag.withDescription("full init and take a fresh template snapshot"), ), - build: Flag.boolean("build").pipe( + build: Flag.Boolean("build").pipe( Flag.withDefault(false), Flag.withDescription("force the image build even when inputs are unchanged"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("setup", flags.json, (report) => diff --git a/src/commands/shell.ts b/src/commands/shell.ts index be33684..1405dde 100644 --- a/src/commands/shell.ts +++ b/src/commands/shell.ts @@ -67,7 +67,7 @@ export const runInteractivePassthrough = ( export const shellCommand = Command.make( "shell", { - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/src/commands/test.ts b/src/commands/test.ts index 5e118cf..8039086 100644 --- a/src/commands/test.ts +++ b/src/commands/test.ts @@ -117,29 +117,29 @@ export const detectSkippedBrowserSuite = (output: { export const testCommand = Command.make( "test", { - tags: Flag.string("tags").pipe(Flag.optional), - file: Flag.string("file").pipe(Flag.optional), - module: Flag.string("module").pipe(Flag.optional), - logLevel: Flag.string("log-level").pipe(Flag.optional), - profile: Flag.string("profile").pipe( + tags: Flag.String("tags").pipe(Flag.optional), + file: Flag.String("file").pipe(Flag.optional), + module: Flag.String("module").pipe(Flag.optional), + logLevel: Flag.String("log-level").pipe(Flag.optional), + profile: Flag.String("profile").pipe( Flag.optional, Flag.withDescription("recipe-defined test profile"), ), - build: Flag.boolean("build").pipe( + build: Flag.Boolean("build").pipe( Flag.withDefault(false), Flag.withDescription("rebuild the Odoo image before running the test container"), ), - includeDemo: Flag.boolean("include-demo").pipe( + includeDemo: Flag.Boolean("include-demo").pipe( Flag.withDefault(false), Flag.withDescription( "accepted for compatibility; demo data is controlled at database init in v1", ), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("test", flags.json, (report) => diff --git a/src/commands/up.ts b/src/commands/up.ts index 79e6433..51a8749 100644 --- a/src/commands/up.ts +++ b/src/commands/up.ts @@ -85,31 +85,31 @@ export const guardUpBuildFlags = (flags: { export const upCommand = Command.make( "up", { - odooOnly: Flag.boolean("odoo-only").pipe( + odooOnly: Flag.Boolean("odoo-only").pipe( Flag.withDefault(false), Flag.withDescription("skip companion apps"), ), - build: Flag.boolean("build").pipe( + build: Flag.Boolean("build").pipe( Flag.withDefault(false), Flag.withDescription("force the image build even when inputs are unchanged"), ), - noBuild: Flag.boolean("no-build").pipe( + noBuild: Flag.Boolean("no-build").pipe( Flag.withDefault(false), Flag.withDescription("never build; warn when the image looks stale"), ), - logs: Flag.boolean("logs").pipe( + logs: Flag.Boolean("logs").pipe( Flag.withDefault(false), Flag.withDescription("follow odoo logs after start"), ), - detach: Flag.boolean("detach").pipe( + detach: Flag.Boolean("detach").pipe( Flag.withDefault(false), Flag.withDescription("start containers and return"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("up", flags.json, (report) => diff --git a/src/commands/update.ts b/src/commands/update.ts index 7c1c96f..363005d 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -9,17 +9,17 @@ import { withJsonReport } from "./json-report.js"; export const updateCommand = Command.make( "update", { - modules: Argument.string("modules"), - noRestart: Flag.boolean("no-restart").pipe(Flag.withDefault(false)), - build: Flag.boolean("build").pipe( + modules: Argument.String("modules"), + noRestart: Flag.Boolean("no-restart").pipe(Flag.withDefault(false)), + build: Flag.Boolean("build").pipe( Flag.withDefault(false), Flag.withDescription("rebuild the Odoo image before running the update container"), ), - json: Flag.boolean("json").pipe( + json: Flag.Boolean("json").pipe( Flag.withDefault(false), Flag.withDescription("suppress decorative output; print one final JSON report line"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => withJsonReport("update", flags.json, (report) => diff --git a/src/commands/worktree.ts b/src/commands/worktree.ts index 2bfb96e..91505d6 100644 --- a/src/commands/worktree.ts +++ b/src/commands/worktree.ts @@ -469,25 +469,25 @@ export const runWorktreeRemoveHook = (options: { const createCommand = Command.make( "create", { - name: Argument.string("name").pipe( + name: Argument.String("name").pipe( Argument.optional, Argument.withDescription("worktree name (omit with --hook-json)"), ), - path: Flag.string("path").pipe( + path: Flag.String("path").pipe( Flag.optional, Flag.withDescription("worktree directory (default: sibling -)"), ), - base: Flag.string("base").pipe( + base: Flag.String("base").pipe( Flag.optional, Flag.withDescription("base ref (default: $ODOO_WORKTREE_BASE_REF, origin HEAD, or HEAD)"), ), - hookJson: Flag.boolean("hook-json").pipe( + hookJson: Flag.Boolean("hook-json").pipe( Flag.withDefault(false), Flag.withDescription( "Claude Code hook mode: read {worktree_name, worktree_path} JSON from stdin; stdout carries only the final path", ), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { @@ -546,22 +546,22 @@ const createCommand = Command.make( const removeCommand = Command.make( "remove", { - path: Argument.string("path").pipe( + path: Argument.String("path").pipe( Argument.optional, Argument.withDescription("worktree directory (omit with --hook-json)"), ), - hookJson: Flag.boolean("hook-json").pipe( + hookJson: Flag.Boolean("hook-json").pipe( Flag.withDefault(false), Flag.withDescription( "Claude Code hook mode: read {worktree_path} JSON from stdin and always exit 0", ), ), - allowShared: Flag.boolean("allow-shared").pipe(Flag.withDefault(false)), - logFile: Flag.string("log-file").pipe( + allowShared: Flag.Boolean("allow-shared").pipe(Flag.withDefault(false)), + logFile: Flag.String("log-file").pipe( Flag.optional, Flag.withDescription("append step logs to this file (its directory is created)"), ), - config: Flag.string("config").pipe(Flag.optional), + config: Flag.String("config").pipe(Flag.optional), }, (flags) => Effect.gen(function* () { diff --git a/test/smoke.test.ts b/test/smoke.test.ts index 779445f..4904539 100644 --- a/test/smoke.test.ts +++ b/test/smoke.test.ts @@ -4,7 +4,7 @@ import { ChildProcess } from "effect/unstable/process"; import { Command, Flag } from "effect/unstable/cli"; import { NodeServices } from "@effect/platform-node"; -describe("effect v4 beta API smoke test", () => { +describe("effect v4 release candidate API smoke test", () => { it("runs a trivial effect", async () => { expect(await Effect.runPromise(Effect.succeed(41 + 1))).toBe(42); }); @@ -63,7 +63,7 @@ describe("effect v4 beta API smoke test", () => { }); it("cli Command/Flag exist", () => { - const cmd = Command.make("x", { f: Flag.boolean("f") }); + const cmd = Command.make("x", { f: Flag.Boolean("f") }); expect(cmd).toBeDefined(); }); });