Skip to content

Split CLI surface into @primitivedotdev/cli#66

Merged
etbyrd merged 2 commits into
mainfrom
split-cli-package
May 11, 2026
Merged

Split CLI surface into @primitivedotdev/cli#66
etbyrd merged 2 commits into
mainfrom
split-cli-package

Conversation

@etbyrd
Copy link
Copy Markdown
Member

@etbyrd etbyrd commented May 11, 2026

Summary

Move the primitive CLI out of @primitivedotdev/sdk into a new @primitivedotdev/cli package.

The motivation comes from the AGX summarizer runs: agents discover the package via npx @primitivedotdev/sdk@latest <command> and then treat @primitivedotdev/sdk as a CLI tool. The most recent run's generated package.json had esbuild as the only devDep and @primitivedotdev/sdk nowhere, even though the docs show createPrimitiveClient from @primitivedotdev/sdk/api for in-handler use. The agent classified the SDK as "the CLI" and wrote raw fetch calls in the handler.

This split matches the convention across every dev-tool ecosystem: wrangler vs @cloudflare/workers-types, vercel vs @vercel/node, aws-cli vs @aws-sdk/*. CLI = binary you install once. SDK = library you add to dependencies. Two packages, two roles, two install moments. Agents have to engage with both.

What changed

  • New cli-node/ workspace publishing @primitivedotdev/cli at 0.24.0. Contains all oclif command source, tests, and build configs. Depends on @primitivedotdev/sdk@^0.23.0 for runtime types and the API client; its own pnpm-lock.yaml.
  • sdk-node/ bumped to 0.24.0. Retains the existing src/oclif/ snapshot for a deprecation window so npx @primitivedotdev/sdk@latest <command> keeps working for agent prompts in the wild. bin/run.js prints a one-line stderr banner pointing at @primitivedotdev/cli on every invocation.
  • sdk-go and sdk-python bumped to 0.24.0 lockstep with sdk-node. No functional change.
  • New .github/workflows/cli-release.yml mirrors node-release.yml; triggers on cli-node/package.json version changes.
  • .github/workflows/sdk-checks.yml adds a cli job paralleling the existing node job (matrix on Node 22/24).
  • Makefile gains cli-install / cli-test / cli-check / cli-build / cli-smoke / cli-coverage and wires them into check, build, release-check, and ci.
  • RELEASE.md and CLAUDE.md updated with the cli-node release flow.
  • functions:init scaffolder's generated README mentions @primitivedotdev/cli as the source of the primitive binary.
  • sdk-node/README.md rewritten to focus on the library and point CLI users at @primitivedotdev/cli.

Backward compat

The CLI surface in sdk-node@0.24.0 is frozen against the 0.23.0 command set. Every invocation prints:

[@primitivedotdev/sdk] Heads up: the CLI moved to @primitivedotdev/cli. Switch to `npx @primitivedotdev/cli@latest <command>` (or `npm install -g @primitivedotdev/cli`). The CLI surface will be removed from @primitivedotdev/sdk in a future minor release.

Existing scripts and prompts continue to work for 2-3 minor releases. After that, the CLI surface gets deleted from sdk-node entirely (separate PR).

Verified locally

  • make node-check: 31 files / 699 tests green.
  • make cli-check: 10 files / 139 tests green.
  • make node-smoke: packed sdk-node installs; primitive list-operations works; deprecation banner visible on each invocation.
  • make cli-smoke: packed cli-node installs; primitive list-operations works.
  • make shared-check: green.

Follow-ups (separate PRs)

  • Mono-repo docs sweep replacing every npx @primitivedotdev/sdk@latest reference in /docs/quickstart, /docs/functions, /docs/cli, and llms.txt with the @primitivedotdev/cli equivalent.
  • After 2-3 minor releases, delete sdk-node/src/oclif/ and the bin entry; @primitivedotdev/cli becomes the sole CLI surface.

Test plan

  • CI green: node, cli, python, go, shared, and the final aggregate ci job.
  • After merge, the CLI Release workflow runs on the version bump in cli-node/package.json and publishes @primitivedotdev/cli@0.24.0 to npm.
  • After merge, the Node Release workflow runs on the version bump in sdk-node/package.json and publishes @primitivedotdev/sdk@0.24.0 to npm with the deprecation banner.
  • npm install -g @primitivedotdev/cli@0.24.0 && primitive whoami works against a real API key.
  • npx @primitivedotdev/sdk@0.24.0 whoami works and prints the deprecation banner to stderr.

The AGX summarizer runs surfaced a consistent mental-model problem: agents discovered the package via `npx @primitivedotdev/sdk@latest` and then treated `@primitivedotdev/sdk` as a CLI tool. In the most recent run, the agent's generated `package.json` had `esbuild` as the only devDep and `@primitivedotdev/sdk` nowhere, even though the docs showed `createPrimitiveClient` from `@primitivedotdev/sdk/api` in handler examples. The agent classified the SDK as "the CLI" and wrote raw fetch calls in the handler.

This split disambiguates: `@primitivedotdev/cli` is a CLI you install once; `@primitivedotdev/sdk` is a library you add to dependencies. Matches the convention across every other dev-tool ecosystem (wrangler vs @cloudflare/workers-types, vercel vs @vercel/node, etc.).

cli-node/ is a new package with all CLI source, tests, build config, and Make targets. Depends on @primitivedotdev/sdk@^0.23.0 for runtime types and the API client. Independent pnpm-lock.yaml so cross-package dep changes are explicit, not transitive.

sdk-node retains its src/oclif/ snapshot for a deprecation window. bin/run.js prints a one-line stderr banner on every CLI invocation pointing at @primitivedotdev/cli. The shipped CLI surface in sdk-node 0.24.0 is frozen against the 0.23.0 command set; new CLI features land in cli-node only. Banner-only behavior covers the agent-prompt corpus in the wild that still references npx @primitivedotdev/sdk@latest <command>.

sdk-go and sdk-python bumped to 0.24.0 lockstep with sdk-node. No functional change in those.

Wired into the existing release pipeline:
- New .github/workflows/cli-release.yml mirrors node-release.yml; triggers on cli-node/package.json version changes.
- .github/workflows/sdk-checks.yml adds a cli job paralleling the node job (matrix on Node 22/24).
- Makefile gains cli-install / cli-test / cli-check / cli-build / cli-smoke / cli-coverage and wires them into check / build / release-check / ci.

Verified locally:
- make node-check: 31 files / 699 tests green; deprecation banner fires in node-smoke.
- make cli-check: 10 files / 139 tests green.
- make cli-smoke: packed tarball installs and `primitive list-operations` works.
- make node-smoke: packed tarball still works; deprecation banner visible on each invocation.
- make shared-check: green.
- No em dashes, no Conventional Commits prefix, no Co-Authored-By trailer.

Follow-ups (separate PRs):
- mono-repo docs sweep replacing every `npx @primitivedotdev/sdk@latest` reference in /docs/quickstart, /docs/functions, /docs/cli, and llms.txt with the @primitivedotdev/cli equivalent.
- After 2-3 minor releases, delete sdk-node/src/oclif/ and the bin entry; @primitivedotdev/cli becomes the sole CLI surface.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 11, 2026

Greptile Summary

This PR extracts the primitive CLI out of @primitivedotdev/sdk into a new @primitivedotdev/cli package, while keeping a deprecated alias in sdk-node for a backward-compatibility window. The split matches the standard convention across dev-tool ecosystems and resolves an agent-classification problem where the SDK was mistaken for a CLI tool.

  • New cli-node/ workspace publishes @primitivedotdev/cli@0.24.0 with all oclif command source, tests, build configs, and its own pnpm-lock.yaml. Depends on @primitivedotdev/sdk@^0.23.0 at runtime.
  • sdk-node/bin/run.js gains a one-line stderr deprecation banner that fires on every invocation, preserving backward compat for 2–3 minor releases before the CLI surface is deleted from sdk-node.
  • CI and release infrastructure added: cli-release.yml mirrors node-release.yml; sdk-checks.yml gains a parallel cli matrix job; Makefile gains cli-install/test/check/build/smoke/coverage targets wired into check, build, release-check, and ci.

Confidence Score: 5/5

Safe to merge — the change is primarily an additive extraction with a backward-compatible deprecation shim on the existing SDK bin.

The new cli-node workspace is a clean copy of the existing oclif surface with no functional changes to command logic. The sdk-node deprecation path retains the full 0.23.0 command set behind a one-liner stderr banner. CI wiring mirrors existing patterns exactly. SDK_VERSION_RANGE in the scaffolder is correctly pinned to ^0.23.0, matching the CLI's own tested dependency.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/cli-release.yml New release workflow for @primitivedotdev/cli, structurally identical to node-release.yml — same version-detection logic, idempotency guards (check published/release already exists), and tag convention (cli-node/vX.Y.Z).
.github/workflows/sdk-checks.yml Adds cli job (Node 22/24 matrix) parallel to existing node job; correctly wires it into the ci aggregate job and treats 'skipped' as success.
cli-node/src/oclif/commands/functions-init.ts Scaffold command; SDK_VERSION_RANGE correctly set to ^0.23.0 matching cli-node's own dep; atomic directory creation prevents TOCTOU; rollback on partial write failure.
cli-node/src/oclif/auth.ts Credential store implementation using atomic temp-file-then-rename writes, lock directory for mutual exclusion, stale-lock detection, and backward-compatible stale-format migration.
cli-node/src/oclif/api-command.ts Core oclif base command; dynamically generates flags from OpenAPI manifest; handles raw-body/body-file mutual exclusion and clean error propagation.
sdk-node/bin/run.js Adds a one-line stderr deprecation banner before delegating to oclif execute, preserving backward compatibility for existing scripts and agent prompts.
cli-node/package.json New @primitivedotdev/cli package at 0.24.0; correct prepack/postpack lifecycle for oclif manifest; depends on @primitivedotdev/sdk@^0.23.0.
Makefile Adds cli-install/test/check/build/smoke/coverage targets; wires them into check, build, release-check, and ci; cli-smoke properly exercises list-operations, fish, and bash completions.
cli-node/src/oclif/commands/login.ts Browser-assisted device-flow login; acquires credential lock before mutating; checks existing login validity and surfaces actionable error messages.

Reviews (2): Last reviewed commit: "Lockstep scaffolder SDK range with the C..." | Re-trigger Greptile

Comment thread cli-node/src/oclif/commands/functions-init.ts Outdated
Greptile review on PR #66 caught that SDK_VERSION_RANGE in the
functions:init scaffolder was pinned to ^0.22.0 while cli-node itself
depends on @primitivedotdev/sdk@^0.23.0. Scaffolded projects would
get a one-minor-old SDK that the CLI hadn't been built or tested
against, opening a silent-break window if 0.22.0 and 0.23.0 differ.

Bump the scaffolder range to ^0.23.0 to match cli-node/package.json,
update the comment to require lockstep with the CLI's own SDK dep,
and add a regression test that reads cli-node/package.json at runtime
and asserts the two ranges are byte-equal. Future bumps now have to
land in both places or the test fails.
@etbyrd
Copy link
Copy Markdown
Member Author

etbyrd commented May 11, 2026

Addressed Greptile finding in c53243b. Bumped SDK_VERSION_RANGE in the functions:init scaffolder from ^0.22.0 to ^0.23.0 so it matches cli-node's own @primitivedotdev/sdk dep, and added a regression test that reads cli-node/package.json at runtime and asserts the two ranges are byte-equal. Future bumps now have to land in both places or the test fails. All 140 cli-node tests green; lint and typecheck clean.

@etbyrd etbyrd merged commit 5b03d5e into main May 11, 2026
12 checks passed
@etbyrd etbyrd deleted the split-cli-package branch May 11, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant