agent-shim 7.11.0 → 7.12.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.
package/README.md CHANGED
@@ -82,47 +82,47 @@ The full mechanics, including the credential block, the merge algorithm, conditi
82
82
  ```
83
83
  agent-shim <noun> <verb> [name] [options] # nouns: identity, profile, pool, provider, rule
84
84
 
85
- agent-shim identity add <name>
85
+ agent-shim identity add <name> [--json]
86
86
  agent-shim identity list [--json]
87
87
  agent-shim identity show <name> [--json]
88
- agent-shim identity set <name> [--default-profile <profile> | --no-default-profile] [--[no-]allow-ambient-credential]
89
- agent-shim identity set <name> [--credential <source>]... [--credential-target <bearer|apiKey|oauthToken>] [--no-credential]
90
- agent-shim identity remove <name> [--yes]
91
- agent-shim identity use <name>
88
+ agent-shim identity set <name> [--default-profile <profile> | --no-default-profile] [--[no-]allow-ambient-credential] [--json]
89
+ agent-shim identity set <name> [--credential <source>]... [--credential-target <bearer|apiKey|oauthToken>] [--no-credential] [--json]
90
+ agent-shim identity remove <name> [--yes] [--json]
91
+ agent-shim identity use <name> [--json]
92
92
  agent-shim @<name> # shorthand for `identity use <name>`
93
93
  agent-shim identity resolve-conflicts <name> # interactively resolve a retained superseded farm's conflicts
94
94
 
95
- agent-shim profile add [name] [--extends <profile>]... [--description <text>] # interactive with no options on a terminal
96
- agent-shim profile set <name> [--category <category>=<bool>]... [--entry <category>/<path>=<bool>]...
97
- agent-shim profile set <name> [--extends <profile>]... [--no-extends] [--description <text> | --no-description]
98
- agent-shim profile set <name> [--[no-]launch-skip-permissions] [--[no-]launch-remote-control] [--[no-]launch-headroom] [--[no-]launch-track-usage]
99
- agent-shim profile set <name> [--launch-provider <provider> | --no-launch-provider]
95
+ agent-shim profile add [name] [--extends <profile>]... [--description <text>] # interactive with no options on a terminal [--json]
96
+ agent-shim profile set <name> [--category <category>=<bool>]... [--entry <category>/<path>=<bool>]... [--json]
97
+ agent-shim profile set <name> [--extends <profile>]... [--no-extends] [--description <text> | --no-description] [--json]
98
+ agent-shim profile set <name> [--[no-]launch-skip-permissions] [--[no-]launch-remote-control] [--[no-]launch-headroom] [--[no-]launch-track-usage] [--json]
99
+ agent-shim profile set <name> [--launch-provider <provider> | --no-launch-provider] [--json]
100
100
  agent-shim profile list [--json]
101
101
  agent-shim profile show <name> [--json]
102
- agent-shim profile remove <name> [--yes]
103
- agent-shim profile use <name> # the global default configuration profile
102
+ agent-shim profile remove <name> [--yes] [--json]
103
+ agent-shim profile use <name> # the global default configuration profile [--json]
104
104
 
105
- agent-shim pool add <name> --identity <identity>...
106
- agent-shim pool set <name> --identity <identity>...
105
+ agent-shim pool add <name> --identity <identity>... [--json]
106
+ agent-shim pool set <name> --identity <identity>... [--json]
107
107
  agent-shim pool list [--json]
108
108
  agent-shim pool show <name> [--json]
109
- agent-shim pool remove <name> [--yes]
110
- agent-shim pool use <name>
109
+ agent-shim pool remove <name> [--yes] [--json]
110
+ agent-shim pool use <name> [--json]
111
111
  agent-shim pool pick <name> [--json]
112
112
 
113
- agent-shim provider add <name> --display-name <name> (--base-url <url> | --kind codex) (--credential <source>)... [--credential-target <bearer|apiKey>] [--env KEY=VALUE]...
113
+ agent-shim provider add <name> --display-name <name> (--base-url <url> | --kind codex) (--credential <source>)... [--credential-target <bearer|apiKey>] [--env KEY=VALUE]... [--json]
114
114
  [--codex-default-model <model>] [--codex-model <tier=model>]... [--codex-effort <none|low|medium|high>]
115
- agent-shim provider set <name> [--display-name <name>] ([--base-url <url>] | --kind codex) [--credential <source>]... [--credential-target <bearer|apiKey>] [--env KEY=VALUE]... [--unset-env KEY]...
115
+ agent-shim provider set <name> [--display-name <name>] ([--base-url <url>] | --kind codex) [--credential <source>]... [--credential-target <bearer|apiKey>] [--env KEY=VALUE]... [--unset-env KEY]... [--json]
116
116
  [--codex-default-model <model>] [--codex-model <tier=model>]... [--codex-effort <none|low|medium|high>]
117
117
  agent-shim provider list [--json]
118
118
  agent-shim provider show <name> [--json]
119
- agent-shim provider remove <name> [--yes]
119
+ agent-shim provider remove <name> [--yes] [--json]
120
120
 
121
- agent-shim rule add <path> [--config-profile <profile>] [--identity <identity>]
122
- agent-shim rule set <path> [--config-profile <profile> | --no-config-profile] [--identity <identity> | --no-identity]
121
+ agent-shim rule add <path> [--config-profile <profile>] [--identity <identity>] [--json]
122
+ agent-shim rule set <path> [--config-profile <profile> | --no-config-profile] [--identity <identity> | --no-identity] [--json]
123
123
  agent-shim rule list [--json]
124
124
  agent-shim rule show <path> [--json]
125
- agent-shim rule remove <path> [--yes]
125
+ agent-shim rule remove <path> [--yes] [--json]
126
126
 
127
127
  agent-shim configure [path] [--identity <identity>]
128
128
  agent-shim check [path] [--identity <identity>] [--json] [--strict]
@@ -144,7 +144,7 @@ agent-shim run [@<identity>] [launch flags] [claude arguments]
144
144
  claude @<identity> ... # the same, once `agent-shim shim enable` has run
145
145
  ```
146
146
 
147
- Every `list`, `show`, `check`, `doctor` and `headroom status` prints text by default and JSON with `--json`. Prompts appear only when standard input is a terminal; without one, a command that needs input fails with the option that supplies it (a `remove` needs `--yes`). Failures print as `agent-shim: <message>` and exit 1, usage errors exit 2, a selected provider or identity whose credential block yields no token exits 64, and `AGENT_SHIM_DEBUG=1` adds stack traces to unexpected errors. `agent-shim completion <bash|zsh|fish>` prints a shell completion script.
147
+ Every `list`, `show`, `check`, `doctor` and `headroom status` prints text by default and JSON with `--json`, and so do the mutating commands (`add`, `set`, `remove` and `use` on identities, profiles, providers, pools and rules), which print one object naming the `action`, the `kind`, the `name` and, for a creation or update, the stored `value`. Prompts appear only when standard input is a terminal; without one, a command that needs input fails with the option that supplies it (a `remove` needs `--yes`). Failures print as `agent-shim: <message>` and exit 1, usage errors exit 2, a selected provider or identity whose credential block yields no token exits 64, and `AGENT_SHIM_DEBUG=1` adds stack traces to unexpected errors. `agent-shim completion <bash|zsh|fish>` prints a shell completion script.
148
148
 
149
149
  `agent-shim check [path]` resolves the full cascade for a directory without touching the farm, and is the primary way to answer "why is X shared or hidden here". `agent-shim doctor` audits the whole `~/.agent-shim` config graph at once: every identity, profile, `extends` chain, provider (naming the exact replacement for a file still in the format before the credential block) and directory rule, plus whether the right `agent-shim` is the one actually on `PATH`.
150
150
 
package/dist/cli.cjs CHANGED
@@ -54858,6 +54858,13 @@ function date4(params) {
54858
54858
  function printJson(value) {
54859
54859
  console.log(JSON.stringify(value, null, 2));
54860
54860
  }
54861
+ function reportMutation(json2, report, printText) {
54862
+ if (json2 === true) {
54863
+ printJson(report);
54864
+ } else {
54865
+ printText();
54866
+ }
54867
+ }
54861
54868
  function withExamples(command, examples) {
54862
54869
  return command.addHelpText("after", `
54863
54870
  Examples:
@@ -60584,30 +60591,36 @@ function registerProfileCommand(program2, deps) {
60584
60591
  withExamples(
60585
60592
  profile.command("add [name]").description(
60586
60593
  "Create a new configuration profile. Fails if one with this name already exists. With no options on a terminal, walks through choosing its categories."
60587
- ).option("--extends <profile>", "A profile this one extends (repeatable, in order).", collectRepeated).option("--description <text>", "A free-text description stored in the profile.").action(async (name, options) => {
60594
+ ).option("--extends <profile>", "A profile this one extends (repeatable, in order).", collectRepeated).option("--description <text>", "A free-text description stored in the profile.").option("--json", "Print the result as JSON.").action(async (name, allOptions) => {
60595
+ const { json: json2, ...options } = allOptions;
60588
60596
  const hasOptions = options.extends !== void 0 || options.description !== void 0;
60589
60597
  if (!hasOptions && deps.isInteractive()) {
60590
60598
  const result = await runProfileWizard(deps.prompts, name === void 0 ? { paths } : { paths, createName: name });
60591
60599
  if (result === void 0) {
60592
60600
  throw new PromptCancelledError();
60593
60601
  }
60594
- console.log(`Created configuration profile "${result.name}".`);
60602
+ reportMutation(json2, { action: "created", kind: "profile", name: result.name, value: readProfile(paths, result.name) }, () => {
60603
+ console.log(`Created configuration profile "${result.name}".`);
60604
+ });
60595
60605
  return;
60596
60606
  }
60597
60607
  if (name === void 0) {
60598
60608
  throw new UsageError("missing required argument 'name' (standard input is not a terminal, so there is nothing to prompt on).");
60599
60609
  }
60600
- createProfile(paths, name, options.extends, options.description);
60601
- console.log(`Created configuration profile "${name}".`);
60610
+ const created = createProfile(paths, name, options.extends, options.description);
60611
+ reportMutation(json2, { action: "created", kind: "profile", name, value: created }, () => {
60612
+ console.log(`Created configuration profile "${name}".`);
60613
+ });
60602
60614
  }),
60603
60615
  ["agent-shim profile add client-acme", "agent-shim profile add client-acme --extends work-default --extends strict"]
60604
60616
  );
60605
60617
  withExamples(
60606
60618
  profile.command("set <name>").description(
60607
60619
  "Update a configuration profile's categories, entries, extends list, description or launch settings. With no options on a terminal, walks through its categories."
60608
- ).option("--category <category=bool>", "Share (true) or hide (false) a whole category (repeatable).", collectBoolPair).option("--entry <path=bool>", "Share (true) or hide (false) one <category>/<path> entry (repeatable).", collectBoolPair).option("--extends <profile>", "Replace the extends list with these profiles (repeatable, in order).", collectRepeated).option("--no-extends", "Clear the extends list.").option("--description <text>", "Replace the description.").option("--no-description", "Clear the description.").option("--launch-skip-permissions", "Launches under this profile skip permission prompts.").option("--no-launch-skip-permissions", "Launches under this profile keep permission prompts.").option("--launch-remote-control", "Launches under this profile enable Remote Control.").option("--no-launch-remote-control", "Launches under this profile leave Remote Control off.").option("--launch-headroom", "Launches under this profile route through headroom.").option("--no-launch-headroom", "Launches under this profile route direct.").option("--launch-track-usage", "Launches under this profile route through the front door so their requests and quota are recorded, with no headroom needed.").option("--no-launch-track-usage", "Launches under this profile are not recorded unless a provider or headroom routes them anyway.").option("--launch-provider <provider>", "Launches under this profile route through this provider.").option("--no-launch-provider", "Clear this profile's provider selection.").action(async (name, options) => {
60620
+ ).option("--category <category=bool>", "Share (true) or hide (false) a whole category (repeatable).", collectBoolPair).option("--entry <path=bool>", "Share (true) or hide (false) one <category>/<path> entry (repeatable).", collectBoolPair).option("--extends <profile>", "Replace the extends list with these profiles (repeatable, in order).", collectRepeated).option("--no-extends", "Clear the extends list.").option("--description <text>", "Replace the description.").option("--no-description", "Clear the description.").option("--launch-skip-permissions", "Launches under this profile skip permission prompts.").option("--no-launch-skip-permissions", "Launches under this profile keep permission prompts.").option("--launch-remote-control", "Launches under this profile enable Remote Control.").option("--no-launch-remote-control", "Launches under this profile leave Remote Control off.").option("--launch-headroom", "Launches under this profile route through headroom.").option("--no-launch-headroom", "Launches under this profile route direct.").option("--launch-track-usage", "Launches under this profile route through the front door so their requests and quota are recorded, with no headroom needed.").option("--no-launch-track-usage", "Launches under this profile are not recorded unless a provider or headroom routes them anyway.").option("--launch-provider <provider>", "Launches under this profile route through this provider.").option("--no-launch-provider", "Clear this profile's provider selection.").option("--json", "Print the result as JSON.").action(async (name, allOptions) => {
60621
+ const { json: json2, ...options } = allOptions;
60609
60622
  requireProfileExists(paths, name);
60610
- if (Object.values(options).every((value) => value === void 0)) {
60623
+ if (Object.entries(allOptions).every(([key, value]) => key === "json" || value === void 0)) {
60611
60624
  if (!deps.isInteractive()) {
60612
60625
  throw new UsageError(
60613
60626
  "Nothing to change: pass --category, --entry, --extends, --description or a --launch-* option (standard input is not a terminal, so there is nothing to prompt on)."
@@ -60619,7 +60632,9 @@ function registerProfileCommand(program2, deps) {
60619
60632
  } else {
60620
60633
  applyProfileSet(paths, name, options);
60621
60634
  }
60622
- console.log(`Updated configuration profile "${name}".`);
60635
+ reportMutation(json2, { action: "updated", kind: "profile", name, value: readProfile(paths, name) }, () => {
60636
+ console.log(`Updated configuration profile "${name}".`);
60637
+ });
60623
60638
  }),
60624
60639
  [
60625
60640
  "agent-shim profile set client-acme --category history=false --category knowledge=true",
@@ -60675,19 +60690,23 @@ function registerProfileCommand(program2, deps) {
60675
60690
  ["agent-shim profile show client-acme", "agent-shim profile show client-acme --json"]
60676
60691
  );
60677
60692
  withExamples(
60678
- profile.command("remove <name>").description("Delete a configuration profile's file. References to it by name are left for `agent-shim doctor` to report.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").action(async (name, options) => {
60693
+ profile.command("remove <name>").description("Delete a configuration profile's file. References to it by name are left for `agent-shim doctor` to report.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").option("--json", "Print the result as JSON.").action(async (name, options) => {
60679
60694
  requireProfileExists(paths, name);
60680
60695
  await confirmRemoval(deps, options.yes, `configuration profile "${name}"`);
60681
60696
  removeProfile(paths, name);
60682
- console.log(`Removed configuration profile "${name}".`);
60697
+ reportMutation(options.json, { action: "removed", kind: "profile", name }, () => {
60698
+ console.log(`Removed configuration profile "${name}".`);
60699
+ });
60683
60700
  }),
60684
60701
  ["agent-shim profile remove client-acme --yes"]
60685
60702
  );
60686
60703
  withExamples(
60687
- profile.command("use <name>").description("Make a configuration profile the global default, used when no directory rule or identity selects one.").action(async (name) => {
60704
+ profile.command("use <name>").description("Make a configuration profile the global default, used when no directory rule or identity selects one.").option("--json", "Print the result as JSON.").action(async (name, options) => {
60688
60705
  await ensureProfileExists(deps, name);
60689
60706
  setGlobalDefaultProfile(paths, name);
60690
- console.log(`Global default configuration profile is now "${name}".`);
60707
+ reportMutation(options.json, { action: "selected", kind: "profile", name }, () => {
60708
+ console.log(`Global default configuration profile is now "${name}".`);
60709
+ });
60691
60710
  }),
60692
60711
  ["agent-shim profile use work-default"]
60693
60712
  );
@@ -60732,7 +60751,7 @@ async function runIdentityWizard(prompts, paths, name, options = {}) {
60732
60751
  prompts.outro(`Identity "${name}" is set up and active.`);
60733
60752
  return true;
60734
60753
  }
60735
- async function selectIdentity(deps, name) {
60754
+ async function selectIdentity(deps, name, json2) {
60736
60755
  if (poolNameOf(name) !== void 0 || identityExists(deps.paths, name)) {
60737
60756
  useIdentity(deps.paths, name);
60738
60757
  } else if (deps.isInteractive()) {
@@ -60742,7 +60761,9 @@ async function selectIdentity(deps, name) {
60742
60761
  } else {
60743
60762
  throw new IdentityNotFoundError(name);
60744
60763
  }
60745
- console.log(`Active identity is now "${name}".`);
60764
+ reportMutation(json2, { action: "selected", kind: "identity", name }, () => {
60765
+ console.log(`Active identity is now "${name}".`);
60766
+ });
60746
60767
  }
60747
60768
  async function tryRunAtIdentityShortcut(deps, argv) {
60748
60769
  if (argv.length !== 1) {
@@ -60784,9 +60805,11 @@ function registerIdentityCommand(program2, deps) {
60784
60805
  ["agent-shim identity add work", "agent-shim identity list"]
60785
60806
  );
60786
60807
  withExamples(
60787
- identity.command("add <name>").description("Create a new identity. Fails if one with this name already exists.").action((name) => {
60788
- addIdentity(paths, name);
60789
- console.log(`Created identity "${name}".`);
60808
+ identity.command("add <name>").description("Create a new identity. Fails if one with this name already exists.").option("--json", "Print the result as JSON.").action((name, options) => {
60809
+ const created = addIdentity(paths, name);
60810
+ reportMutation(options.json, { action: "created", kind: "identity", name, value: created }, () => {
60811
+ console.log(`Created identity "${name}".`);
60812
+ });
60790
60813
  }),
60791
60814
  ["agent-shim identity add work"]
60792
60815
  );
@@ -60833,7 +60856,7 @@ function registerIdentityCommand(program2, deps) {
60833
60856
  }),
60834
60857
  ["agent-shim identity show work", "agent-shim identity show work --json"]
60835
60858
  );
60836
- const identitySet = identity.command("set <name>").description("Update an identity's settings.").option("--default-profile <profile>", "Configuration profile this identity uses when nothing more specific selects one.").option("--no-default-profile", "Clear this identity's default configuration profile.").option("--allow-ambient-credential", "Allow this identity to launch even with an ambient credential env var set.").option("--no-allow-ambient-credential", "Disallow ambient credential env vars for this identity (the default).").option(
60859
+ const identitySet = identity.command("set <name>").description("Update an identity's settings.").option("--json", "Print the result as JSON.").option("--default-profile <profile>", "Configuration profile this identity uses when nothing more specific selects one.").option("--no-default-profile", "Clear this identity's default configuration profile.").option("--allow-ambient-credential", "Allow this identity to launch even with an ambient credential env var set.").option("--no-allow-ambient-credential", "Disallow ambient credential env vars for this identity (the default).").option(
60837
60860
  "--credential <source>",
60838
60861
  `Authenticate this identity from a token instead of its stored login, replacing any existing sources (repeatable, in the order tried): ${CREDENTIAL_SOURCE_SYNTAX}.`,
60839
60862
  collectCredentialSource
@@ -60845,8 +60868,9 @@ function registerIdentityCommand(program2, deps) {
60845
60868
  );
60846
60869
  addCredentialCacheOptions(identitySet);
60847
60870
  withExamples(
60848
- identitySet.action(async (name, options) => {
60849
- if (Object.values(options).every((value) => value === void 0)) {
60871
+ identitySet.action(async (name, allOptions) => {
60872
+ const { json: json2, ...options } = allOptions;
60873
+ if (Object.entries(allOptions).every(([key, value]) => key === "json" || value === void 0)) {
60850
60874
  throw new UsageError(
60851
60875
  "Nothing to change: pass --default-profile, --no-default-profile, --allow-ambient-credential, --no-allow-ambient-credential, --credential, --no-credential, --credential-target or a --credential-cache option."
60852
60876
  );
@@ -60854,25 +60878,24 @@ function registerIdentityCommand(program2, deps) {
60854
60878
  if (!identityExists(paths, name)) {
60855
60879
  throw new IdentityNotFoundError(name);
60856
60880
  }
60881
+ const messages = [];
60857
60882
  if (options.defaultProfile !== void 0) {
60858
60883
  const profileName = options.defaultProfile === false ? void 0 : options.defaultProfile;
60859
60884
  if (profileName !== void 0) {
60860
60885
  await ensureProfileExists(deps, profileName);
60861
60886
  }
60862
60887
  setDefaultConfigProfile(paths, name, profileName);
60863
- console.log(
60888
+ messages.push(
60864
60889
  profileName === void 0 ? `Identity "${name}" no longer has a default configuration profile.` : `Identity "${name}" now defaults to configuration profile "${profileName}".`
60865
60890
  );
60866
60891
  }
60867
60892
  if (options.allowAmbientCredential !== void 0) {
60868
60893
  setAllowAmbientCredential(paths, name, options.allowAmbientCredential);
60869
- console.log(
60870
- `Identity "${name}" ${options.allowAmbientCredential ? "now allows" : "no longer allows"} an ambient credential.`
60871
- );
60894
+ messages.push(`Identity "${name}" ${options.allowAmbientCredential ? "now allows" : "no longer allows"} an ambient credential.`);
60872
60895
  }
60873
60896
  if (options.credential === false) {
60874
60897
  setIdentityCredential(paths, name, false);
60875
- console.log(`Identity "${name}" no longer has a credential and uses its stored login.`);
60898
+ messages.push(`Identity "${name}" no longer has a credential and uses its stored login.`);
60876
60899
  } else if (options.credential !== void 0 || options.credentialTarget !== void 0 || cacheChange(options, void 0) !== void 0) {
60877
60900
  const cache = cacheChange(options, readIdentity(paths, name)?.credential?.cache);
60878
60901
  const updated = setIdentityCredential(paths, name, {
@@ -60881,9 +60904,14 @@ function registerIdentityCommand(program2, deps) {
60881
60904
  ...cache === void 0 ? {} : { cache }
60882
60905
  });
60883
60906
  if (updated.credential !== void 0) {
60884
- console.log(`Identity "${name}" now authenticates with credential ${describeCredential(updated.credential)}.`);
60907
+ messages.push(`Identity "${name}" now authenticates with credential ${describeCredential(updated.credential)}.`);
60885
60908
  }
60886
60909
  }
60910
+ reportMutation(json2, { action: "updated", kind: "identity", name, value: readIdentity(paths, name) }, () => {
60911
+ for (const message of messages) {
60912
+ console.log(message);
60913
+ }
60914
+ });
60887
60915
  }),
60888
60916
  [
60889
60917
  "agent-shim identity set work --default-profile client-acme",
@@ -60895,19 +60923,21 @@ function registerIdentityCommand(program2, deps) {
60895
60923
  withExamples(
60896
60924
  identity.command("remove <name>").description(
60897
60925
  "Delete an identity and its directory, including its credentials and anything it does not share with ~/.claude. Shared data in ~/.claude is kept."
60898
- ).option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").action(async (name, options) => {
60926
+ ).option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").option("--json", "Print the result as JSON.").action(async (name, options) => {
60899
60927
  if (!identityExists(paths, name)) {
60900
60928
  throw new IdentityNotFoundError(name);
60901
60929
  }
60902
60930
  await confirmRemoval(deps, options.yes, `identity "${name}" and its directory ${import_node_path20.default.join(paths.identitiesDir, name)}`);
60903
60931
  removeIdentity(paths, name);
60904
- console.log(`Removed identity "${name}".`);
60932
+ reportMutation(options.json, { action: "removed", kind: "identity", name }, () => {
60933
+ console.log(`Removed identity "${name}".`);
60934
+ });
60905
60935
  }),
60906
60936
  ["agent-shim identity remove old-client --yes"]
60907
60937
  );
60908
60938
  withExamples(
60909
- identity.command("use <name>").description("Select the active identity, used by every launch that names none. Offers to create it on a terminal if it does not exist.").action(async (name) => {
60910
- await selectIdentity(deps, name);
60939
+ identity.command("use <name>").description("Select the active identity, used by every launch that names none. Offers to create it on a terminal if it does not exist.").option("--json", "Print the result as JSON.").action(async (name, options) => {
60940
+ await selectIdentity(deps, name, options.json);
60911
60941
  }),
60912
60942
  ["agent-shim identity use work", "agent-shim @work"]
60913
60943
  );
@@ -60955,7 +60985,7 @@ function registerIdentityCommand(program2, deps) {
60955
60985
  // package.json
60956
60986
  var package_default = {
60957
60987
  name: "agent-shim",
60958
- version: "7.11.0",
60988
+ version: "7.12.0",
60959
60989
  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.",
60960
60990
  license: "MIT",
60961
60991
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -68598,20 +68628,24 @@ function registerPoolCommand(program2, deps) {
68598
68628
  ["agent-shim pool add subs --identity work --identity personal", "agent-shim pool pick subs"]
68599
68629
  );
68600
68630
  withExamples(
68601
- pool.command("add <name>").description("Define a pool. Fails if one with this name already exists.").option("--identity <name>", "A member identity (repeatable).", collectRepeated).action((name, options) => {
68631
+ pool.command("add <name>").description("Define a pool. Fails if one with this name already exists.").option("--identity <name>", "A member identity (repeatable).", collectRepeated).option("--json", "Print the result as JSON.").action((name, options) => {
68602
68632
  const members2 = membersOf(options);
68603
68633
  requireIdentities(paths, members2);
68604
- addPool(paths, name, members2);
68605
- console.log(`Created pool "${name}" with ${members2.join(", ")}.`);
68634
+ const created = addPool(paths, name, members2);
68635
+ reportMutation(options.json, { action: "created", kind: "pool", name, value: created }, () => {
68636
+ console.log(`Created pool "${name}" with ${members2.join(", ")}.`);
68637
+ });
68606
68638
  }),
68607
68639
  ["agent-shim pool add subs --identity work --identity personal"]
68608
68640
  );
68609
68641
  withExamples(
68610
- pool.command("set <name>").description("Replace a pool's members.").option("--identity <name>", "A member identity (repeatable); the full new list.", collectRepeated).action((name, options) => {
68642
+ pool.command("set <name>").description("Replace a pool's members.").option("--identity <name>", "A member identity (repeatable); the full new list.", collectRepeated).option("--json", "Print the result as JSON.").action((name, options) => {
68611
68643
  const members2 = membersOf(options);
68612
68644
  requireIdentities(paths, members2);
68613
- setPool(paths, name, members2);
68614
- console.log(`Pool "${name}" now has ${members2.join(", ")}.`);
68645
+ const updated = setPool(paths, name, members2);
68646
+ reportMutation(options.json, { action: "updated", kind: "pool", name, value: updated }, () => {
68647
+ console.log(`Pool "${name}" now has ${members2.join(", ")}.`);
68648
+ });
68615
68649
  }),
68616
68650
  ["agent-shim pool set subs --identity work --identity personal --identity spare"]
68617
68651
  );
@@ -68647,18 +68681,22 @@ function registerPoolCommand(program2, deps) {
68647
68681
  ["agent-shim pool show subs"]
68648
68682
  );
68649
68683
  withExamples(
68650
- pool.command("remove <name>").description("Delete a pool. Its member identities are untouched.").option("--yes", "Skip the confirmation prompt.").action(async (name, options) => {
68684
+ pool.command("remove <name>").description("Delete a pool. Its member identities are untouched.").option("--yes", "Skip the confirmation prompt.").option("--json", "Print the result as JSON.").action(async (name, options) => {
68651
68685
  requirePool(paths, name);
68652
68686
  await confirmRemoval(deps, options.yes, `pool "${name}"`);
68653
68687
  removePool(paths, name);
68654
- console.log(`Removed pool "${name}".`);
68688
+ reportMutation(options.json, { action: "removed", kind: "pool", name }, () => {
68689
+ console.log(`Removed pool "${name}".`);
68690
+ });
68655
68691
  }),
68656
68692
  ["agent-shim pool remove subs --yes"]
68657
68693
  );
68658
68694
  withExamples(
68659
- pool.command("use <name>").description("Make launches with no other selection pick a member of this pool.").action((name) => {
68695
+ pool.command("use <name>").description("Make launches with no other selection pick a member of this pool.").option("--json", "Print the result as JSON.").action((name, options) => {
68660
68696
  useIdentity(paths, `${POOL_SELECTOR_PREFIX}${name}`);
68661
- console.log(`Active selection is now pool "${name}".`);
68697
+ reportMutation(options.json, { action: "selected", kind: "pool", name }, () => {
68698
+ console.log(`Active selection is now pool "${name}".`);
68699
+ });
68662
68700
  }),
68663
68701
  ["agent-shim pool use subs"]
68664
68702
  );
@@ -68719,7 +68757,7 @@ function registerProviderCommand(program2, deps) {
68719
68757
  new Option("--credential-target <target>", "Where the token goes: bearer sets ANTHROPIC_AUTH_TOKEN (default), apiKey sets ANTHROPIC_API_KEY.").choices(
68720
68758
  PROVIDER_CREDENTIAL_TARGETS
68721
68759
  )
68722
- ).option("--env <KEY=VALUE>", "Extra environment entry for the child (repeatable).", collectStringPair).option("--codex-default-model <model>", "Codex providers: the codex model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: the codex model one tier (fable, opus, sonnet or haiku) maps to (repeatable).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: the reasoning effort when a request asks for none the backend accepts.").choices(CODEX_EFFORTS)).action((name, options) => {
68760
+ ).option("--env <KEY=VALUE>", "Extra environment entry for the child (repeatable).", collectStringPair).option("--codex-default-model <model>", "Codex providers: the codex model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: the codex model one tier (fable, opus, sonnet or haiku) maps to (repeatable).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: the reasoning effort when a request asks for none the backend accepts.").choices(CODEX_EFFORTS)).option("--json", "Print the result as JSON.").action((name, options) => {
68723
68761
  const codex = codexConfigFromOptions(options);
68724
68762
  const created = addProvider(paths, name, {
68725
68763
  ...options.kind === void 0 ? {} : { kind: options.kind },
@@ -68730,7 +68768,9 @@ function registerProviderCommand(program2, deps) {
68730
68768
  ...options.env === void 0 ? {} : { env: options.env },
68731
68769
  ...codex === void 0 ? {} : { codex }
68732
68770
  });
68733
- console.log(`Created provider "${name}" (${describeProviderEndpoint(created)}, credential ${describeCredential(created.credential)}).`);
68771
+ reportMutation(options.json, { action: "created", kind: "provider", name, value: created }, () => {
68772
+ console.log(`Created provider "${name}" (${describeProviderEndpoint(created)}, credential ${describeCredential(created.credential)}).`);
68773
+ });
68734
68774
  }),
68735
68775
  [
68736
68776
  "agent-shim provider add z --display-name z.ai --base-url https://api.z.ai/api/anthropic --credential env:Z_API_TOKEN",
@@ -68752,8 +68792,9 @@ function registerProviderCommand(program2, deps) {
68752
68792
  ).option("--env <KEY=VALUE>", "Add or replace an environment entry for the child (repeatable).", collectStringPair).option("--unset-env <KEY>", "Remove an environment entry (repeatable).", collectRepeated).option("--codex-default-model <model>", "Codex providers: replace the model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: set the codex model one tier maps to (repeatable; other tiers are kept).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: replace the default reasoning effort.").choices(CODEX_EFFORTS));
68753
68793
  addCredentialCacheOptions(providerSet);
68754
68794
  withExamples(
68755
- providerSet.action((name, options) => {
68756
- if (Object.values(options).every((value) => value === void 0)) {
68795
+ providerSet.option("--json", "Print the result as JSON.").action((name, allOptions) => {
68796
+ const { json: json2, ...options } = allOptions;
68797
+ if (Object.entries(allOptions).every(([key, value]) => key === "json" || value === void 0)) {
68757
68798
  throw new UsageError(
68758
68799
  "Nothing to change: pass --display-name, --base-url, --credential, --credential-target, a --credential-cache option, --env, --unset-env, --codex-default-model, --codex-model or --codex-effort."
68759
68800
  );
@@ -68770,7 +68811,9 @@ function registerProviderCommand(program2, deps) {
68770
68811
  ...options.env === void 0 ? {} : { env: options.env },
68771
68812
  ...options.unsetEnv === void 0 ? {} : { unsetEnv: options.unsetEnv }
68772
68813
  });
68773
- console.log(`Updated provider "${name}" (${describeProviderEndpoint(updated)}, credential ${describeCredential(updated.credential)}).`);
68814
+ reportMutation(json2, { action: "updated", kind: "provider", name, value: updated }, () => {
68815
+ console.log(`Updated provider "${name}" (${describeProviderEndpoint(updated)}, credential ${describeCredential(updated.credential)}).`);
68816
+ });
68774
68817
  }),
68775
68818
  [
68776
68819
  "agent-shim provider set z --base-url https://api.z.ai/api/anthropic",
@@ -68824,13 +68867,15 @@ function registerProviderCommand(program2, deps) {
68824
68867
  ["agent-shim provider show z", "agent-shim provider show z --json"]
68825
68868
  );
68826
68869
  withExamples(
68827
- provider.command("remove <name>").description("Delete a provider definition.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").action(async (name, options) => {
68870
+ provider.command("remove <name>").description("Delete a provider definition.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").option("--json", "Print the result as JSON.").action(async (name, options) => {
68828
68871
  if (!providerExists(paths, name)) {
68829
68872
  throw new ProviderNotFoundError(name);
68830
68873
  }
68831
68874
  await confirmRemoval(deps, options.yes, `provider "${name}"`);
68832
68875
  removeProvider(paths, name);
68833
- console.log(`Removed provider "${name}".`);
68876
+ reportMutation(options.json, { action: "removed", kind: "provider", name }, () => {
68877
+ console.log(`Removed provider "${name}".`);
68878
+ });
68834
68879
  }),
68835
68880
  ["agent-shim provider remove z --yes"]
68836
68881
  );
@@ -68856,21 +68901,27 @@ function registerRuleCommand(program2, deps) {
68856
68901
  ["agent-shim rule add ~/work/acme --config-profile client-acme", "agent-shim rule list"]
68857
68902
  );
68858
68903
  withExamples(
68859
- rule.command("add <path>").description("Add a directory rule for a path. Fails if a rule for that exact path already exists.").option("--config-profile <profile>", "Configuration profile to select under this path.").option("--identity <identity>", "Identity to pin under this path.").action(async (rulePath, options) => {
68904
+ rule.command("add <path>").description("Add a directory rule for a path. Fails if a rule for that exact path already exists.").option("--config-profile <profile>", "Configuration profile to select under this path.").option("--identity <identity>", "Identity to pin under this path.").option("--json", "Print the result as JSON.").action(async (rulePath, allOptions) => {
68905
+ const { json: json2, ...options } = allOptions;
68860
68906
  await checkRuleTargets(deps, options);
68861
- addDirectoryRule(paths, rulePath, options);
68862
- console.log(`Added directory rule for "${rulePath}".`);
68907
+ const created = addDirectoryRule(paths, rulePath, options);
68908
+ reportMutation(json2, { action: "created", kind: "rule", name: rulePath, value: created }, () => {
68909
+ console.log(`Added directory rule for "${rulePath}".`);
68910
+ });
68863
68911
  }),
68864
68912
  ["agent-shim rule add ~/work/acme --config-profile client-acme", "agent-shim rule add ~/personal --identity personal"]
68865
68913
  );
68866
68914
  withExamples(
68867
- rule.command("set <path>").description("Update the directory rule for a path.").option("--config-profile <profile>", "Configuration profile to select under this path.").option("--no-config-profile", "Stop selecting a configuration profile under this path.").option("--identity <identity>", "Identity to pin under this path.").option("--no-identity", "Stop pinning an identity under this path.").action(async (rulePath, options) => {
68915
+ rule.command("set <path>").description("Update the directory rule for a path.").option("--config-profile <profile>", "Configuration profile to select under this path.").option("--no-config-profile", "Stop selecting a configuration profile under this path.").option("--identity <identity>", "Identity to pin under this path.").option("--no-identity", "Stop pinning an identity under this path.").option("--json", "Print the result as JSON.").action(async (rulePath, allOptions) => {
68916
+ const { json: json2, ...options } = allOptions;
68868
68917
  if (options.configProfile === void 0 && options.identity === void 0) {
68869
68918
  throw new UsageError("Nothing to change: pass --config-profile, --no-config-profile, --identity or --no-identity.");
68870
68919
  }
68871
68920
  await checkRuleTargets(deps, options);
68872
- updateDirectoryRule(paths, rulePath, options);
68873
- console.log(`Updated directory rule for "${rulePath}".`);
68921
+ const updated = updateDirectoryRule(paths, rulePath, options);
68922
+ reportMutation(json2, { action: "updated", kind: "rule", name: rulePath, value: updated }, () => {
68923
+ console.log(`Updated directory rule for "${rulePath}".`);
68924
+ });
68874
68925
  }),
68875
68926
  ["agent-shim rule set ~/work/acme --identity work", "agent-shim rule set ~/work/acme --no-config-profile"]
68876
68927
  );
@@ -68907,13 +68958,15 @@ function registerRuleCommand(program2, deps) {
68907
68958
  ["agent-shim rule show ~/work/acme"]
68908
68959
  );
68909
68960
  withExamples(
68910
- rule.command("remove <path>").description("Remove the directory rule for a path.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").action(async (rulePath, options) => {
68961
+ rule.command("remove <path>").description("Remove the directory rule for a path.").option("--yes", "Remove without asking for confirmation (required when standard input is not a terminal).").option("--json", "Print the result as JSON.").action(async (rulePath, options) => {
68911
68962
  if (!listDirectoryRules(paths).some((entry) => entry.path === rulePath)) {
68912
68963
  throw new DirectoryRuleNotFoundError(rulePath);
68913
68964
  }
68914
68965
  await confirmRemoval(deps, options.yes, `the directory rule for "${rulePath}"`);
68915
68966
  removeDirectoryRule(paths, rulePath);
68916
- console.log(`Removed directory rule for "${rulePath}".`);
68967
+ reportMutation(options.json, { action: "removed", kind: "rule", name: rulePath }, () => {
68968
+ console.log(`Removed directory rule for "${rulePath}".`);
68969
+ });
68917
68970
  }),
68918
68971
  ["agent-shim rule remove ~/work/acme --yes"]
68919
68972
  );
@@ -15,6 +15,17 @@ export interface CommandDeps {
15
15
  }
16
16
  /** Prints `value` as indented JSON on standard output: the one `--json` output format every `list`, `show`, `check`, `doctor` and `headroom status` command shares. */
17
17
  export declare function printJson(value: unknown): void;
18
+ /** What a mutating command did, as the one object `--json` prints in place of its text output. */
19
+ export interface MutationReport {
20
+ readonly action: "created" | "updated" | "removed" | "selected";
21
+ /** The noun the command works on: `identity`, `profile`, `provider`, `pool` or `rule`. */
22
+ readonly kind: string;
23
+ readonly name: string;
24
+ /** The stored object after the change; absent for a removal and for a selection. */
25
+ readonly value?: unknown;
26
+ }
27
+ /** Reports a mutating command's outcome: the `MutationReport` as JSON under `--json`, otherwise whatever `printText` prints. */
28
+ export declare function reportMutation(json: boolean | undefined, report: MutationReport, printText: () => void): void;
18
29
  /**
19
30
  * Appends an `Examples:` block to `command`'s help output, one `$ agent-shim ...` line per entry. Every command registers at least one, so `--help` on any of them shows a runnable invocation, not just its option list.
20
31
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-shim",
3
- "version": "7.11.0",
3
+ "version": "7.12.0",
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": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",