claude-use 1.3.0 → 1.3.2
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.
- package/README.md +9 -3
- package/dist/cli.cjs +35 -19
- 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
|
|
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
|
|
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
|
|
package/dist/cli.cjs
CHANGED
|
@@ -221857,7 +221857,7 @@ var categories_default_default = {
|
|
|
221857
221857
|
// package.json
|
|
221858
221858
|
var package_default = {
|
|
221859
221859
|
name: "claude-use",
|
|
221860
|
-
version: "1.3.
|
|
221860
|
+
version: "1.3.2",
|
|
221861
221861
|
description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
221862
221862
|
license: "Apache-2.0",
|
|
221863
221863
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -221936,6 +221936,12 @@ var package_default = {
|
|
|
221936
221936
|
|
|
221937
221937
|
// src/config/load.ts
|
|
221938
221938
|
var import_cosmiconfig = __toESM(require_dist(), 1);
|
|
221939
|
+
|
|
221940
|
+
// src/cliError.ts
|
|
221941
|
+
var CliError = class extends Error {
|
|
221942
|
+
};
|
|
221943
|
+
|
|
221944
|
+
// src/config/load.ts
|
|
221939
221945
|
function createExplorer(moduleName = "claude-use") {
|
|
221940
221946
|
return (0, import_cosmiconfig.cosmiconfigSync)(moduleName, { searchPlaces: [] });
|
|
221941
221947
|
}
|
|
@@ -221962,7 +221968,7 @@ function captureRuleEntryOrders(value) {
|
|
|
221962
221968
|
}
|
|
221963
221969
|
return rules.map((rule) => captureEntryOrder(rule));
|
|
221964
221970
|
}
|
|
221965
|
-
var ConfigValidationError = class extends
|
|
221971
|
+
var ConfigValidationError = class extends CliError {
|
|
221966
221972
|
constructor(filepath, issues) {
|
|
221967
221973
|
const detail = issues.map((issue2) => ` ${issue2.path.join(".") || "(root)"}: ${issue2.message}`).join("\n");
|
|
221968
221974
|
super(`Invalid configuration in ${filepath}:
|
|
@@ -237253,7 +237259,7 @@ function splitOnWildcards(pattern) {
|
|
|
237253
237259
|
}
|
|
237254
237260
|
return fragments;
|
|
237255
237261
|
}
|
|
237256
|
-
var UnrootedProjectPathError = class extends
|
|
237262
|
+
var UnrootedProjectPathError = class extends CliError {
|
|
237257
237263
|
constructor(fragment) {
|
|
237258
237264
|
super(
|
|
237259
237265
|
`"${fragment}" is not a rooted path. Everything written after the "history/projects/" prefix is a real absolute working directory (optionally globbed), so it must start with "/" or "~/" \u2014 a bare relative path has nothing to encode against.`
|
|
@@ -237352,7 +237358,7 @@ function normaliseRelative(fragment) {
|
|
|
237352
237358
|
return segments.join("/");
|
|
237353
237359
|
}
|
|
237354
237360
|
var PROJECTS_PREFIX = "projects/";
|
|
237355
|
-
var EntryKeyError = class extends
|
|
237361
|
+
var EntryKeyError = class extends CliError {
|
|
237356
237362
|
constructor(key, message, reason) {
|
|
237357
237363
|
super(message);
|
|
237358
237364
|
this.key = key;
|
|
@@ -237846,7 +237852,7 @@ var import_node_path7 = __toESM(require("node:path"), 1);
|
|
|
237846
237852
|
var DEFAULT_STALE_AFTER_MS = 12e4;
|
|
237847
237853
|
var DEFAULT_RETRY_DELAY_MS = 50;
|
|
237848
237854
|
var DEFAULT_MAX_ATTEMPTS = 200;
|
|
237849
|
-
var IdentityLockBusyError = class extends
|
|
237855
|
+
var IdentityLockBusyError = class extends CliError {
|
|
237850
237856
|
constructor(identity, lockPath, holderPid) {
|
|
237851
237857
|
super(
|
|
237852
237858
|
`Another claude-use resync is already running for identity "${identity}"` + (holderPid === void 0 ? "" : ` (pid ${holderPid})`) + `. Its lock at ${lockPath} was still held after the full retry budget; nothing was changed.`
|
|
@@ -240196,7 +240202,7 @@ async function resolveFarmConflicts(params) {
|
|
|
240196
240202
|
}
|
|
240197
240203
|
|
|
240198
240204
|
// src/identityManager.ts
|
|
240199
|
-
var IdentityNotFoundError = class extends
|
|
240205
|
+
var IdentityNotFoundError = class extends CliError {
|
|
240200
240206
|
constructor(name) {
|
|
240201
240207
|
super(`No identity named "${name}" \u2014 run \`claude-use identity add ${name}\` first.`);
|
|
240202
240208
|
this.name = name;
|
|
@@ -240204,7 +240210,7 @@ var IdentityNotFoundError = class extends Error {
|
|
|
240204
240210
|
}
|
|
240205
240211
|
name;
|
|
240206
240212
|
};
|
|
240207
|
-
var IdentityAlreadyExistsError = class extends
|
|
240213
|
+
var IdentityAlreadyExistsError = class extends CliError {
|
|
240208
240214
|
constructor(identityName) {
|
|
240209
240215
|
super(`An identity named "${identityName}" already exists.`);
|
|
240210
240216
|
this.identityName = identityName;
|
|
@@ -240399,7 +240405,7 @@ function collectBoolPairs(value, previous = {}) {
|
|
|
240399
240405
|
}
|
|
240400
240406
|
|
|
240401
240407
|
// src/configProfiles.ts
|
|
240402
|
-
var ProfileNotFoundError = class extends
|
|
240408
|
+
var ProfileNotFoundError = class extends CliError {
|
|
240403
240409
|
constructor(profileName) {
|
|
240404
240410
|
super(`No configuration profile named "${profileName}" \u2014 run \`claude-use profile create ${profileName}\` first.`);
|
|
240405
240411
|
this.profileName = profileName;
|
|
@@ -240407,7 +240413,7 @@ var ProfileNotFoundError = class extends Error {
|
|
|
240407
240413
|
}
|
|
240408
240414
|
profileName;
|
|
240409
240415
|
};
|
|
240410
|
-
var ProfileAlreadyExistsError = class extends
|
|
240416
|
+
var ProfileAlreadyExistsError = class extends CliError {
|
|
240411
240417
|
constructor(profileName) {
|
|
240412
240418
|
super(`A configuration profile named "${profileName}" already exists.`);
|
|
240413
240419
|
this.profileName = profileName;
|
|
@@ -240415,7 +240421,7 @@ var ProfileAlreadyExistsError = class extends Error {
|
|
|
240415
240421
|
}
|
|
240416
240422
|
profileName;
|
|
240417
240423
|
};
|
|
240418
|
-
var InvalidCategoryNameError = class extends
|
|
240424
|
+
var InvalidCategoryNameError = class extends CliError {
|
|
240419
240425
|
constructor(categoryName) {
|
|
240420
240426
|
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
240421
240427
|
this.categoryName = categoryName;
|
|
@@ -240559,7 +240565,7 @@ function registerProfileCommand(program2, paths) {
|
|
|
240559
240565
|
}
|
|
240560
240566
|
|
|
240561
240567
|
// src/directoryRules.ts
|
|
240562
|
-
var DirectoryRuleNotFoundError = class extends
|
|
240568
|
+
var DirectoryRuleNotFoundError = class extends CliError {
|
|
240563
240569
|
constructor(rulePath) {
|
|
240564
240570
|
super(`No directory rule found for path "${rulePath}".`);
|
|
240565
240571
|
this.rulePath = rulePath;
|
|
@@ -241038,7 +241044,7 @@ var ClaudeShimStateSchema = external_exports.strictObject({
|
|
|
241038
241044
|
method: external_exports.enum(["hardlink", "copy"]),
|
|
241039
241045
|
installedAtMs: external_exports.number()
|
|
241040
241046
|
});
|
|
241041
|
-
var ForeignClaudeEntryError = class extends
|
|
241047
|
+
var ForeignClaudeEntryError = class extends CliError {
|
|
241042
241048
|
constructor(targetPath, action) {
|
|
241043
241049
|
super(
|
|
241044
241050
|
`${targetPath} already exists and does not look like something claude-use created. Refusing to ${action === "enable" ? "overwrite" : "remove"} it automatically \u2014 inspect it yourself, or pass --force if you're sure.`
|
|
@@ -241050,7 +241056,7 @@ var ForeignClaudeEntryError = class extends Error {
|
|
|
241050
241056
|
targetPath;
|
|
241051
241057
|
action;
|
|
241052
241058
|
};
|
|
241053
|
-
var UnsupportedShimSourceError = class extends
|
|
241059
|
+
var UnsupportedShimSourceError = class extends CliError {
|
|
241054
241060
|
constructor(contentSourcePath) {
|
|
241055
241061
|
super(
|
|
241056
241062
|
`Cannot create a working \`claude\` command from ${contentSourcePath} on Windows: an npm-installed claude-use running under Node.js has no .exe to hardlink/copy, and Windows has no shebang-based dispatch the way POSIX does. Install claude-use via Scoop instead (see README), which ships a real claude-use.exe this command can link from.`
|
|
@@ -241533,7 +241539,7 @@ function parseLauncherArgv(argv) {
|
|
|
241533
241539
|
}
|
|
241534
241540
|
|
|
241535
241541
|
// src/launcher/cliOverride.ts
|
|
241536
|
-
var InvalidCliCategoryError = class extends
|
|
241542
|
+
var InvalidCliCategoryError = class extends CliError {
|
|
241537
241543
|
constructor(categoryName) {
|
|
241538
241544
|
super(`"${categoryName}" is not a category this launch may toggle (runtime, history, knowledge, settings).`);
|
|
241539
241545
|
this.categoryName = categoryName;
|
|
@@ -241541,7 +241547,7 @@ var InvalidCliCategoryError = class extends Error {
|
|
|
241541
241547
|
}
|
|
241542
241548
|
categoryName;
|
|
241543
241549
|
};
|
|
241544
|
-
var InvalidCliEntryKeyError = class extends
|
|
241550
|
+
var InvalidCliEntryKeyError = class extends CliError {
|
|
241545
241551
|
constructor(key) {
|
|
241546
241552
|
super(`"${key}" is not a valid entries key \u2014 it must start with "<category>/", e.g. "knowledge/skills/commit".`);
|
|
241547
241553
|
this.key = key;
|
|
@@ -241873,15 +241879,25 @@ function runClaude(argvOverride) {
|
|
|
241873
241879
|
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile }
|
|
241874
241880
|
});
|
|
241875
241881
|
}
|
|
241876
|
-
function main() {
|
|
241882
|
+
async function main() {
|
|
241877
241883
|
const invokedName = import_node_path21.default.basename(process.argv[1] ?? "claude-use");
|
|
241878
241884
|
if (isInvokedAsClaude(invokedName)) {
|
|
241879
241885
|
runClaude();
|
|
241880
|
-
|
|
241881
|
-
buildClaudeUseProgram().parse(process.argv);
|
|
241886
|
+
return;
|
|
241882
241887
|
}
|
|
241888
|
+
if (tryRunAtIdentityShortcut(resolveLayoutPaths(), process.argv.slice(2))) {
|
|
241889
|
+
return;
|
|
241890
|
+
}
|
|
241891
|
+
await buildClaudeUseProgram().parseAsync(process.argv);
|
|
241883
241892
|
}
|
|
241884
|
-
main()
|
|
241893
|
+
main().catch((error51) => {
|
|
241894
|
+
if (error51 instanceof CliError) {
|
|
241895
|
+
console.error(error51.message);
|
|
241896
|
+
process.exitCode = 1;
|
|
241897
|
+
return;
|
|
241898
|
+
}
|
|
241899
|
+
throw error51;
|
|
241900
|
+
});
|
|
241885
241901
|
/*! Bundled license information:
|
|
241886
241902
|
|
|
241887
241903
|
typescript/lib/typescript.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-use",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|