feat(effect): migrate cli to effect beta#6
Conversation
There was a problem hiding this comment.
Pull request overview
Migrates the CLI from Effect 3.x / @effect/cli / @effect/platform-node to Effect 4.0 beta (effect@4.0.0-beta.66, @effect/platform-node@4.0.0-beta.66, effect/unstable/cli) and to the SDK-owned live layer in @putdotio/sdk@^9.3.0. Service tags are converted to Context.Service, schemas migrate to the new Schema.check / Schema.Literals / Schema.Codec APIs, and runtime stdout/stderr are routed through new CliRuntime methods to keep structured (JSON/NDJSON) output streaming-safe while parser errors stay machine-readable.
Changes:
- Wholesale migration to Effect 4 beta APIs across runtime, output, config, state, command, sdk, schema, and tests.
- New
cli.tsexecution path that buffersConsolewrites during structured output and only replays them on success or for parser-help flows. - Adds
scripts/prepare-effect.sh(and anis-cidevDependency) that clonesEffect-TS/effect-smolinto.repos/effectduringpnpm installoutside CI.
Reviewed changes
Copilot reviewed 33 out of 35 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Drops @effect/cli/@effect/platform, bumps to Effect 4 beta and SDK 9.3, wires prepare lifecycle hook. |
| pnpm-lock.yaml | Lockfile update for the new Effect/SDK dependency tree. |
| scripts/prepare-effect.sh | New install-time script that clones the Effect repo locally for skill docs. |
| .gitignore | Ignores the cloned .repos/effect directory. |
| vite.config.ts | Types coverage config, excludes .repos/** from tests. |
| docs/ARCHITECTURE.md | Updates references to the new effect/unstable/cli and SDK live layer. |
| src/bin.ts, src/sea.ts | Switch Effect.catchAllCause → Effect.catchCause. |
| src/cli.ts | New buffered-console runner, version flag handling, ShowHelp error routing. |
| src/cli.test.ts | Updates to Result API, new tests for version, structured parser errors, JSON name validation. |
| src/commands/{auth,brand,download-links,events,files,transfers,whoami}.ts | Imports moved to effect/unstable/cli (Command/Flag); transfers clean builds empty request when no ids; auth adds env-typed alias, replaces Effect.catchTag with broad Effect.catch. |
| src/command-paths.test.ts | Mirrors mock refactor, asserts auth login stderr instructions, transfers clean empty ids. |
| src/test-support/{run-cli,command-path-mocks}.ts | Adds writeStdout/writeStderr injection and shared page constants. |
| src/internal/app-layer.ts | Replaces NodeContext + NodeTerminal with NodeServices. |
| src/internal/runtime.ts | Adds writeStdout/writeStderr to CliRuntime and migrates to Context.Service. |
| src/internal/output-service.ts | Routes data writes through runtime.writeStdout; tightens output type. |
| src/internal/loader-service.ts | Adopts RequestedOutputMode type. |
| src/internal/config.ts | New Schema.check/Schema.makeFilter filters, Context.Service, typed errors. |
| src/internal/state.ts | New effect/FileSystem + PlatformError imports; Context.Service; CliConfig added to requirement graph; new resolveAuthRuntimeConfig wrapper. |
| src/internal/sdk.ts | Uses makePutioSdkLiveLayer and excludes PutioSdkContext from caller env. |
| src/internal/command.ts | Overloaded define{Integer,Text,Choice}Option builders, Flag.atLeast(0) for repeated flags, Schema.Codec typing for JSON decoding. |
| src/internal/command-specs.ts | Schema AST traversal uses effect/SchemaAST; new Suspend-only unwrap; BigInt literal guard. |
| src/internal/agent-dx.ts, src/internal/metadata.ts | Migrate to Schema.Literals / Schema.check. |
| src/internal/auth-flow.ts, src/internal/auth-flow.test.ts | Generic-environment waitForDeviceToken; tests adopt Effect.result/Failure. |
| src/internal/config.test.ts, src/internal/state.test.ts | Migrate to ConfigProvider.fromUnknown + provideService, and findErrorOption. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #!/usr/bin/env sh | ||
|
|
||
| set -eu | ||
|
|
||
| if command -v is-ci >/dev/null 2>&1 && is-ci; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| if [ -n "${CI:-}" ]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| repo_dir=".repos/effect" | ||
| repo_url="https://github.com/Effect-TS/effect-smol" | ||
|
|
||
| if [ -d "$repo_dir/.git" ]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| mkdir -p ".repos" | ||
| git clone "$repo_url" "$repo_dir" |
| if command -v is-ci >/dev/null 2>&1 && is-ci; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| if [ -n "${CI:-}" ]; then |
| type FileListItem = { | ||
| readonly file_type?: string; | ||
| readonly id: number; | ||
| readonly name?: string; | ||
| readonly size?: number; | ||
| }; | ||
| type FileListPage = { | ||
| readonly cursor: string | null; | ||
| readonly files: ReadonlyArray<FileListItem>; | ||
| readonly total?: number; | ||
| }; | ||
| type TransferListItem = { | ||
| readonly id: number; | ||
| readonly name: string; | ||
| readonly percent_done?: number; | ||
| readonly status?: string; | ||
| }; | ||
| type TransferListPage = { | ||
| readonly cursor: string | null; | ||
| readonly transfers: ReadonlyArray<TransferListItem>; | ||
| }; | ||
| const emptyFileListPage: FileListPage = { | ||
| cursor: null, | ||
| files: [], | ||
| total: 1, | ||
| }; | ||
| const emptyTransferListPage: TransferListPage = { | ||
| cursor: null, | ||
| transfers: [], | ||
| }; | ||
| const defaultFileListPage: FileListPage = { | ||
| cursor: null, | ||
| files: [ | ||
| { | ||
| file_type: "FOLDER", | ||
| id: 1, | ||
| name: "Movies", | ||
| size: 0, | ||
| }, | ||
| ], | ||
| total: 1, | ||
| }; | ||
| const defaultSearchFilesPage: FileListPage = { | ||
| cursor: null, | ||
| files: [ | ||
| { | ||
| file_type: "VIDEO", | ||
| id: 2, | ||
| name: "movie.mkv", | ||
| size: 42, | ||
| }, | ||
| ], | ||
| }; | ||
| const defaultTransferListPage: TransferListPage = { | ||
| cursor: null, | ||
| transfers: [ | ||
| { | ||
| id: 7, | ||
| name: "ubuntu.iso", | ||
| percent_done: 50, | ||
| status: "DOWNLOADING", | ||
| }, | ||
| ], | ||
| }; |
|
🎉 This PR is included in version 1.1.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Modernize putio-cli onto Effect beta and the new SDK live layer while preserving the agent-first CLI surface.
Changed
effect@4.0.0-beta.66,@effect/platform-node@4.0.0-beta.66, andeffect/unstable/cli.@putdotio/sdk@^9.3.0and compose the live runtime throughmakePutioSdkLiveLayer..repos/effectsetup throughscripts/prepare-effect.shfor local Effect guidance.Risks
prepareintentionally clones.repos/effectoutside CI per the local Effect skill setup. Codex review flagged this as a lifecycle network concern; we are accepting that tradeoff for this repo setup.Verification
pnpm exec tsc --noEmit --pretty falsepnpm exec vp test src/cli.test.ts src/command-paths.test.ts src/internal/auth-flow.test.ts src/internal/output.test.tspnpm exec vp run verify./dist/bin.mjs transfers clean --dry-run --output json./dist/bin.mjs files list --parent-id foo --output ndjson./dist/bin.mjs files list --parent-id foo --output jsoncodex-review --mode auto: no accepted/actionable findings remaining; the prepare lifecycle network finding was intentionally rejected.Complexity
Increased: CLI runtime output now separates parser-console buffering from direct runtime stdout/stderr writes so structured errors stay clean while NDJSON/auth flows can stream correctly.