feat: layer the CLI as cli → sdk/adapty → sdk/core - #29
Open
gshteynardt wants to merge 1 commit into
Open
Conversation
Introduce the architecture new commands are written against, without
rewriting the existing ones. Three layers, one dependency arrow:
src/sdk/core framework-free mechanics: http transport with retries,
errors, session store, device flow, clock, validation
src/sdk/adapty the product: resources, models, rules
src/cli the oclif adapter: base commands, exit codes, flags, views
auth (login, logout, revoke, status, whoami) and apps (list, get, create,
update) move over as the first users. oclif discovers commands only under
src/commands, so each keeps a one-line re-export there; package.json switches
to the pattern discovery strategy with '!**/lib/**', otherwise a command's
private lib/ ships as a command id of its own.
The boundaries are enforced, not just described: no-restricted-imports zones
in eslint.config.mjs (sdk must not see oclif or cli, core must not see
products, core/http has one door, src/lib is frozen to three named bridges),
test/architecture/frozen-legacy.test.ts fails on a new file in src/lib, and
test/cli/command-layout.test.ts keeps a command's lib/ private to it.
Behaviour changes, all deliberate:
- the migrated commands get their own exit-code dialect: 2 usage, 3 auth,
4 api, 5 network, 130 cancelled, where the old stack answers 1 or 2
- --json gains `source` on auth status and `env_token_set` on auth revoke
- auth revoke now revokes ADAPTY_TOKEN when it is set instead of ignoring
it, and preserves a stored session holding a different token (README)
- ADAPTY_TOKEN alone counts as authenticated for auth status
The manifest is otherwise unchanged against main: the same 75 command ids
with the same flags and the same --json contract, compared entry by entry
against a build of main. Human and --json output on the happy paths, and the
requests behind them (path, query, body, headers), are byte-identical. POST
and PUT are never retried, only GET and anything marked idempotent.
359 tests pass, lint clean. Layer contracts and migration state:
docs/architecture.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gshteynardt
force-pushed
the
feat/sdk-cli-layers
branch
from
September 11, 2026 08:54
a2e8944 to
72353c8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Introduce the architecture new commands are written against, without rewriting the existing ones. Three layers, one dependency arrow:
src/sdk/core framework-free mechanics: http transport with retries,
errors, session store, device flow, clock, validation
src/sdk/adapty the product: resources, models, rules
src/cli the oclif adapter: base commands, exit codes, flags, views
auth (login, logout, revoke, status, whoami) and apps (list, get, create, update) move over as the first users. oclif discovers commands only under src/commands, so each keeps a one-line re-export there; package.json switches to the pattern discovery strategy with '!/lib/', otherwise a command's private lib/ ships as a command id of its own.
The boundaries are enforced, not just described: no-restricted-imports zones in eslint.config.mjs (sdk must not see oclif or cli, core must not see products, core/http has one door, src/lib is frozen to three named bridges), test/architecture/frozen-legacy.test.ts fails on a new file in src/lib, and test/cli/command-layout.test.ts keeps a command's lib/ private to it.
Behaviour changes, all deliberate:
sourceon auth status andenv_token_seton auth revokeThe manifest is otherwise unchanged against main: the same 75 command ids with the same flags and the same --json contract, compared entry by entry against a build of main. Human and --json output on the happy paths, and the requests behind them (path, query, body, headers), are byte-identical. POST and PUT are never retried, only GET and anything marked idempotent.
359 tests pass, lint clean. Layer contracts and migration state: docs/architecture.md.