@jpcasabiancai/cayenne 0.5.7 → 0.5.8

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 (2) hide show
  1. package/dist/cli.js +413 -242
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1325,7 +1325,7 @@ var init_local = __esm({
1325
1325
 
1326
1326
  // ../../packages/shared/src/protocol/runner.ts
1327
1327
  import { z as z14 } from "zod";
1328
- var ProviderModel, ProviderCapability, UNAVAILABLE, RunnerCapabilities, CODEX_TOOL_NAMES;
1328
+ var ProviderModel, ProviderAuthMode, ProviderCapability, UNAVAILABLE, RunnerCapabilities, CODEX_TOOL_NAMES;
1329
1329
  var init_runner = __esm({
1330
1330
  "../../packages/shared/src/protocol/runner.ts"() {
1331
1331
  "use strict";
@@ -1338,13 +1338,21 @@ var init_runner = __esm({
1338
1338
  /** From the app-server's `supportedReasoningEfforts`; empty means the control is hidden. */
1339
1339
  efforts: z14.array(Effort).default([]),
1340
1340
  /** The effort a new chat on this model opens at, sent back on `session.start`. */
1341
- defaultEffort: Effort.optional()
1341
+ defaultEffort: Effort.optional(),
1342
+ /** One line about the model in the provider's own words (Claude's `ModelInfo.description`). */
1343
+ description: z14.string().min(1).max(200).optional()
1342
1344
  });
1345
+ ProviderAuthMode = z14.enum(["subscription", "api", "third_party"]);
1343
1346
  ProviderCapability = z14.object({
1344
1347
  available: z14.boolean(),
1345
1348
  reason: z14.enum(["not_installed", "not_logged_in", "error"]).optional(),
1346
1349
  version: z14.string().max(50).optional(),
1347
- models: z14.array(ProviderModel).max(50).default([])
1350
+ models: z14.array(ProviderModel).max(50).default([]),
1351
+ /**
1352
+ * How the provider is signed in, when the runner can tell without opening a chat (Claude's
1353
+ * `accountInfo`, protocol §9). Absent means unknown. Optional so an older row still parses.
1354
+ */
1355
+ authMode: ProviderAuthMode.optional()
1348
1356
  });
1349
1357
  UNAVAILABLE = { available: false, reason: "not_installed", models: [] };
1350
1358
  RunnerCapabilities = z14.object({
@@ -2624,13 +2632,200 @@ var init_version = __esm({
2624
2632
  }
2625
2633
  });
2626
2634
 
2635
+ // src/providers/env.ts
2636
+ function definedEnv(env) {
2637
+ const out = {};
2638
+ for (const [key, value] of Object.entries(env)) if (value !== void 0) out[key] = value;
2639
+ return out;
2640
+ }
2641
+ var init_env = __esm({
2642
+ "src/providers/env.ts"() {
2643
+ "use strict";
2644
+ }
2645
+ });
2646
+
2647
+ // src/claude/auth-mode.ts
2648
+ function authModeFrom(info) {
2649
+ if (info.apiProvider && info.apiProvider !== "firstParty") return "third_party";
2650
+ if (info.apiKeySource && info.apiKeySource !== "none") return "api";
2651
+ if (info.subscriptionType) return "subscription";
2652
+ return null;
2653
+ }
2654
+ var init_auth_mode = __esm({
2655
+ "src/claude/auth-mode.ts"() {
2656
+ "use strict";
2657
+ }
2658
+ });
2659
+
2660
+ // src/claude/pushable.ts
2661
+ var Pushable;
2662
+ var init_pushable = __esm({
2663
+ "src/claude/pushable.ts"() {
2664
+ "use strict";
2665
+ Pushable = class {
2666
+ queue = [];
2667
+ waiters = [];
2668
+ ended = false;
2669
+ push(value) {
2670
+ if (this.ended) throw new Error("Pushable already ended");
2671
+ const waiter = this.waiters.shift();
2672
+ if (waiter) waiter({ value, done: false });
2673
+ else this.queue.push(value);
2674
+ }
2675
+ end() {
2676
+ this.ended = true;
2677
+ for (const waiter of this.waiters.splice(0)) waiter({ value: void 0, done: true });
2678
+ }
2679
+ [Symbol.asyncIterator]() {
2680
+ return {
2681
+ next: () => {
2682
+ const value = this.queue.shift();
2683
+ if (value !== void 0) return Promise.resolve({ value, done: false });
2684
+ if (this.ended) return Promise.resolve({ value: void 0, done: true });
2685
+ return new Promise((resolve6) => this.waiters.push(resolve6));
2686
+ }
2687
+ };
2688
+ }
2689
+ };
2690
+ }
2691
+ });
2692
+
2693
+ // src/claude/detect.ts
2694
+ import { readFileSync as readFileSync4 } from "fs";
2695
+ import { createRequire as createRequire2 } from "module";
2696
+ import { dirname as dirname3, join as join8 } from "path";
2697
+ import {
2698
+ query as sdkQuery
2699
+ } from "@anthropic-ai/claude-agent-sdk";
2700
+ function claudeCodeVersion(resolve6 = createRequire2(import.meta.url).resolve) {
2701
+ try {
2702
+ const entry = resolve6("@anthropic-ai/claude-agent-sdk");
2703
+ const pkg = JSON.parse(readFileSync4(join8(dirname3(entry), "package.json"), "utf8"));
2704
+ return typeof pkg.claudeCodeVersion === "string" && pkg.claudeCodeVersion ? pkg.claudeCodeVersion.slice(0, MAX_VERSION) : void 0;
2705
+ } catch {
2706
+ return void 0;
2707
+ }
2708
+ }
2709
+ async function probeClaude(opts) {
2710
+ const queryFn = opts.queryFn ?? sdkQuery;
2711
+ const prompt = new Pushable();
2712
+ const abortController = new AbortController();
2713
+ let timer;
2714
+ let q;
2715
+ try {
2716
+ q = queryFn({
2717
+ prompt,
2718
+ options: {
2719
+ cwd: opts.cwd,
2720
+ settingSources: opts.settingSources,
2721
+ strictMcpConfig: true,
2722
+ mcpServers: {},
2723
+ settings: { disableAllHooks: true },
2724
+ abortController,
2725
+ stderr: () => {
2726
+ },
2727
+ env: definedEnv({ ...process.env, ...opts.env })
2728
+ }
2729
+ });
2730
+ const init = q.initializationResult();
2731
+ init.catch(() => {
2732
+ });
2733
+ const timeoutMs = opts.timeoutMs ?? PROBE_TIMEOUT_MS;
2734
+ const timeout = new Promise((_, reject) => {
2735
+ timer = setTimeout(() => {
2736
+ abortController.abort();
2737
+ reject(new Error(`Claude probe timed out after ${timeoutMs} ms`));
2738
+ }, timeoutMs);
2739
+ });
2740
+ const res = await Promise.race([init, timeout]);
2741
+ return { account: res.account ?? {}, models: res.models ?? [] };
2742
+ } finally {
2743
+ clearTimeout(timer);
2744
+ prompt.end();
2745
+ try {
2746
+ q?.close();
2747
+ } catch {
2748
+ }
2749
+ }
2750
+ }
2751
+ function toProviderModel(m, id, isDefault) {
2752
+ const levels = m.supportedEffortLevels ?? [];
2753
+ const efforts = levels.filter((e) => Effort.options.includes(e));
2754
+ const description = (m.description ?? "").slice(0, MAX_NAME);
2755
+ return {
2756
+ id: id.slice(0, MAX_NAME),
2757
+ // `||`, not `??`: an empty display name would fail `label.min(1)` and take the whole
2758
+ // capability with it.
2759
+ label: (m.displayName || id).slice(0, MAX_NAME),
2760
+ ...isDefault ? { default: true } : {},
2761
+ ...description ? { description } : {},
2762
+ efforts
2763
+ };
2764
+ }
2765
+ function toProviderModels(models) {
2766
+ const fallback = models.find((m) => m.value === "default");
2767
+ const resolved = fallback?.resolvedModel;
2768
+ const rows = models.filter((m) => m.value !== "default");
2769
+ const match = resolved ? rows.findIndex((m) => (m.resolvedModel ?? m.value) === resolved) : -1;
2770
+ const mapped = rows.map((m, i) => toProviderModel(m, m.value ?? "", i === match));
2771
+ if (fallback && resolved && match === -1) {
2772
+ mapped.unshift(toProviderModel(fallback, resolved, true));
2773
+ }
2774
+ const seen = /* @__PURE__ */ new Set();
2775
+ return mapped.filter((m) => !seen.has(m.id) && seen.add(m.id)).slice(0, MAX_MODELS).filter((m) => ProviderModel.safeParse(m).success);
2776
+ }
2777
+ async function detectClaude(deps) {
2778
+ let version;
2779
+ try {
2780
+ version = deps.version();
2781
+ } catch {
2782
+ version = void 0;
2783
+ }
2784
+ const withVersion = version ? { version } : {};
2785
+ let res;
2786
+ try {
2787
+ res = await deps.probe();
2788
+ } catch (err) {
2789
+ const message = err instanceof Error ? err.message : String(err);
2790
+ if (/native binary not found/i.test(message)) {
2791
+ return { available: false, reason: "not_installed", ...withVersion, models: [] };
2792
+ }
2793
+ return { available: true, ...withVersion, models: [] };
2794
+ }
2795
+ const account = res.account ?? {};
2796
+ const authMode = authModeFrom(account);
2797
+ if (authMode === null && account.tokenSource === "none") {
2798
+ return { available: false, reason: "not_logged_in", ...withVersion, models: [] };
2799
+ }
2800
+ return {
2801
+ available: true,
2802
+ ...withVersion,
2803
+ models: toProviderModels(res.models ?? []),
2804
+ ...authMode ? { authMode } : {}
2805
+ };
2806
+ }
2807
+ var PROBE_TIMEOUT_MS, MAX_MODELS, MAX_NAME, MAX_VERSION;
2808
+ var init_detect = __esm({
2809
+ "src/claude/detect.ts"() {
2810
+ "use strict";
2811
+ init_src();
2812
+ init_env();
2813
+ init_auth_mode();
2814
+ init_pushable();
2815
+ PROBE_TIMEOUT_MS = 15e3;
2816
+ MAX_MODELS = 50;
2817
+ MAX_NAME = 200;
2818
+ MAX_VERSION = 50;
2819
+ }
2820
+ });
2821
+
2627
2822
  // src/exec-path.ts
2628
2823
  import { accessSync, constants, statSync as statSync3 } from "fs";
2629
- import { delimiter, isAbsolute as isAbsolute5, join as join8 } from "path";
2824
+ import { delimiter, isAbsolute as isAbsolute5, join as join9 } from "path";
2630
2825
  function findOnPath(name, path) {
2631
2826
  for (const dir of (path ?? "").split(delimiter)) {
2632
2827
  if (!isAbsolute5(dir)) continue;
2633
- const candidate = join8(dir, name);
2828
+ const candidate = join9(dir, name);
2634
2829
  try {
2635
2830
  if (!statSync3(candidate).isFile()) continue;
2636
2831
  accessSync(candidate, constants.X_OK);
@@ -2786,7 +2981,7 @@ async function allModels(request) {
2786
2981
  const data = page.data ?? [];
2787
2982
  models.push(...data);
2788
2983
  cursor = data.length > 0 && page.nextCursor ? page.nextCursor : void 0;
2789
- } while (cursor && models.length < MAX_MODELS);
2984
+ } while (cursor && models.length < MAX_MODELS2);
2790
2985
  return models;
2791
2986
  }
2792
2987
  function findCodexOnPath(env) {
@@ -2797,7 +2992,7 @@ function resolveCodexPath(cfg, env, onPath) {
2797
2992
  }
2798
2993
  async function runCodex(path, args) {
2799
2994
  try {
2800
- const { stdout, stderr } = await exec(path, args, { timeout: PROBE_TIMEOUT_MS });
2995
+ const { stdout, stderr } = await exec(path, args, { timeout: PROBE_TIMEOUT_MS2 });
2801
2996
  return { code: 0, stdout, stderr };
2802
2997
  } catch (err) {
2803
2998
  const e = err;
@@ -2824,22 +3019,22 @@ async function listCodexModels(path) {
2824
3019
  child.kill();
2825
3020
  }
2826
3021
  }
2827
- function toProviderModel(m) {
3022
+ function toProviderModel2(m) {
2828
3023
  const efforts = (m.supportedReasoningEfforts ?? []).map((e) => e.reasoningEffort).filter((e) => Effort.options.includes(e));
2829
3024
  const fallback = Effort.options.includes(m.defaultReasoningEffort) ? m.defaultReasoningEffort : void 0;
2830
3025
  return {
2831
- id: (m.id ?? "").slice(0, MAX_NAME),
3026
+ id: (m.id ?? "").slice(0, MAX_NAME2),
2832
3027
  // `||`, not `??`: an app-server that sends `displayName: ''` would otherwise publish an
2833
3028
  // empty label, which `ProviderModel.label.min(1)` rejects — taking the whole capability
2834
3029
  // with it.
2835
- label: (m.displayName || m.id || "").slice(0, MAX_NAME),
3030
+ label: (m.displayName || m.id || "").slice(0, MAX_NAME2),
2836
3031
  ...m.isDefault ? { default: true } : {},
2837
3032
  efforts,
2838
3033
  ...fallback ? { defaultEffort: fallback } : {}
2839
3034
  };
2840
3035
  }
2841
- function toProviderModels(models) {
2842
- return models.filter((m) => m.hidden !== true).slice(0, MAX_MODELS).map(toProviderModel).filter((m) => ProviderModel.safeParse(m).success);
3036
+ function toProviderModels2(models) {
3037
+ return models.filter((m) => m.hidden !== true).slice(0, MAX_MODELS2).map(toProviderModel2).filter((m) => ProviderModel.safeParse(m).success);
2843
3038
  }
2844
3039
  async function detectCodex(deps) {
2845
3040
  if (!deps.path) return { available: false, reason: "not_installed", models: [] };
@@ -2847,33 +3042,37 @@ async function detectCodex(deps) {
2847
3042
  try {
2848
3043
  const probe = await deps.run(deps.path, ["--version"]);
2849
3044
  if (probe.code !== 0) return { available: false, reason: "not_installed", models: [] };
2850
- version = probe.stdout.trim().split(/\s+/).at(-1)?.slice(0, MAX_VERSION);
3045
+ version = probe.stdout.trim().split(/\s+/).at(-1)?.slice(0, MAX_VERSION2);
2851
3046
  const login2 = await deps.run(deps.path, ["login", "status"]);
2852
3047
  if (login2.code !== 0) {
2853
3048
  return { available: false, reason: "not_logged_in", version, models: [] };
2854
3049
  }
2855
3050
  const models = await deps.listModels(deps.path);
2856
- return { available: true, version, models: toProviderModels(models) };
3051
+ return { available: true, version, models: toProviderModels2(models) };
2857
3052
  } catch {
2858
3053
  return { available: false, reason: "error", ...version ? { version } : {}, models: [] };
2859
3054
  }
2860
3055
  }
2861
3056
  async function detectProviders(deps) {
2862
- return { claude: { available: true, models: [] }, codex: await detectCodex(deps) };
3057
+ const [claude, codex] = await Promise.all([
3058
+ deps.claude().catch(() => ({ available: true, models: [] })),
3059
+ detectCodex(deps)
3060
+ ]);
3061
+ return { claude, codex };
2863
3062
  }
2864
- var exec, PROBE_TIMEOUT_MS, LIST_TIMEOUT_MS, MAX_MODELS, MAX_NAME, MAX_VERSION;
2865
- var init_detect = __esm({
3063
+ var exec, PROBE_TIMEOUT_MS2, LIST_TIMEOUT_MS, MAX_MODELS2, MAX_NAME2, MAX_VERSION2;
3064
+ var init_detect2 = __esm({
2866
3065
  "src/codex/detect.ts"() {
2867
3066
  "use strict";
2868
3067
  init_src();
2869
3068
  init_exec_path();
2870
3069
  init_rpc();
2871
3070
  exec = promisify(execFile2);
2872
- PROBE_TIMEOUT_MS = 5e3;
3071
+ PROBE_TIMEOUT_MS2 = 5e3;
2873
3072
  LIST_TIMEOUT_MS = 1e4;
2874
- MAX_MODELS = 50;
2875
- MAX_NAME = 200;
2876
- MAX_VERSION = 50;
3073
+ MAX_MODELS2 = 50;
3074
+ MAX_NAME2 = 200;
3075
+ MAX_VERSION2 = 50;
2877
3076
  }
2878
3077
  });
2879
3078
 
@@ -3517,7 +3716,7 @@ var init_source = __esm({
3517
3716
 
3518
3717
  // src/paths.ts
3519
3718
  import { lstatSync, realpathSync as realpathSync5 } from "fs";
3520
- import { dirname as dirname3, isAbsolute as isAbsolute6, join as join9, relative as relative2, sep as sep2 } from "path";
3719
+ import { dirname as dirname4, isAbsolute as isAbsolute6, join as join10, relative as relative2, sep as sep2 } from "path";
3521
3720
  function assertInsideRoots(target, roots) {
3522
3721
  return resolveInsideRoots(target, roots).real;
3523
3722
  }
@@ -3537,8 +3736,8 @@ function assertInsideRootsOrAncestor(target, roots) {
3537
3736
  assertRoots(roots);
3538
3737
  const absolute = isAbsolute6(target) ? target : `${process.cwd()}${sep2}${target}`;
3539
3738
  let walked = walk(target, absolute);
3540
- if (walked.tail.includes("..")) walked = walk(target, join9(walked.real, ...walked.tail));
3541
- const checked = walked.tail.length === 0 ? walked.real : join9(walked.real, ...walked.tail);
3739
+ if (walked.tail.includes("..")) walked = walk(target, join10(walked.real, ...walked.tail));
3740
+ const checked = walked.tail.length === 0 ? walked.real : join10(walked.real, ...walked.tail);
3542
3741
  if (!insideAny(checked, roots)) throw new Error(`ROOT_NOT_ALLOWED: ${target}`);
3543
3742
  return checked;
3544
3743
  }
@@ -3555,7 +3754,7 @@ function walk(target, absolute) {
3555
3754
  tail.push(segment);
3556
3755
  continue;
3557
3756
  }
3558
- const next = segment === ".." ? dirname3(real) : join9(real, segment);
3757
+ const next = segment === ".." ? dirname4(real) : join10(real, segment);
3559
3758
  if (!lstatSync(next, { throwIfNoEntry: false })) {
3560
3759
  tail.push(segment);
3561
3760
  continue;
@@ -3603,8 +3802,8 @@ var init_paths = __esm({
3603
3802
 
3604
3803
  // src/git/introspect.ts
3605
3804
  import { execFile as execFile3 } from "child_process";
3606
- import { readFileSync as readFileSync4, realpathSync as realpathSync6 } from "fs";
3607
- import { delimiter as delimiter2, dirname as dirname4, isAbsolute as isAbsolute7, join as join10, resolve as resolve2 } from "path";
3805
+ import { readFileSync as readFileSync5, realpathSync as realpathSync6 } from "fs";
3806
+ import { delimiter as delimiter2, dirname as dirname5, isAbsolute as isAbsolute7, join as join11, resolve as resolve2 } from "path";
3608
3807
  import { promisify as promisify2 } from "util";
3609
3808
  function parseGitHubRemote(url) {
3610
3809
  const trimmed = url.trim();
@@ -3690,10 +3889,10 @@ function insideRoot(path, root) {
3690
3889
  }
3691
3890
  }
3692
3891
  function isLinkedWorktree(gitDir, commonDir, repoRoot) {
3693
- if (gitDir === commonDir || dirname4(gitDir) !== join10(commonDir, "worktrees")) return false;
3892
+ if (gitDir === commonDir || dirname5(gitDir) !== join11(commonDir, "worktrees")) return false;
3694
3893
  try {
3695
- const back = readFileSync4(join10(gitDir, "gitdir"), "utf8").trim();
3696
- const mine = canonical(join10(repoRoot, ".git"), repoRoot);
3894
+ const back = readFileSync5(join11(gitDir, "gitdir"), "utf8").trim();
3895
+ const mine = canonical(join11(repoRoot, ".git"), repoRoot);
3697
3896
  return mine !== null && canonical(back, gitDir) === mine;
3698
3897
  } catch {
3699
3898
  return false;
@@ -3701,7 +3900,7 @@ function isLinkedWorktree(gitDir, commonDir, repoRoot) {
3701
3900
  }
3702
3901
  async function introspectRepo(cwd, run, root, { names = true } = {}) {
3703
3902
  try {
3704
- const ceiling = dirname4(root);
3903
+ const ceiling = dirname5(root);
3705
3904
  if (ceiling.includes(delimiter2)) return { ...NONE };
3706
3905
  const line = async (args) => {
3707
3906
  const { code, stdout } = await run(args, cwd, ceiling);
@@ -3798,9 +3997,9 @@ var init_introspect = __esm({
3798
3997
  });
3799
3998
 
3800
3999
  // src/repos/credentials.ts
3801
- import { chmodSync as chmodSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
4000
+ import { chmodSync as chmodSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
3802
4001
  import { devNull, homedir as homedir4 } from "os";
3803
- import { delimiter as delimiter3, isAbsolute as isAbsolute8, join as join11 } from "path";
4002
+ import { delimiter as delimiter3, isAbsolute as isAbsolute8, join as join12 } from "path";
3804
4003
  function gitWrapper(git, gitHome) {
3805
4004
  if (git === null || !isAbsolute8(git)) {
3806
4005
  return `#!/bin/sh
@@ -3815,7 +4014,7 @@ exec ${shellQuote(git)} "$@"
3815
4014
  `;
3816
4015
  }
3817
4016
  function repositoryGitHome(runDir) {
3818
- return join11(runDir, GIT_HOME);
4017
+ return join12(runDir, GIT_HOME);
3819
4018
  }
3820
4019
  function gitCredentialEnv(token, readOnlyText, opts = {}) {
3821
4020
  const env = {
@@ -3836,7 +4035,7 @@ function gitCredentialEnv(token, readOnlyText, opts = {}) {
3836
4035
  CAYENNE_READ_ONLY: readOnlyText
3837
4036
  };
3838
4037
  if (token !== null) env.GH_TOKEN = token;
3839
- if (opts.runDir !== void 0) env.GIT_SSH_COMMAND = shellQuote(join11(opts.runDir, NO_SSH));
4038
+ if (opts.runDir !== void 0) env.GIT_SSH_COMMAND = shellQuote(join12(opts.runDir, NO_SSH));
3840
4039
  if (opts.home !== void 0) env.HOME = opts.home;
3841
4040
  return env;
3842
4041
  }
@@ -3848,14 +4047,14 @@ function repositoryChatEnv(input) {
3848
4047
  SSH_AUTH_SOCK: void 0,
3849
4048
  GIT_CONFIG_PARAMETERS: void 0,
3850
4049
  ...Object.fromEntries(TRACE_AND_TLS_VARS.map((key) => [key, void 0])),
3851
- GH_CONFIG_DIR: join11(input.runDir, GH_CONFIG),
4050
+ GH_CONFIG_DIR: join12(input.runDir, GH_CONFIG),
3852
4051
  GH_PROMPT_DISABLED: "1",
3853
4052
  // A replace ref another chat planted in the shared clone must not change what a sha reads as.
3854
4053
  GIT_NO_REPLACE_OBJECTS: "1",
3855
4054
  // Only git runs with HOME at the git home. Go, SwiftPM and python-requests read `$NETRC` first:
3856
4055
  // this one never exists. curl reads the first `.curlrc` of `$CURL_HOME`, then `$HOME`: the git
3857
4056
  // home's is empty, so the owner's (`netrc`, `user = …`) never applies.
3858
- NETRC: join11(repositoryGitHome(input.runDir), NETRC_NAME),
4057
+ NETRC: join12(repositoryGitHome(input.runDir), NETRC_NAME),
3859
4058
  CURL_HOME: repositoryGitHome(input.runDir),
3860
4059
  // With it and CLAUDE_CONFIG_DIR both set, the CLI names its project dir by it rather than by
3861
4060
  // the cwd's slug: every chat's tool outputs would share one dir the worktree bound lets it read.
@@ -3870,8 +4069,8 @@ function repositoryChatEnv(input) {
3870
4069
  };
3871
4070
  if (input.token === null) env.GH_TOKEN = void 0;
3872
4071
  const bins = [
3873
- ...input.token === null ? [join11(input.runDir, READONLY_BIN)] : [],
3874
- join11(input.runDir, GIT_BIN)
4072
+ ...input.token === null ? [join12(input.runDir, READONLY_BIN)] : [],
4073
+ join12(input.runDir, GIT_BIN)
3875
4074
  ];
3876
4075
  env.PATH = [...bins, ...input.path ? [input.path] : []].join(delimiter3);
3877
4076
  for (const key of Object.keys(input.inherited ?? process.env)) {
@@ -3882,17 +4081,17 @@ function repositoryChatEnv(input) {
3882
4081
  }
3883
4082
  function prepareRunDir(runDir, opts) {
3884
4083
  mkdirSync4(runDir, { recursive: true, mode: 448 });
3885
- freshDir(join11(runDir, GH_CONFIG));
4084
+ freshDir(join12(runDir, GH_CONFIG));
3886
4085
  freshDir(repositoryGitHome(runDir));
3887
- writeFileSync4(join11(repositoryGitHome(runDir), CURLRC_NAME), "", { mode: 384, flag: "wx" });
3888
- chmodSync4(join11(repositoryGitHome(runDir), CURLRC_NAME), 384);
3889
- const bin = join11(runDir, READONLY_BIN);
4086
+ writeFileSync4(join12(repositoryGitHome(runDir), CURLRC_NAME), "", { mode: 384, flag: "wx" });
4087
+ chmodSync4(join12(repositoryGitHome(runDir), CURLRC_NAME), 384);
4088
+ const bin = join12(runDir, READONLY_BIN);
3890
4089
  freshDir(bin);
3891
- writeShim(join11(bin, "gh"), GH_SHIM);
3892
- writeShim(join11(runDir, NO_SSH), NO_SSH_SHIM);
3893
- const gitBin = join11(runDir, GIT_BIN);
4090
+ writeShim(join12(bin, "gh"), GH_SHIM);
4091
+ writeShim(join12(runDir, NO_SSH), NO_SSH_SHIM);
4092
+ const gitBin = join12(runDir, GIT_BIN);
3894
4093
  freshDir(gitBin);
3895
- writeShim(join11(gitBin, "git"), gitWrapper(opts.git, repositoryGitHome(runDir)));
4094
+ writeShim(join12(gitBin, "git"), gitWrapper(opts.git, repositoryGitHome(runDir)));
3896
4095
  }
3897
4096
  function freshDir(path) {
3898
4097
  rmSync3(path, { recursive: true, force: true });
@@ -3900,12 +4099,12 @@ function freshDir(path) {
3900
4099
  chmodSync4(path, 448);
3901
4100
  }
3902
4101
  function githubNetrcFiles(home = homedir4(), env = process.env) {
3903
- const candidates = [env.NETRC, join11(home, ".netrc")].filter(
4102
+ const candidates = [env.NETRC, join12(home, ".netrc")].filter(
3904
4103
  (path) => typeof path === "string" && path !== ""
3905
4104
  );
3906
4105
  return [...new Set(candidates)].filter((path) => {
3907
4106
  try {
3908
- return netrcCoversGitHub(readFileSync5(path, "utf8"));
4107
+ return netrcCoversGitHub(readFileSync6(path, "utf8"));
3909
4108
  } catch {
3910
4109
  return false;
3911
4110
  }
@@ -3986,13 +4185,13 @@ exit 1
3986
4185
 
3987
4186
  // src/git/commands.ts
3988
4187
  import { lstatSync as lstatSync2, realpathSync as realpathSync7, statSync as statSync4 } from "fs";
3989
- import { dirname as dirname5, isAbsolute as isAbsolute9, join as join12, relative as relative3, resolve as resolve3, sep as sep3 } from "path";
4188
+ import { dirname as dirname6, isAbsolute as isAbsolute9, join as join13, relative as relative3, resolve as resolve3, sep as sep3 } from "path";
3990
4189
  async function scopeRepo(cwd, run, root) {
3991
4190
  const identity = await introspectRepo(cwd, run, root, { names: false });
3992
4191
  if (identity.repo_root === null) {
3993
4192
  throw new Error(`VALIDATION: ${cwd} is not inside a git repository under an allowed root`);
3994
4193
  }
3995
- return { repoRoot: identity.repo_root, ceiling: dirname5(root) };
4194
+ return { repoRoot: identity.repo_root, ceiling: dirname6(root) };
3996
4195
  }
3997
4196
  function fold(letter) {
3998
4197
  switch (letter) {
@@ -4152,13 +4351,13 @@ async function gitDirs(scope, run) {
4152
4351
  }
4153
4352
  }
4154
4353
  function isGitDirShape(dir) {
4155
- const head = lstatSync2(join12(dir, "HEAD"), { throwIfNoEntry: false });
4354
+ const head = lstatSync2(join13(dir, "HEAD"), { throwIfNoEntry: false });
4156
4355
  if (!head || !(head.isFile() || head.isSymbolicLink())) return false;
4157
- const commondir = lstatSync2(join12(dir, "commondir"), { throwIfNoEntry: false });
4356
+ const commondir = lstatSync2(join13(dir, "commondir"), { throwIfNoEntry: false });
4158
4357
  if (commondir?.isFile()) return true;
4159
4358
  const isDir = (name) => {
4160
4359
  try {
4161
- return statSync4(join12(dir, name), { throwIfNoEntry: false })?.isDirectory() === true;
4360
+ return statSync4(join13(dir, name), { throwIfNoEntry: false })?.isDirectory() === true;
4162
4361
  } catch {
4163
4362
  return false;
4164
4363
  }
@@ -4179,13 +4378,13 @@ function enclosesHome(repoRoot, home) {
4179
4378
  } catch {
4180
4379
  return false;
4181
4380
  }
4182
- for (let cur = real; ; cur = dirname5(cur)) {
4381
+ for (let cur = real; ; cur = dirname6(cur)) {
4183
4382
  try {
4184
4383
  const st = statSync4(cur, { bigint: true, throwIfNoEntry: false });
4185
4384
  if (st && st.dev === root.dev && st.ino === root.ino) return true;
4186
4385
  } catch {
4187
4386
  }
4188
- if (dirname5(cur) === cur) return false;
4387
+ if (dirname6(cur) === cur) return false;
4189
4388
  }
4190
4389
  }
4191
4390
  function protectedTest(paths, exempt = []) {
@@ -4213,10 +4412,10 @@ function protectedTest(paths, exempt = []) {
4213
4412
  return (path) => {
4214
4413
  if (exempts.length === 0 && entries2.some((e) => within(path, e.real))) return true;
4215
4414
  let exempted = false;
4216
- for (let cur = path; ; cur = dirname5(cur)) {
4415
+ for (let cur = path; ; cur = dirname6(cur)) {
4217
4416
  if (!exempted && isOneOf(cur, entries2)) return true;
4218
4417
  if (exempts.length > 0 && isOneOf(cur, exempts)) exempted = true;
4219
- if (dirname5(cur) === cur) return false;
4418
+ if (dirname6(cur) === cur) return false;
4220
4419
  }
4221
4420
  };
4222
4421
  }
@@ -4233,7 +4432,7 @@ function identities(paths) {
4233
4432
  return out;
4234
4433
  }
4235
4434
  function insideOtherGitDir(real, repoRoot) {
4236
- for (let dir = dirname5(real); within(dir, repoRoot) && dir !== repoRoot; dir = dirname5(dir)) {
4435
+ for (let dir = dirname6(real); within(dir, repoRoot) && dir !== repoRoot; dir = dirname6(dir)) {
4237
4436
  if (isGitDirShape(dir)) return true;
4238
4437
  }
4239
4438
  return false;
@@ -4243,7 +4442,7 @@ async function gitDiff(scope, run, opts, guard = {}) {
4243
4442
  const guarded = protectedTest(guard.protectedPaths ?? [], guard.exemptPaths ?? []);
4244
4443
  const isProtected = (p) => guarded?.(p) === true;
4245
4444
  if (opts.path) {
4246
- const named = join12(scope.repoRoot, opts.path);
4445
+ const named = join13(scope.repoRoot, opts.path);
4247
4446
  let resolved = null;
4248
4447
  try {
4249
4448
  resolved = realpathSync7.native(named);
@@ -4273,7 +4472,7 @@ async function gitDiff(scope, run, opts, guard = {}) {
4273
4472
  `VALIDATION: untracked files are not diffed in a repository at or above the home directory (${scope.repoRoot})`
4274
4473
  );
4275
4474
  }
4276
- const absolute = join12(scope.repoRoot, opts.path);
4475
+ const absolute = join13(scope.repoRoot, opts.path);
4277
4476
  if (!lstatSync2(absolute, { throwIfNoEntry: false })) {
4278
4477
  try {
4279
4478
  assertInsideRootsOrAncestor(absolute, [scope.repoRoot]);
@@ -4316,7 +4515,7 @@ async function gitDiff(scope, run, opts, guard = {}) {
4316
4515
  }
4317
4516
  const diff = splitDiff(first.stdout);
4318
4517
  if (guarded === null) return diff;
4319
- const keep = (p) => p === void 0 || !isProtected(join12(scope.repoRoot, p));
4518
+ const keep = (p) => p === void 0 || !isProtected(join13(scope.repoRoot, p));
4320
4519
  return { ...diff, files: diff.files.filter((f) => keep(f.path) && keep(f.oldPath)) };
4321
4520
  }
4322
4521
  function parseBranches(raw, head) {
@@ -4385,16 +4584,16 @@ import {
4385
4584
  lstatSync as lstatSync3,
4386
4585
  mkdirSync as mkdirSync5,
4387
4586
  openSync,
4388
- readFileSync as readFileSync6,
4587
+ readFileSync as readFileSync7,
4389
4588
  realpathSync as realpathSync8,
4390
4589
  statSync as statSync5,
4391
4590
  writeSync
4392
4591
  } from "fs";
4393
4592
  import { readdir } from "fs/promises";
4394
- import { dirname as dirname6, isAbsolute as isAbsolute10, join as join13, relative as relative4, sep as sep4 } from "path";
4593
+ import { dirname as dirname7, isAbsolute as isAbsolute10, join as join14, relative as relative4, sep as sep4 } from "path";
4395
4594
  import { setImmediate } from "timers/promises";
4396
4595
  function worktreePathFor(repoRoot, name) {
4397
- return join13(repoRoot, WORKTREES_DIR, name);
4596
+ return join14(repoRoot, WORKTREES_DIR, name);
4398
4597
  }
4399
4598
  async function listedWorktrees(scope, run) {
4400
4599
  const r = await run(["worktree", "list", "--porcelain"], scope.repoRoot, scope.ceiling);
@@ -4417,7 +4616,7 @@ async function excludeCayenneDir(scope, run) {
4417
4616
  );
4418
4617
  const common = r.stdout.trim();
4419
4618
  if (r.code !== 0 || !isAbsolute10(common)) return;
4420
- const dotGit = join13(scope.repoRoot, ".git");
4619
+ const dotGit = join14(scope.repoRoot, ".git");
4421
4620
  let dotGitIsRealDir;
4422
4621
  try {
4423
4622
  dotGitIsRealDir = lstatSync3(dotGit).isDirectory();
@@ -4434,16 +4633,16 @@ async function excludeCayenneDir(scope, run) {
4434
4633
  return;
4435
4634
  }
4436
4635
  if (commonReal !== dotGitReal) return;
4437
- const exclude = join13(commonReal, "info", "exclude");
4636
+ const exclude = join14(commonReal, "info", "exclude");
4438
4637
  let checkedExclude;
4439
4638
  try {
4440
4639
  checkedExclude = assertInsideRootsOrAncestor(exclude, [scope.repoRoot]);
4441
4640
  } catch {
4442
4641
  return;
4443
4642
  }
4444
- const existing = existsSync4(checkedExclude) ? readFileSync6(checkedExclude, "utf8") : "";
4643
+ const existing = existsSync4(checkedExclude) ? readFileSync7(checkedExclude, "utf8") : "";
4445
4644
  if (existing.split("\n").includes(".cayenne/")) return;
4446
- mkdirSync5(dirname6(checkedExclude), { recursive: true });
4645
+ mkdirSync5(dirname7(checkedExclude), { recursive: true });
4447
4646
  const toAppend = `${existing.endsWith("\n") || existing === "" ? "" : "\n"}.cayenne/
4448
4647
  `;
4449
4648
  let fd;
@@ -4469,7 +4668,7 @@ function asNotADirectory(path, err) {
4469
4668
  return err instanceof Error ? err : new Error(String(err));
4470
4669
  }
4471
4670
  function canonicalWorktreesDir(scope) {
4472
- const dir = join13(scope.repoRoot, WORKTREES_DIR);
4671
+ const dir = join14(scope.repoRoot, WORKTREES_DIR);
4473
4672
  try {
4474
4673
  assertInsideRootsOrAncestor(dir, [scope.repoRoot]);
4475
4674
  } catch (err) {
@@ -4492,7 +4691,7 @@ async function createWorktree(scope, run, name, base) {
4492
4691
  throw new Error(`VALIDATION: ${name} is not a worktree name`);
4493
4692
  }
4494
4693
  const worktreesDir = canonicalWorktreesDir(scope);
4495
- const path = join13(worktreesDir, name);
4694
+ const path = join14(worktreesDir, name);
4496
4695
  if (lstatSync3(path, { throwIfNoEntry: false })) {
4497
4696
  try {
4498
4697
  resolveInsideRoots(path, [scope.repoRoot]);
@@ -4541,7 +4740,7 @@ async function findNestedRepository(top, limits = NESTED_WALK_LIMITS) {
4541
4740
  const lower = entry.name.toLowerCase();
4542
4741
  if (lower === ".git" && !(depth === 0 && entry.name === ".git")) return "repository";
4543
4742
  if (lower === "head") hasHead = true;
4544
- const path = join13(dir, entry.name);
4743
+ const path = join14(dir, entry.name);
4545
4744
  let isDir;
4546
4745
  try {
4547
4746
  isDir = entry.isDirectory() || // Some filesystems report no type; lstat decides, still without following.
@@ -4577,7 +4776,7 @@ async function worktreeIdentity(scope, run) {
4577
4776
  }
4578
4777
  }
4579
4778
  async function removeWorktree(scope, run, worktreePath, force) {
4580
- const worktreesDir = join13(scope.repoRoot, WORKTREES_DIR);
4779
+ const worktreesDir = join14(scope.repoRoot, WORKTREES_DIR);
4581
4780
  let base;
4582
4781
  try {
4583
4782
  base = realpathSync8.native(worktreesDir);
@@ -4682,10 +4881,10 @@ var init_ops = __esm({
4682
4881
 
4683
4882
  // src/git/worktree-owners.ts
4684
4883
  import { createHash, randomBytes } from "crypto";
4685
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
4686
- import { join as join14 } from "path";
4884
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, renameSync as renameSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
4885
+ import { join as join15 } from "path";
4687
4886
  function createWorktreeOwners(dir) {
4688
- const fileFor = (path) => join14(dir, `${createHash("sha256").update(path).digest("hex")}.json`);
4887
+ const fileFor = (path) => join15(dir, `${createHash("sha256").update(path).digest("hex")}.json`);
4689
4888
  return {
4690
4889
  record(path, rec) {
4691
4890
  const body = {
@@ -4728,7 +4927,7 @@ function writeRecordFile(dir, file, body) {
4728
4927
  }
4729
4928
  function readRecordFile(file) {
4730
4929
  try {
4731
- const parsed = JSON.parse(readFileSync7(file, "utf8"));
4930
+ const parsed = JSON.parse(readFileSync8(file, "utf8"));
4732
4931
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
4733
4932
  } catch {
4734
4933
  return null;
@@ -4742,9 +4941,9 @@ var init_worktree_owners = __esm({
4742
4941
 
4743
4942
  // src/git/session-worktrees.ts
4744
4943
  import { rmSync as rmSync5 } from "fs";
4745
- import { join as join15 } from "path";
4944
+ import { join as join16 } from "path";
4746
4945
  function createSessionWorktrees(dir) {
4747
- const fileFor = (sessionId) => UUID2.test(sessionId) ? join15(dir, `${sessionId}.json`) : null;
4946
+ const fileFor = (sessionId) => UUID2.test(sessionId) ? join16(dir, `${sessionId}.json`) : null;
4748
4947
  return {
4749
4948
  record(rec) {
4750
4949
  const file = fileFor(rec.sessionId);
@@ -5564,16 +5763,16 @@ import {
5564
5763
  mkdirSync as mkdirSync7,
5565
5764
  openSync as openSync2,
5566
5765
  readdirSync,
5567
- readFileSync as readFileSync8,
5766
+ readFileSync as readFileSync9,
5568
5767
  realpathSync as realpathSync9,
5569
5768
  renameSync as renameSync3,
5570
5769
  rmSync as rmSync6,
5571
5770
  statSync as statSync6,
5572
5771
  writeSync as writeSync2
5573
5772
  } from "fs";
5574
- import { dirname as dirname7, join as join16 } from "path";
5773
+ import { dirname as dirname8, join as join17 } from "path";
5575
5774
  function managedReposRoot(profileDir2) {
5576
- return join16(profileDir2, "repos");
5775
+ return join17(profileDir2, "repos");
5577
5776
  }
5578
5777
  function githubRemoteUrl(repo) {
5579
5778
  return `https://github.com/${repo.owner}/${repo.name}.git`;
@@ -5608,9 +5807,9 @@ function createManagedRepos(deps) {
5608
5807
  }
5609
5808
  };
5610
5809
  async function cloneInto(repo, env) {
5611
- const ownerDir = join16(root, repo.owner);
5612
- const path = join16(ownerDir, repo.name);
5613
- const tmp = join16(ownerDir, `.${repo.name}.clone-${randomBytes2(4).toString("hex")}`);
5810
+ const ownerDir = join17(root, repo.owner);
5811
+ const path = join17(ownerDir, repo.name);
5812
+ const tmp = join17(ownerDir, `.${repo.name}.clone-${randomBytes2(4).toString("hex")}`);
5614
5813
  inflight.add(tmp);
5615
5814
  try {
5616
5815
  mkdirSync7(ownerDir, { recursive: true, mode: 448 });
@@ -5655,7 +5854,7 @@ function createManagedRepos(deps) {
5655
5854
  const listed = await run(
5656
5855
  ["config", "--local", "--no-includes", "--list", "-z"],
5657
5856
  path,
5658
- dirname7(path)
5857
+ dirname8(path)
5659
5858
  );
5660
5859
  const entries2 = listed.code === 0 ? parseConfigList(listed.stdout) : [];
5661
5860
  const mark = entries2.filter((e) => e.section === "cayenne" && e.subsection === null && e.name === "repositoryid").at(-1)?.value;
@@ -5680,8 +5879,8 @@ function createManagedRepos(deps) {
5680
5879
  }
5681
5880
  async function sync(repo, tok) {
5682
5881
  const slug = `${repo.owner}/${repo.name}`;
5683
- const ownerDir = join16(root, repo.owner);
5684
- const path = join16(ownerDir, repo.name);
5882
+ const ownerDir = join17(root, repo.owner);
5883
+ const path = join17(ownerDir, repo.name);
5685
5884
  assertRealDirs(ownerDir, path);
5686
5885
  const env = gitCredentialEnv(tok.token, "", { home: deps.gitHome });
5687
5886
  const run = makeGitRun({ timeoutMs: GIT_SLOW_TIMEOUT_MS, env });
@@ -5721,9 +5920,9 @@ function createManagedRepos(deps) {
5721
5920
  return { clonePath: real, defaultBranch: branch, baseSha };
5722
5921
  }
5723
5922
  function excludePlans(path) {
5724
- const gitDir = join16(path, ".git");
5725
- const info = join16(gitDir, "info");
5726
- const file = join16(info, "exclude");
5923
+ const gitDir = join17(path, ".git");
5924
+ const info = join17(gitDir, "info");
5925
+ const file = join17(info, "exclude");
5727
5926
  try {
5728
5927
  if (!lstatSync4(gitDir).isDirectory()) throw new Error(".git is not a directory");
5729
5928
  const infoStat = lstatSync4(info, { throwIfNoEntry: false });
@@ -5739,7 +5938,7 @@ function createManagedRepos(deps) {
5739
5938
  420
5740
5939
  );
5741
5940
  try {
5742
- const text = readFileSync8(fd, "utf8");
5941
+ const text = readFileSync9(fd, "utf8");
5743
5942
  const lines = text.split("\n");
5744
5943
  const isOld = (line) => line.replace(/\r$/, "") === OLD_PLANS_EXCLUDE_LINE;
5745
5944
  const has = lines.some((line) => line.replace(/\r$/, "") === PLANS_EXCLUDE_LINE);
@@ -5780,9 +5979,9 @@ function createManagedRepos(deps) {
5780
5979
  }
5781
5980
  async function markOf(path) {
5782
5981
  const { code, stdout } = await scanRun(
5783
- ["config", "--file", join16(path, ".git", "config"), "--get", ID_KEY],
5982
+ ["config", "--file", join17(path, ".git", "config"), "--get", ID_KEY],
5784
5983
  path,
5785
- dirname7(path)
5984
+ dirname8(path)
5786
5985
  );
5787
5986
  const text = stdout.trim();
5788
5987
  if (code !== 0 || !ID_TEXT.test(text)) return null;
@@ -5809,8 +6008,8 @@ function createManagedRepos(deps) {
5809
6008
  });
5810
6009
  },
5811
6010
  verifyConfig(repo) {
5812
- const ownerDir = join16(root, repo.owner);
5813
- const path = join16(ownerDir, repo.name);
6011
+ const ownerDir = join17(root, repo.owner);
6012
+ const path = join17(ownerDir, repo.name);
5814
6013
  const check = async () => {
5815
6014
  assertRealDirs(ownerDir, path);
5816
6015
  await checkConfig(repo, path, scanRun);
@@ -5831,12 +6030,12 @@ function createManagedRepos(deps) {
5831
6030
  const candidates = [];
5832
6031
  for (const owner of entries(root)) {
5833
6032
  if (!owner.isDirectory()) continue;
5834
- const ownerDir = join16(root, owner.name);
6033
+ const ownerDir = join17(root, owner.name);
5835
6034
  for (const entry of entries(ownerDir)) {
5836
- const path = join16(ownerDir, entry.name);
6035
+ const path = join17(ownerDir, entry.name);
5837
6036
  if (TEMP_CLONE.test(entry.name)) {
5838
6037
  if (inflight.has(path)) continue;
5839
- if (!present(join16(path, ".cayenne"))) {
6038
+ if (!present(join17(path, ".cayenne"))) {
5840
6039
  rmSync6(path, { recursive: true, force: true });
5841
6040
  deps.log.info({ path }, "removed an interrupted managed clone");
5842
6041
  continue;
@@ -5912,7 +6111,7 @@ function keyText(e) {
5912
6111
  return `${e.section}.${subsection}.${e.name}`;
5913
6112
  }
5914
6113
  function recency(path) {
5915
- for (const file of [join16(path, ".git", "FETCH_HEAD"), join16(path, ".git")]) {
6114
+ for (const file of [join17(path, ".git", "FETCH_HEAD"), join17(path, ".git")]) {
5916
6115
  try {
5917
6116
  const stat = statSync6(file, { throwIfNoEntry: false });
5918
6117
  if (stat) return stat.mtimeMs;
@@ -6041,7 +6240,7 @@ var init_managed = __esm({
6041
6240
 
6042
6241
  // src/repos/sweep.ts
6043
6242
  import { lstatSync as lstatSync5, realpathSync as realpathSync10 } from "fs";
6044
- import { isAbsolute as isAbsolute12, join as join17, relative as relative5, sep as sep7 } from "path";
6243
+ import { isAbsolute as isAbsolute12, join as join18, relative as relative5, sep as sep7 } from "path";
6045
6244
  function createRepoWorktreeSweep(deps) {
6046
6245
  const { log } = deps;
6047
6246
  const said3 = /* @__PURE__ */ new Set();
@@ -6056,8 +6255,8 @@ function createRepoWorktreeSweep(deps) {
6056
6255
  return null;
6057
6256
  }
6058
6257
  const leaf = worktreeName(sessionId);
6059
- const repoRoot = join17(deps.managedRoot, owner, name);
6060
- if (leaf === null || rec.path !== join17(repoRoot, WORKTREES_DIR, leaf)) return null;
6258
+ const repoRoot = join18(deps.managedRoot, owner, name);
6259
+ if (leaf === null || rec.path !== join18(repoRoot, WORKTREES_DIR, leaf)) return null;
6061
6260
  return { sessionId, path: rec.path, repoRoot, record: rec };
6062
6261
  }
6063
6262
  async function readRows(ids) {
@@ -6499,7 +6698,7 @@ var init_roster = __esm({
6499
6698
 
6500
6699
  // src/sessions/attachments.ts
6501
6700
  import { readdirSync as readdirSync2, rmSync as rmSync7 } from "fs";
6502
- import { join as join18 } from "path";
6701
+ import { join as join19 } from "path";
6503
6702
  async function loadAttachments(db, scope, attachments) {
6504
6703
  const prefix = `${scope.workspaceId.toLowerCase()}/${scope.sessionId.toLowerCase()}/`;
6505
6704
  const named = attachments.map(({ storagePath }, i) => {
@@ -6536,13 +6735,13 @@ async function loadAttachments(db, scope, attachments) {
6536
6735
  return loaded;
6537
6736
  }
6538
6737
  function attachmentsTempDir(home, runnerId, sessionId) {
6539
- return join18(home, "tmp", "attachments", runnerId, sessionId);
6738
+ return join19(home, "tmp", "attachments", runnerId, sessionId);
6540
6739
  }
6541
6740
  function assertStagingInsideHome(home, dir) {
6542
6741
  return assertInsideRootsOrAncestor(dir, [home]);
6543
6742
  }
6544
6743
  function clearAttachmentsTemp(home, runnerId, heldBy) {
6545
- clearRunnerStaging(home, join18(home, "tmp", "attachments"), runnerId, heldBy);
6744
+ clearRunnerStaging(home, join19(home, "tmp", "attachments"), runnerId, heldBy);
6546
6745
  }
6547
6746
  function clearRunnerStaging(home, dir, runnerId, heldBy = (id) => holderOf({ root: home, runnerId: id }) !== null) {
6548
6747
  const proved = assertStagingInsideHome(home, dir);
@@ -6553,9 +6752,9 @@ function clearRunnerStaging(home, dir, runnerId, heldBy = (id) => holderOf({ roo
6553
6752
  return;
6554
6753
  }
6555
6754
  for (const name of names) {
6556
- assertStagingInsideHome(home, join18(proved, name));
6755
+ assertStagingInsideHome(home, join19(proved, name));
6557
6756
  if (name !== runnerId && heldBy(name)) continue;
6558
- rmSync7(join18(proved, name), { recursive: true, force: true });
6757
+ rmSync7(join19(proved, name), { recursive: true, force: true });
6559
6758
  }
6560
6759
  }
6561
6760
  var ATTACHMENTS_BUCKET, TYPE_OF_EXT, refuse;
@@ -6575,7 +6774,7 @@ var init_attachments2 = __esm({
6575
6774
 
6576
6775
  // src/fs/list.ts
6577
6776
  import { readdirSync as readdirSync3, realpathSync as realpathSync11, statSync as statSync7 } from "fs";
6578
- import { join as join19, relative as relative6, sep as sep8 } from "path";
6777
+ import { join as join20, relative as relative6, sep as sep8 } from "path";
6579
6778
  function order(a, b) {
6580
6779
  const aDir = a.kind === "dir" ? 0 : 1;
6581
6780
  const bDir = b.kind === "dir" ? 0 : 1;
@@ -6616,7 +6815,7 @@ function listDirectory(dir, root, depth) {
6616
6815
  return;
6617
6816
  }
6618
6817
  const level = dirents.map((dirent) => {
6619
- const path = join19(current, dirent.name);
6818
+ const path = join20(current, dirent.name);
6620
6819
  const link = dirent.isSymbolicLink();
6621
6820
  const resolved = link ? symlinkKind(path, root) : null;
6622
6821
  const isDir = link ? resolved?.kind === "dir" : dirent.isDirectory();
@@ -7061,7 +7260,7 @@ var init_broadcast = __esm({
7061
7260
 
7062
7261
  // src/documents/paths.ts
7063
7262
  import { realpathSync as realpathSync12 } from "fs";
7064
- import { isAbsolute as isAbsolute13, join as join20, relative as relative7, sep as sep10 } from "path";
7263
+ import { isAbsolute as isAbsolute13, join as join21, relative as relative7, sep as sep10 } from "path";
7065
7264
  function contained(dir, candidate) {
7066
7265
  const rel = relative7(dir, candidate);
7067
7266
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep10}`) && !isAbsolute13(rel);
@@ -7098,7 +7297,7 @@ var init_paths2 = __esm({
7098
7297
  "src/documents/paths.ts"() {
7099
7298
  "use strict";
7100
7299
  init_src();
7101
- docsDir = (cwd) => join20(cwd, ...DOCUMENTS_DIR.split("/"));
7300
+ docsDir = (cwd) => join21(cwd, ...DOCUMENTS_DIR.split("/"));
7102
7301
  }
7103
7302
  });
7104
7303
 
@@ -7275,10 +7474,10 @@ import {
7275
7474
  lstatSync as lstatSync6,
7276
7475
  opendirSync,
7277
7476
  openSync as openSync3,
7278
- readFileSync as readFileSync9
7477
+ readFileSync as readFileSync10
7279
7478
  } from "fs";
7280
7479
  import { open } from "fs/promises";
7281
- import { join as join21 } from "path";
7480
+ import { join as join22 } from "path";
7282
7481
  function isNew(baseline, relPath, contentHash) {
7283
7482
  const before = baseline.hashes.get(relPath);
7284
7483
  if (before === void 0) return baseline.complete;
@@ -7365,7 +7564,7 @@ function walk2(st, dir, rel, depth) {
7365
7564
  for (const entry of entries2) {
7366
7565
  if (st.stop) return;
7367
7566
  if (entry.name.startsWith(".")) continue;
7368
- const path = join21(dir, entry.name);
7567
+ const path = join22(dir, entry.name);
7369
7568
  const relPath = rel === "" ? entry.name : `${rel}/${entry.name}`;
7370
7569
  if (entry.isSymbolicLink()) {
7371
7570
  st.log.warn({ path, relPath }, `document skipped: ${SYMLINK_SKIPPED}`);
@@ -7476,7 +7675,7 @@ function baselineOf(root, log) {
7476
7675
  continue;
7477
7676
  }
7478
7677
  try {
7479
- hashes.set(file.relPath, sha256(readFileSync9(fd)));
7678
+ hashes.set(file.relPath, sha256(readFileSync10(fd)));
7480
7679
  } catch {
7481
7680
  hashes.set(file.relPath, UNREADABLE);
7482
7681
  } finally {
@@ -7598,31 +7797,6 @@ var init_service = __esm({
7598
7797
  }
7599
7798
  });
7600
7799
 
7601
- // src/providers/env.ts
7602
- function definedEnv(env) {
7603
- const out = {};
7604
- for (const [key, value] of Object.entries(env)) if (value !== void 0) out[key] = value;
7605
- return out;
7606
- }
7607
- var init_env = __esm({
7608
- "src/providers/env.ts"() {
7609
- "use strict";
7610
- }
7611
- });
7612
-
7613
- // src/claude/auth-mode.ts
7614
- function authModeFrom(info) {
7615
- if (info.apiProvider && info.apiProvider !== "firstParty") return "third_party";
7616
- if (info.apiKeySource && info.apiKeySource !== "none") return "api";
7617
- if (info.subscriptionType) return "subscription";
7618
- return null;
7619
- }
7620
- var init_auth_mode = __esm({
7621
- "src/claude/auth-mode.ts"() {
7622
- "use strict";
7623
- }
7624
- });
7625
-
7626
7800
  // src/claude/normalize.ts
7627
7801
  function normalizeSdkMessage(msg) {
7628
7802
  switch (msg.type) {
@@ -7726,43 +7900,10 @@ var init_normalize = __esm({
7726
7900
  }
7727
7901
  });
7728
7902
 
7729
- // src/claude/pushable.ts
7730
- var Pushable;
7731
- var init_pushable = __esm({
7732
- "src/claude/pushable.ts"() {
7733
- "use strict";
7734
- Pushable = class {
7735
- queue = [];
7736
- waiters = [];
7737
- ended = false;
7738
- push(value) {
7739
- if (this.ended) throw new Error("Pushable already ended");
7740
- const waiter = this.waiters.shift();
7741
- if (waiter) waiter({ value, done: false });
7742
- else this.queue.push(value);
7743
- }
7744
- end() {
7745
- this.ended = true;
7746
- for (const waiter of this.waiters.splice(0)) waiter({ value: void 0, done: true });
7747
- }
7748
- [Symbol.asyncIterator]() {
7749
- return {
7750
- next: () => {
7751
- const value = this.queue.shift();
7752
- if (value !== void 0) return Promise.resolve({ value, done: false });
7753
- if (this.ended) return Promise.resolve({ value: void 0, done: true });
7754
- return new Promise((resolve6) => this.waiters.push(resolve6));
7755
- }
7756
- };
7757
- }
7758
- };
7759
- }
7760
- });
7761
-
7762
7903
  // src/claude/session.ts
7763
7904
  import { randomUUID as randomUUID3 } from "crypto";
7764
7905
  import { EventEmitter } from "events";
7765
- import { query as sdkQuery } from "@anthropic-ai/claude-agent-sdk";
7906
+ import { query as sdkQuery2 } from "@anthropic-ai/claude-agent-sdk";
7766
7907
  async function askGuard(guard, toolName2, input) {
7767
7908
  try {
7768
7909
  const refusal = await guard(toolName2, input);
@@ -7823,7 +7964,7 @@ var init_session2 = __esm({
7823
7964
  stopAsked = false;
7824
7965
  start() {
7825
7966
  if (this.q) throw new Error("VALIDATION: session already started");
7826
- const queryFn = this.opts.queryFn ?? sdkQuery;
7967
+ const queryFn = this.opts.queryFn ?? sdkQuery2;
7827
7968
  const guard = this.opts.toolGuard;
7828
7969
  const canUseTool = async (toolName2, input, options) => {
7829
7970
  if (guard) {
@@ -8213,7 +8354,7 @@ import { spawn as spawn2 } from "child_process";
8213
8354
  import { randomUUID as randomUUID4 } from "crypto";
8214
8355
  import { EventEmitter as EventEmitter2 } from "events";
8215
8356
  import { chmodSync as chmodSync5, lstatSync as lstatSync7, mkdirSync as mkdirSync8, rmdirSync, rmSync as rmSync8, writeFileSync as writeFileSync6 } from "fs";
8216
- import { dirname as dirname8, join as join22 } from "path";
8357
+ import { dirname as dirname9, join as join23 } from "path";
8217
8358
  import { z as z18 } from "zod";
8218
8359
  function newStop() {
8219
8360
  let resolve6;
@@ -8445,7 +8586,7 @@ ${scrubSecrets(this.stderrTail.join("")).slice(-STDERR_TAIL_MAX)}` : "";
8445
8586
  assertStagingInsideHome(home, dir);
8446
8587
  mkdirSync8(dir, { recursive: true, mode: 448 });
8447
8588
  assertStagingInsideHome(home, dir);
8448
- for (const path of [dirname8(dir), dir]) {
8589
+ for (const path of [dirname9(dir), dir]) {
8449
8590
  const st = lstatSync7(path);
8450
8591
  if (st.isSymbolicLink() || !st.isDirectory()) {
8451
8592
  throw new Error(`${path} is not a plain directory`);
@@ -8453,7 +8594,7 @@ ${scrubSecrets(this.stderrTail.join("")).slice(-STDERR_TAIL_MAX)}` : "";
8453
8594
  }
8454
8595
  chmodSync5(dir, 448);
8455
8596
  for (const image of images) {
8456
- const path = join22(dir, `${randomUUID4()}.${ATTACHMENT_MEDIA_TYPES[image.mediaType]}`);
8597
+ const path = join23(dir, `${randomUUID4()}.${ATTACHMENT_MEDIA_TYPES[image.mediaType]}`);
8457
8598
  writeFileSync6(path, image.data, { mode: 384, flag: "wx" });
8458
8599
  written.push(path);
8459
8600
  }
@@ -8920,9 +9061,9 @@ var init_types = __esm({
8920
9061
 
8921
9062
  // src/skills/paths.ts
8922
9063
  import { realpathSync as realpathSync13 } from "fs";
8923
- import { isAbsolute as isAbsolute14, join as join23, relative as relative8, sep as sep11 } from "path";
9064
+ import { isAbsolute as isAbsolute14, join as join24, relative as relative8, sep as sep11 } from "path";
8924
9065
  function clearSessionSkills(home, runnerId, heldBy) {
8925
- clearRunnerStaging(home, join23(home, "session-skills"), runnerId, heldBy);
9066
+ clearRunnerStaging(home, join24(home, "session-skills"), runnerId, heldBy);
8926
9067
  }
8927
9068
  function assertInsideScopeDir(path, scopeDir) {
8928
9069
  let dirNow;
@@ -8947,8 +9088,8 @@ var init_paths3 = __esm({
8947
9088
  "use strict";
8948
9089
  init_paths();
8949
9090
  init_attachments2();
8950
- sessionSkillsRoot = (home, runnerId) => join23(home, "session-skills", runnerId);
8951
- sessionSkillsDir = (home, runnerId, sessionId) => join23(sessionSkillsRoot(home, runnerId), sessionId);
9091
+ sessionSkillsRoot = (home, runnerId) => join24(home, "session-skills", runnerId);
9092
+ sessionSkillsDir = (home, runnerId, sessionId) => join24(sessionSkillsRoot(home, runnerId), sessionId);
8952
9093
  }
8953
9094
  });
8954
9095
 
@@ -9087,7 +9228,7 @@ import {
9087
9228
  realpathSync as realpathSync14
9088
9229
  } from "fs";
9089
9230
  import { open as open2, opendir } from "fs/promises";
9090
- import { isAbsolute as isAbsolute15, join as join24, relative as relative9, sep as sep12 } from "path";
9231
+ import { isAbsolute as isAbsolute15, join as join25, relative as relative9, sep as sep12 } from "path";
9091
9232
  function inside2(root, candidate) {
9092
9233
  const rel = relative9(root, candidate);
9093
9234
  return rel === "" || !rel.startsWith(`..${sep12}`) && rel !== ".." && !isAbsolute15(rel);
@@ -9116,7 +9257,7 @@ function scopeRoots(opts) {
9116
9257
  const isGitRepo = opts.isGitRepo ?? defaultIsGitRepo;
9117
9258
  const home = canonical2(opts.home);
9118
9259
  const homes = protectedHomes(opts);
9119
- const found = [{ scope: "personal", dir: join24(home, "skills"), base: home }];
9260
+ const found = [{ scope: "personal", dir: join25(home, "skills"), base: home }];
9120
9261
  let skillsRepo;
9121
9262
  if (opts.skillsRepo !== void 0) {
9122
9263
  try {
@@ -9137,7 +9278,7 @@ function scopeRoots(opts) {
9137
9278
  continue;
9138
9279
  }
9139
9280
  if (!isGitRepo(real)) continue;
9140
- const dir = join24(real, ".cayenne", "skills");
9281
+ const dir = join25(real, ".cayenne", "skills");
9141
9282
  const shared = skillsRepo !== void 0 && real === skillsRepo ? "workspace" : "project";
9142
9283
  if (overlapsHome(canonical2(dir), homes, shared, log)) continue;
9143
9284
  if (skillsRepo !== void 0 && real === skillsRepo) {
@@ -9199,7 +9340,7 @@ async function scanSkills(opts) {
9199
9340
  } catch (err) {
9200
9341
  log.warn(
9201
9342
  {
9202
- path: join24(root.dirReal, entry.name),
9343
+ path: join25(root.dirReal, entry.name),
9203
9344
  scope: root.scope,
9204
9345
  err: err instanceof Error ? err.message : String(err)
9205
9346
  },
@@ -9244,13 +9385,13 @@ async function readAtMost2(fh, limit) {
9244
9385
  return buffer.subarray(0, filled);
9245
9386
  }
9246
9387
  async function readEntry(root, entry, log, budget) {
9247
- const path = join24(root.dirReal, entry.name);
9388
+ const path = join25(root.dirReal, entry.name);
9248
9389
  const skip = (reason) => {
9249
9390
  log.warn({ path, scope: root.scope }, `skill skipped: ${reason}`);
9250
9391
  return null;
9251
9392
  };
9252
9393
  if (entry.isDirectory()) {
9253
- const nested = existsSync5(join24(path, "SKILL.md"));
9394
+ const nested = existsSync5(join25(path, "SKILL.md"));
9254
9395
  return skip(
9255
9396
  nested ? `a directory holding SKILL.md: Cayenne's store is flat, so move it to ${entry.name}.md beside this directory` : "a directory, and the scan does not recurse"
9256
9397
  );
@@ -9410,7 +9551,7 @@ var init_scan2 = __esm({
9410
9551
  SKILL_SCAN_MAX_PER_DIR = 500;
9411
9552
  SKILL_SCAN_MAX_SKILLS = 500;
9412
9553
  SKILL_SCAN_MAX_BYTES = 32 * 1024 * 1024;
9413
- defaultIsGitRepo = (dir) => lstatSync8(join24(dir, ".git"), { throwIfNoEntry: false }) !== void 0;
9554
+ defaultIsGitRepo = (dir) => lstatSync8(join25(dir, ".git"), { throwIfNoEntry: false }) !== void 0;
9414
9555
  summariesOf = (scan) => scan.map((s) => s.summary);
9415
9556
  FRONTMATTER = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
9416
9557
  KEY = /^([A-Za-z_][A-Za-z0-9_-]*):[ \t]*(.*)$/;
@@ -9429,7 +9570,7 @@ import {
9429
9570
  rmSync as rmSync9
9430
9571
  } from "fs";
9431
9572
  import { open as open3, writeFile } from "fs/promises";
9432
- import { dirname as dirname9, isAbsolute as isAbsolute16, join as join25, relative as relative10, sep as sep13 } from "path";
9573
+ import { dirname as dirname10, isAbsolute as isAbsolute16, join as join26, relative as relative10, sep as sep13 } from "path";
9433
9574
  function isSkillRefusal(err) {
9434
9575
  const msg = err instanceof Error ? err.message : String(err);
9435
9576
  return msg.startsWith("ROOT_NOT_ALLOWED") || msg.startsWith(BUDGET) || msg.includes("is not a plain directory") || msg.includes("is not part of the session skills plugin");
@@ -9476,7 +9617,7 @@ async function materialiseSkills(scan, t) {
9476
9617
  }
9477
9618
  const path = plainDirectory(t.home, sessionSkillsDir(t.home, t.runnerId, t.sessionId));
9478
9619
  await writeFile(
9479
- join25(plainDirectory(t.home, join25(path, ".claude-plugin")), "plugin.json"),
9620
+ join26(plainDirectory(t.home, join26(path, ".claude-plugin")), "plugin.json"),
9480
9621
  `${JSON.stringify(
9481
9622
  {
9482
9623
  name: SKILL_PLUGIN_NAME,
@@ -9506,13 +9647,13 @@ async function materialiseSkills(scan, t) {
9506
9647
  );
9507
9648
  }
9508
9649
  try {
9509
- const dir = plainDirectory(t.home, join25(path, "skills", slug));
9510
- await writeFile(join25(dir, "SKILL.md"), bytes, { mode: 384, flag: "wx" });
9650
+ const dir = plainDirectory(t.home, join26(path, "skills", slug));
9651
+ await writeFile(join26(dir, "SKILL.md"), bytes, { mode: 384, flag: "wx" });
9511
9652
  slugs.push(slug);
9512
9653
  } catch (err) {
9513
9654
  note(log, err, { slug, scope, path: skill.path }, "skill not materialised for this session");
9514
9655
  try {
9515
- rmSync9(assertStagingInsideHome(t.home, join25(path, "skills", slug)), {
9656
+ rmSync9(assertStagingInsideHome(t.home, join26(path, "skills", slug)), {
9516
9657
  recursive: true,
9517
9658
  force: true
9518
9659
  });
@@ -9576,20 +9717,20 @@ function assertSealed(path, slugs) {
9576
9717
  const want = new Set(expected);
9577
9718
  for (const entry of entries2) {
9578
9719
  const ok = kind === "dir" ? entry.isDirectory() : entry.isFile();
9579
- if (!ok || !want.has(entry.name)) throw stray(join25(dir, entry.name));
9720
+ if (!ok || !want.has(entry.name)) throw stray(join26(dir, entry.name));
9580
9721
  }
9581
- if (entries2.length !== want.size) throw stray(join25(dir, [...want].join("|")));
9722
+ if (entries2.length !== want.size) throw stray(join26(dir, [...want].join("|")));
9582
9723
  };
9583
9724
  named(path, [".claude-plugin", "skills"], "dir");
9584
- named(join25(path, ".claude-plugin"), ["plugin.json"], "file");
9585
- named(join25(path, "skills"), slugs, "dir");
9586
- for (const slug of slugs) named(join25(path, "skills", slug), ["SKILL.md"], "file");
9725
+ named(join26(path, ".claude-plugin"), ["plugin.json"], "file");
9726
+ named(join26(path, "skills"), slugs, "dir");
9727
+ for (const slug of slugs) named(join26(path, "skills", slug), ["SKILL.md"], "file");
9587
9728
  }
9588
9729
  function plainDirectory(home, dir) {
9589
9730
  const intended = assertStagingInsideHome(home, dir);
9590
9731
  mkdirSync9(intended, { recursive: true, mode: 448 });
9591
9732
  const proved = assertStagingInsideHome(home, intended);
9592
- for (const p of [dirname9(proved), proved]) {
9733
+ for (const p of [dirname10(proved), proved]) {
9593
9734
  const st = lstatSync9(p);
9594
9735
  if (st.isSymbolicLink() || !st.isDirectory()) throw new Error(`${p} is not a plain directory`);
9595
9736
  }
@@ -10146,13 +10287,13 @@ var init_secret_files = __esm({
10146
10287
 
10147
10288
  // src/sessions/task-sessions.ts
10148
10289
  import { existsSync as existsSync6 } from "fs";
10149
- import { join as join26 } from "path";
10290
+ import { join as join27 } from "path";
10150
10291
  function createTaskSessions(dir) {
10151
10292
  const fileFor = (sessionId) => {
10152
10293
  if (!UUID3.test(sessionId)) {
10153
10294
  throw new Error(`VALIDATION: ${sessionId.slice(0, 40)} is not a session id`);
10154
10295
  }
10155
- return join26(dir, `${sessionId.toLowerCase()}.json`);
10296
+ return join27(dir, `${sessionId.toLowerCase()}.json`);
10156
10297
  };
10157
10298
  return {
10158
10299
  mark(sessionId) {
@@ -10173,10 +10314,10 @@ var init_task_sessions = __esm({
10173
10314
  });
10174
10315
 
10175
10316
  // src/sessions/user-guards.ts
10176
- import { closeSync as closeSync4, constants as constants4, fstatSync, openSync as openSync4, readFileSync as readFileSync10 } from "fs";
10177
- import { isAbsolute as isAbsolute20, join as join27 } from "path";
10317
+ import { closeSync as closeSync4, constants as constants4, fstatSync, openSync as openSync4, readFileSync as readFileSync11 } from "fs";
10318
+ import { isAbsolute as isAbsolute20, join as join28 } from "path";
10178
10319
  function readUserGuards(configDir) {
10179
- const settingsFile = join27(configDir, "settings.json");
10320
+ const settingsFile = join28(configDir, "settings.json");
10180
10321
  const none = { settingsFile, preToolUseHooks: 0, denyRules: 0 };
10181
10322
  if (!isAbsolute20(configDir)) return none;
10182
10323
  const text = readSmallFile(settingsFile);
@@ -10211,7 +10352,7 @@ function readSmallFile(path) {
10211
10352
  try {
10212
10353
  const stats = fstatSync(fd);
10213
10354
  if (!stats.isFile() || stats.size > USER_SETTINGS_MAX_BYTES) return null;
10214
- return readFileSync10(fd, "utf8");
10355
+ return readFileSync11(fd, "utf8");
10215
10356
  } catch {
10216
10357
  return null;
10217
10358
  } finally {
@@ -10245,7 +10386,7 @@ var init_user_guards = __esm({
10245
10386
  // src/sessions/manager.ts
10246
10387
  import { randomUUID as randomUUID5 } from "crypto";
10247
10388
  import { realpathSync as realpathSync17 } from "fs";
10248
- import { isAbsolute as isAbsolute21, join as join28, relative as relative14, sep as sep17 } from "path";
10389
+ import { isAbsolute as isAbsolute21, join as join29, relative as relative14, sep as sep17 } from "path";
10249
10390
  import { isDeepStrictEqual } from "util";
10250
10391
  function releaseCreds(creds) {
10251
10392
  if (creds?.state === "ok") creds.release();
@@ -11635,7 +11776,7 @@ var init_manager = __esm({
11635
11776
  const id = sessionId === void 0 ? null : this.live.get(sessionId)?.session.providerSessionId;
11636
11777
  if (projectDir === null || typeof id !== "string" || !PROVIDER_SESSION_ID.test(id))
11637
11778
  return [];
11638
- return [join28(projectDir, id, "tool-results")];
11779
+ return [join29(projectDir, id, "tool-results")];
11639
11780
  };
11640
11781
  const policy2 = this.settingsPolicy(createdBy, cwd, permissionMode, []);
11641
11782
  this.warnBypassWithoutUserGuards(createdBy, permissionMode, policy2, sessionId);
@@ -11683,7 +11824,7 @@ var init_manager = __esm({
11683
11824
  canonical3 = realpathSync17.native(configDir);
11684
11825
  } catch {
11685
11826
  }
11686
- return join28(canonical3, "projects", claudeProjectDirName(cwd));
11827
+ return join29(canonical3, "projects", claudeProjectDirName(cwd));
11687
11828
  }
11688
11829
  /** Whether one of `roots` contains the managed root, as an allowed root such as `~` can. */
11689
11830
  reachesManagedRoot(roots) {
@@ -11714,7 +11855,7 @@ var init_manager = __esm({
11714
11855
  return parsed.data;
11715
11856
  }
11716
11857
  taskSessions() {
11717
- return this.deps.taskSessions ?? createTaskSessions(join28(this.deps.cayenneHome ?? cayenneHome(), "task-sessions"));
11858
+ return this.deps.taskSessions ?? createTaskSessions(join29(this.deps.cayenneHome ?? cayenneHome(), "task-sessions"));
11718
11859
  }
11719
11860
  markTaskSession(sessionId) {
11720
11861
  try {
@@ -12858,10 +12999,10 @@ var init_manager = __esm({
12858
12999
  });
12859
13000
 
12860
13001
  // src/sessions/repo-sessions.ts
12861
- import { readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
12862
- import { join as join29 } from "path";
13002
+ import { readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
13003
+ import { join as join30 } from "path";
12863
13004
  function createRepoSessions(dir) {
12864
- const fileFor = (sessionId) => UUID4.test(sessionId) ? join29(dir, `${sessionId.toLowerCase()}.json`) : null;
13005
+ const fileFor = (sessionId) => UUID4.test(sessionId) ? join30(dir, `${sessionId.toLowerCase()}.json`) : null;
12865
13006
  return {
12866
13007
  mark(sessionId, mark) {
12867
13008
  const file = fileFor(sessionId);
@@ -12881,7 +13022,7 @@ function createRepoSessions(dir) {
12881
13022
  if (file === null) return null;
12882
13023
  let text;
12883
13024
  try {
12884
- text = readFileSync11(file, "utf8");
13025
+ text = readFileSync12(file, "utf8");
12885
13026
  } catch (err) {
12886
13027
  return isMissing(err) ? null : "unreadable";
12887
13028
  }
@@ -13265,9 +13406,9 @@ function createTerminalChannels({
13265
13406
  const inflight = joining.get(terminalId);
13266
13407
  if (inflight) return inflight;
13267
13408
  if (joined.has(terminalId)) return;
13268
- const join32 = attachOne(terminalId).finally(() => joining.delete(terminalId));
13269
- joining.set(terminalId, join32);
13270
- return join32;
13409
+ const join33 = attachOne(terminalId).finally(() => joining.delete(terminalId));
13410
+ joining.set(terminalId, join33);
13411
+ return join33;
13271
13412
  },
13272
13413
  flush,
13273
13414
  detach,
@@ -13328,9 +13469,9 @@ var init_channels = __esm({
13328
13469
  });
13329
13470
 
13330
13471
  // src/terminal/env.ts
13331
- import { readFileSync as readFileSync12, realpathSync as realpathSync19 } from "fs";
13472
+ import { readFileSync as readFileSync13, realpathSync as realpathSync19 } from "fs";
13332
13473
  import { lstat } from "fs/promises";
13333
- import { dirname as dirname10, isAbsolute as isAbsolute22, relative as relative15 } from "path";
13474
+ import { dirname as dirname11, isAbsolute as isAbsolute22, relative as relative15 } from "path";
13334
13475
  function terminalEnv(base, { runnerOwner, claudeConfigDir }) {
13335
13476
  const env = {};
13336
13477
  for (const [key, value] of Object.entries(base)) {
@@ -13389,7 +13530,7 @@ async function rootOwnedPath(fs, real) {
13389
13530
  return false;
13390
13531
  }
13391
13532
  if (st.isSymbolicLink() || st.uid !== 0 || (st.mode & 18) !== 0) return false;
13392
- const parent = dirname10(path);
13533
+ const parent = dirname11(path);
13393
13534
  if (parent === path) return true;
13394
13535
  path = parent;
13395
13536
  }
@@ -13428,7 +13569,7 @@ var init_env2 = __esm({
13428
13569
  nodeShellFs = {
13429
13570
  readEtcShells: () => {
13430
13571
  try {
13431
- return readFileSync12("/etc/shells", "utf8");
13572
+ return readFileSync13("/etc/shells", "utf8");
13432
13573
  } catch {
13433
13574
  return "";
13434
13575
  }
@@ -14474,7 +14615,7 @@ __export(start_exports, {
14474
14615
  start: () => start
14475
14616
  });
14476
14617
  import { homedir as homedir6, hostname } from "os";
14477
- import { join as join30 } from "path";
14618
+ import { join as join31 } from "path";
14478
14619
  function protectedPaths(claudeConfigDir, home = homedir6()) {
14479
14620
  const inHome = [
14480
14621
  ".claude",
@@ -14482,12 +14623,12 @@ function protectedPaths(claudeConfigDir, home = homedir6()) {
14482
14623
  ".codex",
14483
14624
  ".ssh",
14484
14625
  ".aws",
14485
- join30(".config", "gh"),
14626
+ join31(".config", "gh"),
14486
14627
  ".gnupg",
14487
14628
  ".git-credentials",
14488
14629
  ".npmrc",
14489
14630
  ".netrc"
14490
- ].map((p) => join30(home, p));
14631
+ ].map((p) => join31(home, p));
14491
14632
  return [cayenneHome(), ...claudeConfigDir ? [claudeConfigDir] : [], ...inHome];
14492
14633
  }
14493
14634
  function withRepoClone(repoClone) {
@@ -14503,6 +14644,16 @@ function logCodex(codex) {
14503
14644
  `codex available on ${codex.models.map((m) => m.id).join(", ") || "its default model"}`
14504
14645
  );
14505
14646
  }
14647
+ function logClaude(claude) {
14648
+ if (!claude.available) {
14649
+ logger.info({ reason: claude.reason }, `claude ${claude.reason ?? "unavailable"}`);
14650
+ return;
14651
+ }
14652
+ logger.info(
14653
+ { version: claude.version, models: claude.models.map((m) => m.id), authMode: claude.authMode },
14654
+ `claude available on ${claude.models.map((m) => m.id).join(", ") || "the curated model list"}`
14655
+ );
14656
+ }
14506
14657
  function holdLock(root, record) {
14507
14658
  let held;
14508
14659
  return {
@@ -14595,7 +14746,6 @@ async function compose(cfg, lock, host, preflight) {
14595
14746
  );
14596
14747
  });
14597
14748
  const codexPath = resolveCodexPath(cfg, process.env, () => findCodexOnPath(process.env));
14598
- const detect = () => detectProviders({ path: codexPath, run: runCodex, listModels: listCodexModels });
14599
14749
  let providers = {
14600
14750
  claude: { available: true, models: [] },
14601
14751
  codex: { available: false, reason: "not_installed", models: [] }
@@ -14603,9 +14753,23 @@ async function compose(cfg, lock, host, preflight) {
14603
14753
  const pty = await loadPty();
14604
14754
  const memberTerminalIds = memberTerminalIdsOf(cfg);
14605
14755
  const kinds2 = handledKinds({ terminal: pty !== null });
14606
- const runDir = join30(cayenneDir(), "run");
14756
+ const runDir = join31(cayenneDir(), "run");
14607
14757
  prepareRunDir(runDir, { git: findOnPath("git", process.env.PATH) });
14608
14758
  const gitHome = repositoryGitHome(runDir);
14759
+ const claudeProbeEnv = claudeEnv(claudeConfigDir);
14760
+ const detect = () => detectProviders({
14761
+ path: codexPath,
14762
+ run: runCodex,
14763
+ listModels: listCodexModels,
14764
+ claude: () => detectClaude({
14765
+ version: claudeCodeVersion,
14766
+ probe: () => probeClaude({
14767
+ env: claudeProbeEnv,
14768
+ cwd: runDir,
14769
+ settingSources: cfg.loadUserSettings ? ["user"] : []
14770
+ })
14771
+ })
14772
+ });
14609
14773
  for (const path of githubNetrcFiles()) {
14610
14774
  logger.warn(
14611
14775
  { path },
@@ -14685,6 +14849,7 @@ async function compose(cfg, lock, host, preflight) {
14685
14849
  });
14686
14850
  const registered = capabilities();
14687
14851
  let loggedCodex = null;
14852
+ let loggedClaude = null;
14688
14853
  const refresh = async () => {
14689
14854
  providers = await detect();
14690
14855
  const line = JSON.stringify(providers.codex);
@@ -14692,6 +14857,11 @@ async function compose(cfg, lock, host, preflight) {
14692
14857
  loggedCodex = line;
14693
14858
  logCodex(providers.codex);
14694
14859
  }
14860
+ const claudeLine = JSON.stringify(providers.claude);
14861
+ if (claudeLine !== loggedClaude) {
14862
+ loggedClaude = claudeLine;
14863
+ logClaude(providers.claude);
14864
+ }
14695
14865
  return capabilities();
14696
14866
  };
14697
14867
  let identity;
@@ -14752,15 +14922,15 @@ async function compose(cfg, lock, host, preflight) {
14752
14922
  const trustedRoots = trustedRepoRoots(cfg.trustRepoSettings);
14753
14923
  if (!cfg.loadUserSettings) {
14754
14924
  const guards = readUserGuards(sessionClaudeConfigDir(claudeEnv(claudeConfigDir)));
14755
- const warning = userGuardsWarning(guards, join30(cayenneDir(), "config.json"));
14925
+ const warning = userGuardsWarning(guards, join31(cayenneDir(), "config.json"));
14756
14926
  if (warning) {
14757
14927
  const { settingsFile, preToolUseHooks, denyRules } = guards;
14758
14928
  logger.warn({ settingsFile, preToolUseHooks, denyRules }, warning);
14759
14929
  }
14760
14930
  }
14761
- const repoSessions = createRepoSessions(join30(cayenneHome(), "repo-sessions"));
14762
- const worktreeOwners = createWorktreeOwners(join30(cayenneHome(), "worktrees"));
14763
- const sessionWorktrees = createSessionWorktrees(join30(cayenneHome(), "session-worktrees"));
14931
+ const repoSessions = createRepoSessions(join31(cayenneHome(), "repo-sessions"));
14932
+ const worktreeOwners = createWorktreeOwners(join31(cayenneHome(), "worktrees"));
14933
+ const sessionWorktrees = createSessionWorktrees(join31(cayenneHome(), "session-worktrees"));
14764
14934
  const git = createGitOps({
14765
14935
  protectedPaths: protectedPaths(claudeConfigDir),
14766
14936
  home: homedir6(),
@@ -14788,7 +14958,7 @@ async function compose(cfg, lock, host, preflight) {
14788
14958
  worktreeOwners,
14789
14959
  sessionWorktrees,
14790
14960
  // And which sessions were started from a task, so their secret-file guard outlives a restart.
14791
- taskSessions: createTaskSessions(join30(cayenneHome(), "task-sessions")),
14961
+ taskSessions: createTaskSessions(join31(cayenneHome(), "task-sessions")),
14792
14962
  // The paths no `git.diff` returns are paths no task session reads (protocol §2.8).
14793
14963
  protectedPaths: protectedPaths(claudeConfigDir),
14794
14964
  // D50: where repository chats run, which of them are, and their credentials' run dir. A
@@ -15147,9 +15317,10 @@ var init_start = __esm({
15147
15317
  "src/start.ts"() {
15148
15318
  "use strict";
15149
15319
  init_src();
15320
+ init_detect();
15150
15321
  init_claude_dir();
15151
15322
  init_cloud();
15152
- init_detect();
15323
+ init_detect2();
15153
15324
  init_dispatcher();
15154
15325
  init_source();
15155
15326
  init_config();
@@ -15192,9 +15363,9 @@ var init_start = __esm({
15192
15363
  });
15193
15364
 
15194
15365
  // src/cli.ts
15195
- import { existsSync as existsSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync13, realpathSync as realpathSync20, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "fs";
15366
+ import { existsSync as existsSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync14, realpathSync as realpathSync20, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "fs";
15196
15367
  import { homedir as homedir7, hostname as hostname2, userInfo } from "os";
15197
- import { dirname as dirname11, join as join31 } from "path";
15368
+ import { dirname as dirname12, join as join32 } from "path";
15198
15369
  import { Command } from "commander";
15199
15370
  import prompts from "prompts";
15200
15371
 
@@ -15699,7 +15870,7 @@ var service = () => {
15699
15870
  return { exec: exec4, launchd: makeLaunchd(exec4, userInfo().uid), label: plistLabel(currentProfile()) };
15700
15871
  };
15701
15872
  var writePlistFile = (path, xml2) => {
15702
- mkdirSync10(dirname11(path), { recursive: true });
15873
+ mkdirSync10(dirname12(path), { recursive: true });
15703
15874
  writeFileSync7(path, xml2, { mode: 420 });
15704
15875
  };
15705
15876
  var requireMac = () => {
@@ -15848,7 +16019,7 @@ program.command("doctor").description("Check the install and say how to fix what
15848
16019
  if (checks.some((c) => !c.ok)) process.exitCode = 1;
15849
16020
  });
15850
16021
  program.command("logs").description("Show the service log").option("-f, --follow", "keep streaming").action(async (opts) => {
15851
- const file = join31(cayenneDir(), "logs", "runner.log");
16022
+ const file = join32(cayenneDir(), "logs", "runner.log");
15852
16023
  if (!existsSync7(file)) throw new Error(`no log yet at ${file}. Run \`cayenne up\` first.`);
15853
16024
  const print = (chunk) => {
15854
16025
  for (const line of chunk.split("\n")) {
@@ -15861,12 +16032,12 @@ program.command("logs").description("Show the service log").option("-f, --follow
15861
16032
  }
15862
16033
  }
15863
16034
  };
15864
- print(readFileSync13(file, "utf8"));
16035
+ print(readFileSync14(file, "utf8"));
15865
16036
  if (opts.follow) {
15866
16037
  const { watch: watch2 } = await import("fs");
15867
- let size = readFileSync13(file).length;
16038
+ let size = readFileSync14(file).length;
15868
16039
  watch2(file, () => {
15869
- const buf = readFileSync13(file);
16040
+ const buf = readFileSync14(file);
15870
16041
  if (buf.length > size) print(buf.subarray(size).toString("utf8"));
15871
16042
  size = buf.length;
15872
16043
  });
@@ -15894,7 +16065,7 @@ program.command("update").description("Re-pin the installed app and restart the
15894
16065
  cliPath: record.cliPath,
15895
16066
  profile: currentProfile(),
15896
16067
  pathEnv: record.pathEnv,
15897
- logDir: join31(dir, "logs")
16068
+ logDir: join32(dir, "logs")
15898
16069
  })
15899
16070
  );
15900
16071
  await launchd.bootout(label);
@@ -15915,7 +16086,7 @@ program.command("uninstall").description("Stop the service and remove the launch
15915
16086
  if (dir === "/" || dir === home || home.startsWith(`${dir}/`)) {
15916
16087
  throw new Error(`refusing to purge ${dir}: it is the home directory or an ancestor of it`);
15917
16088
  }
15918
- if (!existsSync7(join31(dir, "config.json")) && !existsSync7(join31(dir, "app", "install.json"))) {
16089
+ if (!existsSync7(join32(dir, "config.json")) && !existsSync7(join32(dir, "app", "install.json"))) {
15919
16090
  throw new Error(`refusing to purge ${dir}: it does not look like a Cayenne profile`);
15920
16091
  }
15921
16092
  if (!opts.yes) {