claude-use 1.3.1 → 1.4.0

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 (3) hide show
  1. package/README.md +9 -3
  2. package/dist/cli.cjs +1285 -1047
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -468,9 +468,9 @@ One compiled binary backs both `claude` and `claude-use` — the entrypoint disp
468
468
  ```
469
469
  src/
470
470
  cli.ts # entrypoint; dispatches on invoked name -> launcher vs identity/profile-manager subcommands
471
+ cliError.ts # CliError — the base class every user-facing error extends, so main()'s top-level catch can print a clean message instead of a stack trace
471
472
  paths.ts # CLAUDE_USE_HOME-aware layout paths — every other module resolves ~/.claude-use/... paths through this, never inline
472
473
  pathNorm.ts # rule-path normalisation/ancestor helpers shared across the resolver and directory rules
473
- exit.ts # exit code constants
474
474
  versionDiscovery.ts # portable "find the real claude binary" logic
475
475
  realPorts.ts # the real filesystem/spawn/proc/clock/git ports wired into runLauncher by cli.ts (tests wire fakes instead)
476
476
  launcher.ts # runLauncher: thin orchestration over launcher/* below
@@ -529,6 +529,12 @@ install.sh # downloads the latest release's binary for the runni
529
529
  # checksum, and installs it as both `claude` and `claude-use` in ~/.local/bin
530
530
  ```
531
531
 
532
+ ### Error reporting: `CliError` vs. everything else
533
+
534
+ Every custom error this project throws to represent an expected, user-facing failure — a missing identity/profile/rule, a malformed config file, an invalid `--category`/`--share`/`--hide` flag — extends `CliError` (`src/cliError.ts`), an otherwise-empty abstract subclass of `Error`. `main()` in `src/cli.ts` wraps its whole body in one top-level `try`/`catch`: a `CliError` prints as `error.message` alone, with no stack trace, and exits `1`; anything else — a genuine, unanticipated bug — is rethrown and crashes with its full stack trace, which is more useful for diagnosing it than swallowing it would be. Before this existed, an error like `IdentityNotFoundError` thrown from the `@name` shortcut or from inside a Commander action (`identity use`, `profile create`, etc.) crashed with a raw Node.js stack trace instead of the one-line message its own constructor already built — the class carried the right text, nothing at the top ever caught it. `main()` calls `buildClaudeUseProgram().parseAsync(process.argv)`, not `.parse()`, specifically so an `async` action's rejection (e.g. `identity resolve <name>`, which awaits an interactive prompt) reaches this same catch too, rather than surfacing as an unhandled promise rejection Commander's synchronous `.parse()` never awaits.
535
+
536
+ `cliError.test.ts` asserts every one of these error classes actually extends `CliError` — the one regression `tsc`/`eslint` can never catch on their own, since a class silently reverting to `extends Error`, or a new one added without extending `CliError` at all, is still perfectly valid TypeScript.
537
+
532
538
  `schema.ts` models `categories` and `entries` differently despite their identical JSON-object appearance in every example above, because they have opposite key cardinality: `categories` only ever touches the four overridable names in the [category table](#category-based-sharing) plus the `all` shorthand, so it's a closed `z.strictObject({ all: z.boolean().optional(), runtime: z.boolean().optional(), history: z.boolean().optional(), knowledge: z.boolean().optional(), settings: z.boolean().optional() })` piped through a `.transform()` that expands `all` into the four real categories and drops it from the result — deliberately omitting `secret` from the shape entirely, so an attempted `secret` key is rejected at parse time rather than relying only on the runtime check described above — while `entries` is genuinely open-ended (any literal or glob path, each required to carry its `<category>/` prefix per the [Category-based sharing](#category-based-sharing) section above) and stays a `z.record(z.string().regex(ENTRY_KEY_RE), EntryValueSchema)`. The closed shape for `categories` also gives editors real key-name autocomplete from the published JSON Schema (the `schema/` directory above) — generated with Zod's `io: "input"` option specifically because a schema with a `.transform()` can't be represented in JSON Schema at all under the default `"output"` mode, and `"input"` is what a hand-written config actually needs describing anyway, which a record type couldn't offer either way.
533
539
 
534
540
  `ConfigProfile.extends` is a flat `z.array(z.string()).optional()` — a list of other profiles' *names*, resolved by `resolve/extends.ts` loading each named file and walking the resulting graph at runtime. It's correctly **not** a self-referential Zod schema (no `z.lazy()` needed): nothing in `ConfigProfile`'s own shape points back at `ConfigProfile`. Because each profile file validates in isolation, though, Zod has no way to catch a circular `extends` definition (`a` extends `b` extends `a`) — the walker in `resolve/extends.ts` needs its own cycle guard (a visited-set), independent of schema validation.
@@ -563,7 +569,7 @@ Every push to `main` runs [semantic-release](https://github.com/semantic-release
563
569
 
564
570
  One accepted quirk worth knowing rather than being surprised by: semantic-release creates the release tag pointing at the commit that already existed (the actual code change being released) *before* running `@semantic-release/git`'s commit step — so the changelog/version-bump commit lands on `main` **after** the tag, not folded into it. Every downstream job below checks out `ref: main` (not the commit that triggered the run) specifically to pick up this post-release state, matching how this org's other semantic-release repos handle the identical quirk.
565
571
 
566
- **The six platform build jobs (`build-arm64` through `build-windows-arm64`) also run on every pull request, not only a published release.** Each builds the real SEA binary for its own platform and then directly executes it (`--version`, `--help`) as a genuine smoke test — this is what would have caught, before merge rather than after a real release, an actual regression this project shipped once: a Turbo cache key that didn't distinguish `runner.arch`, letting one architecture's compiled binary silently serve for another (see `.github/actions/setup-and-run/action.yml`'s own comment). On a PR, each build job checks out the PR's own head commit instead of `ref: main`, since there's no post-release state to pick up yet. Publishing itself — npm, GitHub Packages, the GitHub Release, and the Homebrew/Scoop tap updates — stays gated on `needs.semantic-release.outputs.published == 'true'` alone and never runs on a PR, since those all have real, one-way side effects.
572
+ **The six platform build jobs (`build-arm64` through `build-windows-arm64`) also run on every pull request, not only a published release.** Each has its own dedicated `smoke-test-*` job — a separate job, not a step inside the build job, so a smoke-test failure shows as its own line in the PR checks list, distinct from "did it build" — that downloads the just-built artifact and directly executes it (`--version`, `--help`) on a runner of the same OS+architecture, since a binary built for one architecture cannot run on another. This is what would have caught, before merge rather than after a real release, an actual regression this project shipped once: a Turbo cache key that didn't distinguish `runner.arch`, letting one architecture's compiled binary silently serve for another (see `.github/actions/setup-and-run/action.yml`'s own comment). `release` depends on all five verified `smoke-test-*` jobs succeeding, not just their matching `build-*` jobs, so a smoke-test failure blocks the release the same way a build failure already did — `smoke-test-macos-x64-best-effort` is deliberately excluded from that gate, matching `build-x64-best-effort`'s own `continue-on-error: true`. On a PR, each build job checks out the PR's own head commit instead of `ref: main`, since there's no post-release state to pick up yet. Publishing itself — npm, GitHub Packages, the GitHub Release, and the Homebrew/Scoop tap updates — stays gated on `needs.semantic-release.outputs.published == 'true'` alone and never runs on a PR, since those all have real, one-way side effects.
567
573
 
568
574
  **Branch/tag protection had to be disabled for this to work.** `main`'s branch-protection ruleset previously required every push go through a reviewed PR, and a separate ruleset blocked tag creation/deletion outright — both only bypassable by the Admin repository role. semantic-release's own git operations run as the workflow's default `GITHUB_TOKEN`, which doesn't hold that role, so both rulesets were disabled (not deleted — the rule definitions are preserved and can be re-enabled with a single API call or via the repo's Rules settings page) rather than routing around them with a separate bypass credential.
569
575
 
@@ -575,7 +581,7 @@ One accepted quirk worth knowing rather than being surprised by: semantic-releas
575
581
 
576
582
  One gotcha worth knowing before reaching for this: **Homebrew's macOS Node build has the single-executable-application feature compiled out.** Running the build against a Homebrew-installed Node fails partway through with "Single executable application is disabled" — `scripts/build.mts` detects this specific error and rewrites it into an explanation naming the cause, rather than leaving a contributor to debug an opaque native error. Use a Node binary from a distribution that ships SEA support instead — the official nodejs.org build, or a version manager installing upstream builds (mise, nvm, volta, fnm) — ahead of Homebrew's on `PATH`.
577
583
 
578
- macOS SEA support is tested and verified upstream on **arm64 only** — x64 is explicitly unsupported and skipped in Node core's own test suite. CI builds and publishes the arm64 binary as the verified release artefact; it also attempts an x64 build as a clearly-labelled best-effort convenience (allowed to fail without blocking the release, and published as `claude-use-macos-x64-unverified` when it succeeds), never presented as a supported target. This isn't just a theoretical caveat: v0.2.7 confirmed `claude-use --version` itself segfaults on real x64 macOS hardware, a crash the build job's own smoke test had silently swallowed on every prior release via `|| true` until a dedicated install-and-run verify job (also best-effort, non-blocking) finally exercised the binary for real.
584
+ macOS SEA support is tested and verified upstream on **arm64 only** — x64 is explicitly unsupported and skipped in Node core's own test suite. CI builds and publishes the arm64 binary as the verified release artefact; it also attempts an x64 build as a clearly-labelled best-effort convenience (allowed to fail without blocking the release, and published as `claude-use-macos-x64-unverified` when it succeeds), never presented as a supported target. This isn't just a theoretical caveat: v0.2.7 confirmed `claude-use --version` itself segfaults on real x64 macOS hardware, a crash `smoke-test-macos-x64-best-effort` has silently swallowed via `|| true` until a dedicated install-and-run verify job (also best-effort, non-blocking) finally exercised the binary for real.
579
585
 
580
586
  **Root cause, confirmed rather than assumed.** Reproduced locally under Rosetta with 100% fidelity to CI: `lldb` shows `EXC_BAD_ACCESS` inside `__cxx_global_var_init`, invoked by `dyld` while running C++ static initializers — before `main()` ever executes, with `rdi` (the faulting access) holding the literal value `0x2`. A trivial one-line `console.log(...)` script built through the exact same `--build-sea` + ad-hoc-codesign steps crashes identically, which rules out anything in claude-use's own bundle, build script, or code — this is `--build-sea` itself misbehaving on x64 macOS. This is a known, tracked, and deliberately unfixed upstream limitation: [nodejs/node#62893](https://github.com/nodejs/node/issues/62893) reproduces the identical crash and was closed as documentation-only ([nodejs/node#63181](https://github.com/nodejs/node/pull/63181)), with a Node core maintainer stating SEA on x64 macOS "is not supported and skipped in the tests... until someone volunteers to implement support for it." The deeper investigation thread ([nodejs/node#59553](https://github.com/nodejs/node/issues/59553)) floats an unconfirmed theory — that `postject`'s LIEF-based Mach-O binary injection corrupts the executable such that `dyld` misidentifies a segment as an oversized (>4GB) thread-local-storage region — but that thread closed stale, unfixed, with the same maintainer concluding it's "unlikely that anyone would invest time in fixing it for macOS" given x64 macOS's Tier 2 deprioritisation upstream. There is no available workaround (no alternate `codesign` invocation, `sea-config.json` option, or Node flag) — the fault is inside `--build-sea`'s own binary-injection step, before any code this project controls runs at all.
581
587