@ycodium-ai/agent-claude 0.2.2678 → 0.2.2694

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/dist/index.js CHANGED
@@ -217,7 +217,7 @@ var CLAUDE_CARD = {
217
217
 
218
218
  // src/claudeInstance.ts
219
219
  import * as NodeOS8 from "node:os";
220
- import * as NodePath8 from "node:path";
220
+ import * as NodePath13 from "node:path";
221
221
 
222
222
  // ../../node_modules/.pnpm/@anthropic-ai+claude-agent-sdk@0.3.170_@anthropic-ai+sdk@0.93.0_zod@4.6.5__@modelcontex_64204813d4dba2ebc464c3aa751485a4/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
223
223
  import { createRequire as xz } from "node:module";
@@ -19650,10 +19650,12 @@ function HU(e, t) {
19650
19650
  }
19651
19651
 
19652
19652
  // src/claudeInstance.ts
19653
+ import { mergeLaunchEnv as mergeLaunchEnv3 } from "@ycodium-ai/plugin-api/executor";
19654
+
19655
+ // src/claudeProbe.ts
19653
19656
  import {
19654
19657
  describeExecutorSpawnFailure,
19655
- ExecutorSpawnRefusedError,
19656
- mergeLaunchEnv as mergeLaunchEnv2
19658
+ ExecutorSpawnRefusedError
19657
19659
  } from "@ycodium-ai/plugin-api/executor";
19658
19660
 
19659
19661
  // src/claudeAccount.ts
@@ -19743,78 +19745,73 @@ function readClaudeAccount(credentialsDirPath) {
19743
19745
  };
19744
19746
  }
19745
19747
 
19746
- // src/claudeExecutable.ts
19747
- import * as NodeFS2 from "node:fs";
19748
- import * as NodeOS2 from "node:os";
19749
- import * as NodePath2 from "node:path";
19750
- var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat", ".ps1"]);
19751
- var NPM_PACKAGE_ENTRY_CANDIDATES = [
19752
- ["node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe"],
19753
- ["node_modules", "@anthropic-ai", "claude-code", "cli.js"]
19754
- ];
19755
- function isExistingFile(filePath) {
19756
- try {
19757
- return NodeFS2.statSync(filePath).isFile();
19758
- } catch {
19759
- return false;
19760
- }
19761
- }
19762
- function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, isFile = isExistingFile, isWindows = NodeOS2.type() === "Windows_NT") {
19763
- if (!isWindows) {
19764
- return binaryPath;
19765
- }
19766
- const resolved = resolveWindowsExecutable(binaryPath, environment) ?? binaryPath;
19767
- const extension = NodePath2.win32.extname(resolved).toLowerCase();
19768
- if (!WINDOWS_SHIM_EXTENSIONS.has(extension)) {
19769
- return resolved;
19770
- }
19771
- const shimDirectory = NodePath2.win32.dirname(resolved);
19772
- for (const entrySegments of NPM_PACKAGE_ENTRY_CANDIDATES) {
19773
- const candidate = NodePath2.win32.join(shimDirectory, ...entrySegments);
19774
- if (isFile(candidate)) {
19775
- return candidate;
19776
- }
19777
- }
19778
- return binaryPath;
19779
- }
19780
- function resolveWindowsExecutable(binaryPath, environment) {
19781
- const trimmed = binaryPath.trim();
19782
- if (trimmed.length === 0) return void 0;
19783
- if (NodePath2.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
19784
- const pathValue = environment.PATH ?? environment.Path ?? "";
19785
- const pathext = environment.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
19786
- const extensions = pathext.split(";").filter((ext) => ext.length > 0);
19787
- const hasExt = NodePath2.win32.extname(trimmed).length > 0;
19788
- for (const dir of pathValue.split(";")) {
19789
- if (dir.trim().length === 0) continue;
19790
- const candidates = hasExt ? [NodePath2.win32.join(dir, trimmed)] : extensions.map((ext) => NodePath2.win32.join(dir, `${trimmed}${ext}`));
19791
- for (const candidate of candidates) {
19792
- if (isExistingFile(candidate)) return candidate;
19793
- }
19794
- }
19795
- return void 0;
19796
- }
19797
-
19798
19748
  // src/claudeHome.ts
19799
- import * as NodeOS4 from "node:os";
19800
- import * as NodePath4 from "node:path";
19801
-
19802
- // src/pathExpansion.ts
19803
19749
  import * as NodeOS3 from "node:os";
19804
19750
  import * as NodePath3 from "node:path";
19751
+
19752
+ // src/pathExpansion.ts
19753
+ import * as NodeOS2 from "node:os";
19754
+ import * as NodePath2 from "node:path";
19805
19755
  function expandHomePath(value) {
19806
- if (value === "~") return NodeOS3.homedir();
19756
+ if (value === "~") return NodeOS2.homedir();
19807
19757
  if (value.startsWith("~/") || value.startsWith("~\\")) {
19808
- return NodePath3.join(NodeOS3.homedir(), value.slice(2));
19758
+ return NodePath2.join(NodeOS2.homedir(), value.slice(2));
19809
19759
  }
19810
19760
  return value;
19811
19761
  }
19812
19762
 
19763
+ // src/usageLimit.ts
19764
+ var WINDOW_NAMES = {
19765
+ five_hour: "5-hour",
19766
+ seven_day: "7-day",
19767
+ seven_day_opus: "7-day Opus",
19768
+ seven_day_sonnet: "7-day Sonnet",
19769
+ overage: "overage"
19770
+ };
19771
+ var MAX_CREDIBLE_WAIT_MS = 30 * 24 * 60 * 60 * 1e3;
19772
+ var isBlockingRateLimit = (info) => info.status === "rejected" && info.overageStatus !== "allowed" && info.overageStatus !== "allowed_warning" && info.isUsingOverage !== true && info.overageInUse !== true;
19773
+ var isClearingRateLimit = (info) => info.status === "allowed" || info.status === "allowed_warning" || info.overageStatus === "allowed" || info.overageStatus === "allowed_warning" || info.isUsingOverage === true || info.overageInUse === true;
19774
+ var rateLimitKey = (info) => `${info.rateLimitType ?? "unknown"}:${String(info.resetsAt ?? "unknown")}`;
19775
+ var formatLimitWait = (waitMs) => {
19776
+ const totalMinutes = Math.max(1, Math.ceil(waitMs / 6e4));
19777
+ const days = Math.floor(totalMinutes / (24 * 60));
19778
+ const hours = Math.floor(totalMinutes % (24 * 60) / 60);
19779
+ const minutes = totalMinutes % 60;
19780
+ if (days > 0) return hours === 0 ? `${String(days)}d` : `${String(days)}d ${String(hours)}h`;
19781
+ if (hours === 0) return `${String(totalMinutes)}m`;
19782
+ return minutes === 0 ? `${String(hours)}h` : `${String(hours)}h ${String(minutes)}m`;
19783
+ };
19784
+ var waitUntil = (resetsAtSeconds, nowMs) => {
19785
+ if (resetsAtSeconds === void 0 || !Number.isFinite(nowMs)) return void 0;
19786
+ const waitMs = resetsAtSeconds * 1e3 - nowMs;
19787
+ return waitMs > 0 && waitMs <= MAX_CREDIBLE_WAIT_MS ? formatLimitWait(waitMs) : void 0;
19788
+ };
19789
+ var describeUsageLimitPause = (info, nowMs) => {
19790
+ const name = info.rateLimitType === void 0 ? void 0 : WINDOW_NAMES[info.rateLimitType];
19791
+ const wait = waitUntil(info.resetsAt, nowMs);
19792
+ return `Claude usage limit reached. This turn is paused until the ${name === void 0 ? "" : `${name} `}limit resets${wait === void 0 ? "" : ` in ${wait}`}.`;
19793
+ };
19794
+ var describeUsageLimitFailure = (rejected, nowMs) => {
19795
+ let name;
19796
+ let wait;
19797
+ let latestResetSeconds = Number.NEGATIVE_INFINITY;
19798
+ for (const [type4, resetsAt] of rejected) {
19799
+ const label = WINDOW_NAMES[type4];
19800
+ const reachable = waitUntil(resetsAt, nowMs);
19801
+ if (label === void 0 || reachable === void 0 || resetsAt === void 0) continue;
19802
+ if (resetsAt <= latestResetSeconds) continue;
19803
+ latestResetSeconds = resetsAt;
19804
+ name = label;
19805
+ wait = reachable;
19806
+ }
19807
+ return name === void 0 ? "Claude usage limit reached. Send the message again once the limit resets." : `Claude usage limit reached. The ${name} limit resets in ${wait}. Send the message again once it resets.`;
19808
+ };
19809
+
19813
19810
  // src/claudeHome.ts
19814
19811
  var CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
19815
19812
  function resolveClaudeHomePath(homePath) {
19816
19813
  const trimmed = homePath.trim();
19817
- return NodePath4.resolve(trimmed.length > 0 ? expandHomePath(trimmed) : NodeOS4.homedir());
19814
+ return NodePath3.resolve(trimmed.length > 0 ? expandHomePath(trimmed) : NodeOS3.homedir());
19818
19815
  }
19819
19816
  function claudeContinuationKey(homePath) {
19820
19817
  return `claude:home:${resolveClaudeHomePath(homePath)}`;
@@ -19822,18 +19819,18 @@ function claudeContinuationKey(homePath) {
19822
19819
  function resolveClaudeConfigDirPath(homePath, environment, cwd) {
19823
19820
  const trimmed = homePath.trim();
19824
19821
  if (trimmed.length > 0) {
19825
- return NodePath4.resolve(expandHomePath(trimmed));
19822
+ return NodePath3.resolve(expandHomePath(trimmed));
19826
19823
  }
19827
19824
  const environmentConfigDir = environment.CLAUDE_CONFIG_DIR?.trim() ?? "";
19828
19825
  if (environmentConfigDir.length > 0) {
19829
- return cwd ? NodePath4.resolve(cwd, environmentConfigDir) : NodePath4.resolve(environmentConfigDir);
19826
+ return cwd ? NodePath3.resolve(cwd, environmentConfigDir) : NodePath3.resolve(environmentConfigDir);
19830
19827
  }
19831
- return NodePath4.join(NodeOS4.homedir(), ".claude");
19828
+ return NodePath3.join(NodeOS3.homedir(), ".claude");
19832
19829
  }
19833
19830
  function resolveClaudeCredentialsDirPath(credentialsPath) {
19834
19831
  const trimmed = credentialsPath.trim();
19835
19832
  if (trimmed.length === 0) return null;
19836
- return NodePath4.resolve(expandHomePath(trimmed));
19833
+ return NodePath3.resolve(expandHomePath(trimmed));
19837
19834
  }
19838
19835
  function resolveClaudeHomeLayout(input) {
19839
19836
  return {
@@ -19864,13 +19861,13 @@ function claudeSignedOutMessage(input) {
19864
19861
  const configDir = input.configDir?.trim() ?? "";
19865
19862
  if (configDir.length > 0) {
19866
19863
  variables.push(
19867
- `CLAUDE_CONFIG_DIR set to ${quoteLiteral(NodePath4.resolve(input.cwd, configDir))}`
19864
+ `CLAUDE_CONFIG_DIR set to ${quoteLiteral(NodePath3.resolve(input.cwd, configDir))}`
19868
19865
  );
19869
19866
  }
19870
19867
  const credentialsDir = input.credentialsDir?.trim() ?? "";
19871
19868
  if (credentialsDir.length > 0) {
19872
19869
  variables.push(
19873
- `${CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE} set to ${quoteLiteral(NodePath4.resolve(input.cwd, credentialsDir))}`
19870
+ `${CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE} set to ${quoteLiteral(NodePath3.resolve(input.cwd, credentialsDir))}`
19874
19871
  );
19875
19872
  }
19876
19873
  const withVariables = variables.length > 0 ? `, with ${variables.join(" and ")}` : "";
@@ -19884,34 +19881,37 @@ function claudeFailureHint(input) {
19884
19881
  cwd: input.cwd
19885
19882
  });
19886
19883
  }
19884
+ const limit = input.usageLimit;
19885
+ if (limit !== void 0 && (limit.rejected.size > 0 || limit.latestAssistantRateLimited)) {
19886
+ return describeUsageLimitFailure(limit.rejected, limit.nowMs);
19887
+ }
19887
19888
  return void 0;
19888
19889
  }
19889
19890
 
19890
- // src/claudeSignIn.ts
19891
- var POSIX_BARE_VALUE = /^[A-Za-z0-9_@%+=:,./-]+$/;
19892
- function quotePosixShellValue(value) {
19893
- if (POSIX_BARE_VALUE.test(value)) return value;
19894
- return `'${value.replaceAll("'", `'\\''`)}'`;
19895
- }
19896
- function quotePowerShellValue(value) {
19897
- return `'${value.replaceAll("'", "''")}'`;
19898
- }
19899
- function withClaudeCredentialsDir(input) {
19900
- const variable = CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE;
19901
- return input.platform === "win32" ? `$env:${variable}=${quotePowerShellValue(input.credentialsDirPath)}; ${input.command}` : `${variable}=${quotePosixShellValue(input.credentialsDirPath)} ${input.command}`;
19902
- }
19903
- function claudeSignInCommand(credentialsPath, platform) {
19904
- const command = "claude auth login";
19905
- const credentialsDirPath = resolveClaudeCredentialsDirPath(credentialsPath);
19906
- if (credentialsDirPath === null) return command;
19907
- return withClaudeCredentialsDir({ command, credentialsDirPath, platform });
19908
- }
19909
-
19910
19891
  // src/claudeSkills.ts
19911
19892
  import * as NodeFSP from "node:fs/promises";
19912
- import * as NodePath5 from "node:path";
19913
- var FRONT_MATTER_KEY = /^(name|description):[ \t]*(.*)$/;
19893
+ import * as NodePath4 from "node:path";
19894
+ import * as NodeProcess from "node:process";
19895
+ var FRONT_MATTER_KEY = /^(description|disable-model-invocation|user-invocable|argument-hint):[ \t]*(.*)$/;
19914
19896
  var BLOCK_SCALAR = /^[|>][+-]?$/;
19897
+ function parseFrontMatterBoolean(value) {
19898
+ switch (value?.trim().toLowerCase()) {
19899
+ case "true":
19900
+ case "yes":
19901
+ case "on":
19902
+ case "y":
19903
+ case "1":
19904
+ return true;
19905
+ case "false":
19906
+ case "no":
19907
+ case "off":
19908
+ case "n":
19909
+ case "0":
19910
+ return false;
19911
+ default:
19912
+ return void 0;
19913
+ }
19914
+ }
19915
19915
  function parseSkillFrontMatter(contents) {
19916
19916
  const lines = contents.split(/\r?\n/);
19917
19917
  if (lines[0]?.trim() !== "---") return {};
@@ -19933,149 +19933,852 @@ function parseSkillFrontMatter(contents) {
19933
19933
  }
19934
19934
  value = block.join(value.startsWith("|") ? "\n" : " ");
19935
19935
  } else {
19936
+ if (key !== "description" && key !== "argument-hint") value = value.replace(/\s+#.*$/, "");
19936
19937
  value = value.replace(/^(["'])(.*)\1$/, "$2").trim();
19937
19938
  }
19938
19939
  if (value.length > 0 && values[key] === void 0) values[key] = value;
19939
19940
  }
19940
- return values;
19941
+ const description = values["description"];
19942
+ const argumentHint = values["argument-hint"];
19943
+ return {
19944
+ ...description === void 0 ? {} : { description },
19945
+ ...argumentHint === void 0 ? {} : { argumentHint },
19946
+ ...parseFrontMatterBoolean(values["disable-model-invocation"]) === true ? { userInvocationOnly: true } : {},
19947
+ ...parseFrontMatterBoolean(values["user-invocable"]) === false ? { userInvocable: false } : {}
19948
+ };
19941
19949
  }
19942
- async function discoverClaudeSkills(configDirPath) {
19943
- const root = NodePath5.join(configDirPath, "skills");
19944
- const entries = await NodeFSP.readdir(root).catch(() => []);
19945
- const skills = /* @__PURE__ */ new Map();
19946
- for (const entry of entries.toSorted()) {
19947
- const skillPath = NodePath5.join(root, entry, "SKILL.md");
19948
- const contents = await NodeFSP.readFile(skillPath, "utf8").catch(() => void 0);
19949
- if (contents === void 0) continue;
19950
- const frontMatter = parseSkillFrontMatter(contents);
19951
- const name = frontMatter.name ?? entry.trim();
19952
- if (name.length === 0) continue;
19953
- skills.set(name, {
19954
- name,
19955
- path: skillPath,
19956
- enabled: true,
19957
- scope: "user",
19958
- ...frontMatter.description === void 0 ? {} : { description: frontMatter.description }
19959
- });
19950
+ function claudeManagedSettingsPath(platform2, environment) {
19951
+ if (platform2 === "darwin") {
19952
+ return "/Library/Application Support/ClaudeCode/managed-settings.json";
19960
19953
  }
19961
- return [...skills.values()].toSorted((left, right) => left.name.localeCompare(right.name));
19954
+ if (platform2 === "win32") {
19955
+ const programData = environment["PROGRAMDATA"]?.trim();
19956
+ return programData ? NodePath4.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
19957
+ }
19958
+ return "/etc/claude-code/managed-settings.json";
19962
19959
  }
19963
-
19964
- // src/config.ts
19965
- var CLAUDE_AUTO_COMPACT_WINDOW_PATTERN = /^(?:|[1-9]\d{5}|1000000)$/;
19966
- function isRecord2(value) {
19967
- return typeof value === "object" && value !== null && !Array.isArray(value);
19960
+ function skillOverrideSettingsPaths(configDirPath, cwd, platform2, environment, repositoryRoot) {
19961
+ const managedPath = claudeManagedSettingsPath(platform2, environment);
19962
+ const root = repositoryRoot !== void 0 && repositoryRoot !== cwd ? repositoryRoot : void 0;
19963
+ return [
19964
+ NodePath4.join(configDirPath, "settings.json"),
19965
+ ...cwd ? [
19966
+ NodePath4.join(cwd, ".claude", "settings.json"),
19967
+ NodePath4.join(cwd, ".claude", "settings.local.json")
19968
+ ] : [],
19969
+ ...root ? [NodePath4.join(root, ".claude", "settings.local.json")] : [],
19970
+ ...managedPath ? [managedPath] : []
19971
+ ];
19968
19972
  }
19969
- function readString(value) {
19970
- return typeof value === "string" ? value : "";
19973
+ async function findRepositoryRoot(cwd) {
19974
+ let current = NodePath4.resolve(cwd);
19975
+ for (; ; ) {
19976
+ const found = await NodeFSP.stat(NodePath4.join(current, ".git")).then(
19977
+ () => true,
19978
+ () => false
19979
+ );
19980
+ if (found) return current;
19981
+ const parent = NodePath4.dirname(current);
19982
+ if (parent === current) return void 0;
19983
+ current = parent;
19984
+ }
19985
+ }
19986
+ function parseLenientJson(text) {
19987
+ let output = "";
19988
+ let index = 0;
19989
+ while (index < text.length) {
19990
+ const char = text[index];
19991
+ const next = text[index + 1];
19992
+ if (char === '"') {
19993
+ let end = index + 1;
19994
+ while (end < text.length && text[end] !== '"') end += text[end] === "\\" ? 2 : 1;
19995
+ output += text.slice(index, end + 1);
19996
+ index = end + 1;
19997
+ } else if (char === "/" && next === "/") {
19998
+ while (index < text.length && text[index] !== "\n") index += 1;
19999
+ } else if (char === "/" && next === "*") {
20000
+ const end = text.indexOf("*/", index + 2);
20001
+ index = end === -1 ? text.length : end + 2;
20002
+ } else {
20003
+ output += char;
20004
+ index += 1;
20005
+ }
20006
+ }
20007
+ return JSON.parse(output.replace(/,(\s*[}\]])/g, "$1"));
19971
20008
  }
19972
- function readTrimmed(value) {
19973
- return readString(value).trim();
20009
+ var SKILL_OVERRIDE_BY_VALUE = {
20010
+ off: { enabled: false, userInvocationOnly: false },
20011
+ // 与 `disable-model-invocation` 一样对 agent 隐藏,所以与普通开关分开记。
20012
+ "user-invocable-only": { enabled: true, userInvocationOnly: true },
20013
+ on: { enabled: true, userInvocationOnly: false },
20014
+ "name-only": { enabled: true, userInvocationOnly: false }
20015
+ };
20016
+ function readOverridesFromSettings(text) {
20017
+ let parsed;
20018
+ try {
20019
+ parsed = parseLenientJson(text);
20020
+ } catch {
20021
+ return void 0;
20022
+ }
20023
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
20024
+ const overrides = parsed["skillOverrides"];
20025
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
20026
+ return void 0;
20027
+ }
20028
+ const result = /* @__PURE__ */ new Map();
20029
+ for (const [name, value] of Object.entries(overrides)) {
20030
+ const override = typeof value === "string" ? SKILL_OVERRIDE_BY_VALUE[value] : void 0;
20031
+ if (override === void 0) return void 0;
20032
+ result.set(name, override);
20033
+ }
20034
+ return result;
19974
20035
  }
19975
- function readEnvironment(value) {
19976
- if (!Array.isArray(value)) return {};
19977
- const env = {};
19978
- for (const entry of value) {
19979
- if (!isRecord2(entry)) continue;
19980
- const name = readTrimmed(entry["name"]);
19981
- if (name.length === 0) continue;
19982
- env[name] = readString(entry["value"]);
20036
+ async function readSkillOverrides(configDirPath, cwd, platform2, environment) {
20037
+ const repositoryRoot = cwd === void 0 ? void 0 : await findRepositoryRoot(cwd);
20038
+ const merged = /* @__PURE__ */ new Map();
20039
+ for (const settingsPath of skillOverrideSettingsPaths(
20040
+ configDirPath,
20041
+ cwd,
20042
+ platform2,
20043
+ environment,
20044
+ repositoryRoot
20045
+ )) {
20046
+ const contents = await NodeFSP.readFile(settingsPath, "utf8").catch(() => void 0);
20047
+ if (contents === void 0) continue;
20048
+ for (const [name, override] of readOverridesFromSettings(contents) ?? []) {
20049
+ merged.set(name, override);
20050
+ }
19983
20051
  }
19984
- return env;
20052
+ return merged;
19985
20053
  }
19986
- function readCustomModels(value) {
19987
- if (!Array.isArray(value)) return [];
19988
- const models = [];
19989
- for (const entry of value) {
19990
- if (typeof entry === "string") {
19991
- const slug2 = entry.trim();
19992
- if (slug2.length > 0) models.push({ slug: slug2 });
19993
- continue;
20054
+ async function discoverClaudeSkills(configDirPath, options = {}) {
20055
+ const { cwd } = options;
20056
+ const overrides = await readSkillOverrides(
20057
+ configDirPath,
20058
+ cwd,
20059
+ options.platform ?? NodeProcess.platform,
20060
+ options.environment ?? NodeProcess.env
20061
+ );
20062
+ const roots = [
20063
+ { directory: NodePath4.join(configDirPath, "skills"), scope: "user" },
20064
+ ...cwd ? [{ directory: NodePath4.join(cwd, ".claude", "skills"), scope: "project" }] : []
20065
+ ];
20066
+ const skills = /* @__PURE__ */ new Map();
20067
+ for (const root of roots) {
20068
+ const entries = await NodeFSP.readdir(root.directory).catch(() => []);
20069
+ for (const entry of entries.toSorted()) {
20070
+ const name = entry.trim();
20071
+ if (name.length === 0 || skills.has(name)) continue;
20072
+ const skillPath = NodePath4.join(root.directory, entry, "SKILL.md");
20073
+ const contents = await NodeFSP.readFile(skillPath, "utf8").catch(() => void 0);
20074
+ if (contents === void 0) continue;
20075
+ const frontMatter = parseSkillFrontMatter(contents);
20076
+ const override = overrides.get(name);
20077
+ const userInvocationOnly = frontMatter.userInvocationOnly === true || override?.userInvocationOnly === true;
20078
+ skills.set(name, {
20079
+ name,
20080
+ path: skillPath,
20081
+ enabled: override?.enabled ?? true,
20082
+ scope: root.scope,
20083
+ ...frontMatter.description === void 0 ? {} : { description: frontMatter.description },
20084
+ ...userInvocationOnly ? { userInvocationOnly: true } : {},
20085
+ ...frontMatter.userInvocable === false ? { userInvocable: false } : {}
20086
+ });
19994
20087
  }
19995
- if (!isRecord2(entry)) continue;
19996
- const slug = readTrimmed(entry["slug"]);
19997
- if (slug.length === 0) continue;
19998
- const thinkingRaw = entry["thinkingOptions"];
19999
- const thinkingOptions = Array.isArray(thinkingRaw) ? thinkingRaw.flatMap((option) => {
20000
- if (!isRecord2(option)) return [];
20001
- const id2 = readTrimmed(option["id"]) || readTrimmed(option["value"]);
20002
- const label = readTrimmed(option["label"]) || id2;
20003
- if (id2.length === 0) return [];
20004
- return [
20005
- {
20006
- id: id2,
20007
- label,
20008
- ...typeof option["description"] === "string" ? { description: option["description"] } : {},
20009
- ...option["isDefault"] === true ? { isDefault: true } : {}
20010
- }
20011
- ];
20012
- }) : void 0;
20013
- const aliasesRaw = entry["aliases"];
20014
- const aliases = Array.isArray(aliasesRaw) ? aliasesRaw.filter(
20015
- (alias) => typeof alias === "string" && alias.trim().length > 0
20016
- ) : void 0;
20017
- const contextWindow = typeof entry["contextWindow"] === "number" && Number.isFinite(entry["contextWindow"]) && entry["contextWindow"] > 0 ? Math.round(entry["contextWindow"]) : void 0;
20018
- models.push({
20019
- slug,
20020
- ...readTrimmed(entry["label"]) ? { label: readTrimmed(entry["label"]) } : {},
20021
- ...thinkingOptions && thinkingOptions.length > 0 ? { thinkingOptions } : {},
20022
- ...contextWindow !== void 0 ? { contextWindow } : {},
20023
- ...aliases && aliases.length > 0 ? { aliases } : {}
20024
- });
20025
20088
  }
20026
- return models;
20089
+ return [...skills.values()].toSorted((left, right) => left.name.localeCompare(right.name));
20027
20090
  }
20028
- function readAutoCompactWindow(value) {
20029
- const trimmed = readTrimmed(value);
20030
- return CLAUDE_AUTO_COMPACT_WINDOW_PATTERN.test(trimmed) ? trimmed : "";
20091
+
20092
+ // src/model.ts
20093
+ import { matchCatalogModel } from "@ycodium-ai/plugin-api/executor";
20094
+ var DEFAULT_CLAUDE_MODEL_CAPABILITIES = {
20095
+ optionDescriptors: []
20096
+ };
20097
+ var CLAUDE_CUSTOM_MODEL_THINKING_SELECT = {
20098
+ id: "effort",
20099
+ label: "Reasoning"
20100
+ };
20101
+ function parseGenericCliVersion(output) {
20102
+ const match = output.match(/\bv?(\d+\.\d+\.\d+)\b/);
20103
+ return match?.[1] ?? null;
20031
20104
  }
20032
- function parseClaudeInstanceConfig(config) {
20033
- if (!isRecord2(config)) {
20105
+ function normalizeCustomModelSlug(slug) {
20106
+ return slug.trim();
20107
+ }
20108
+ function knownClaudeModelCapabilities(model, catalog, customModels) {
20109
+ const slug = model?.trim();
20110
+ const fromCatalog = catalog.find((candidate) => candidate.slug === slug)?.capabilities;
20111
+ if (fromCatalog) return fromCatalog;
20112
+ const custom = customModels?.find(
20113
+ (candidate) => normalizeCustomModelSlug(candidate.slug) === slug
20114
+ );
20115
+ if (custom?.thinkingOptions !== void 0 && custom.thinkingOptions.length > 0) {
20116
+ const currentValue = custom.thinkingOptions.find((option) => option.isDefault)?.id;
20034
20117
  return {
20035
- binaryPath: "",
20036
- homePath: "",
20037
- credentialsPath: "",
20038
- launchArgs: "",
20039
- autoCompactWindow: "",
20040
- customModels: [],
20041
- enabled: true,
20042
- environment: {},
20043
- envelopeDriver: void 0
20118
+ optionDescriptors: [
20119
+ {
20120
+ type: "select",
20121
+ id: CLAUDE_CUSTOM_MODEL_THINKING_SELECT.id,
20122
+ label: CLAUDE_CUSTOM_MODEL_THINKING_SELECT.label,
20123
+ options: custom.thinkingOptions,
20124
+ ...currentValue !== void 0 ? {} : {}
20125
+ }
20126
+ ]
20044
20127
  };
20045
20128
  }
20046
- const nested = isRecord2(config["config"]) ? config["config"] : void 0;
20047
- const settings = nested ?? config;
20048
- return {
20049
- binaryPath: readTrimmed(settings["binaryPath"]),
20050
- homePath: readTrimmed(settings["homePath"]) || readTrimmed(config["homePath"]),
20051
- credentialsPath: readTrimmed(settings["credentialsPath"]) || readTrimmed(config["credentialsPath"]),
20052
- launchArgs: readString(settings["launchArgs"]),
20053
- autoCompactWindow: readAutoCompactWindow(settings["autoCompactWindow"]),
20054
- customModels: readCustomModels(settings["customModels"]),
20055
- enabled: settings["enabled"] !== false,
20056
- environment: readEnvironment(config["environment"]),
20057
- envelopeDriver: readTrimmed(config["driver"]) || void 0
20058
- };
20129
+ return void 0;
20059
20130
  }
20060
- function tokenizeLaunchArgs(value) {
20061
- const input = value.trim();
20062
- if (!input) return [];
20063
- const tokens = [];
20064
- let current = "";
20065
- let quote;
20066
- let quoted = false;
20067
- for (let index = 0; index < input.length; index++) {
20068
- const char = input[index];
20069
- if (char === void 0) continue;
20070
- if (quote) {
20071
- if (char === quote) {
20072
- quote = void 0;
20073
- quoted = true;
20074
- } else if (char === "\\" && quote === '"') {
20075
- const next = input[index + 1];
20076
- if (next !== void 0 && ['"', "\\", "$", "`"].includes(next)) {
20077
- current += next;
20078
- index++;
20131
+ function claudeModelCapabilities(model, catalog, customModels) {
20132
+ return knownClaudeModelCapabilities(model, catalog, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
20133
+ }
20134
+ function isRecord2(value) {
20135
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20136
+ }
20137
+ function getModelSelectionStringOptionValue(modelSelection, optionId) {
20138
+ if (!isRecord2(modelSelection)) return void 0;
20139
+ const options = modelSelection["options"];
20140
+ if (!Array.isArray(options)) return void 0;
20141
+ for (const entry of options) {
20142
+ if (!isRecord2(entry)) continue;
20143
+ if (entry["id"] === optionId && typeof entry["value"] === "string") {
20144
+ const trimmed = entry["value"].trim();
20145
+ if (trimmed.length > 0) return trimmed;
20146
+ }
20147
+ }
20148
+ return void 0;
20149
+ }
20150
+ function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
20151
+ if (!isRecord2(modelSelection)) return void 0;
20152
+ const options = modelSelection["options"];
20153
+ if (!Array.isArray(options)) return void 0;
20154
+ for (const entry of options) {
20155
+ if (!isRecord2(entry)) continue;
20156
+ if (entry["id"] === optionId && typeof entry["value"] === "boolean") {
20157
+ return entry["value"];
20158
+ }
20159
+ }
20160
+ return void 0;
20161
+ }
20162
+ function modelIdFromSelection(selection) {
20163
+ if (!isRecord2(selection)) return void 0;
20164
+ const model = selection["model"];
20165
+ return typeof model === "string" ? model.trim() || void 0 : void 0;
20166
+ }
20167
+ function currentSelectValue(descriptor, raw) {
20168
+ if (raw && descriptor.options.some((option) => option.id === raw)) return raw;
20169
+ return descriptor.options.find((option) => option.isDefault)?.id;
20170
+ }
20171
+ function resolveClaudeEffort(caps, raw) {
20172
+ const descriptor = caps.optionDescriptors.find((entry) => entry.id === "effort");
20173
+ if (!descriptor || descriptor.type !== "select") return void 0;
20174
+ return currentSelectValue(descriptor, raw);
20175
+ }
20176
+ function normalizeClaudeCliEffort(effort) {
20177
+ if (!effort || effort === "ultrathink") {
20178
+ return void 0;
20179
+ }
20180
+ if (effort === "ultracode") {
20181
+ return "xhigh";
20182
+ }
20183
+ return effort;
20184
+ }
20185
+ function isClaudeUltracodeEffort(effort) {
20186
+ return effort === "ultracode";
20187
+ }
20188
+ function claudeCliModelId(input) {
20189
+ const requested = input.requestedModelId.trim();
20190
+ if (requested.length > 0 && (input.catalog ?? []).length === 0) return requested;
20191
+ const known = [
20192
+ ...(input.catalog ?? []).map((model) => model.slug),
20193
+ ...input.declared ?? [],
20194
+ ...(input.customModels ?? []).map((model) => normalizeCustomModelSlug(model.slug))
20195
+ ];
20196
+ return matchCatalogModel(input.requestedModelId, known) ?? input.placeholder;
20197
+ }
20198
+ function resolvePromptInjectedEffort(caps, rawEffort) {
20199
+ const trimmed = rawEffort?.trim();
20200
+ if (!trimmed) return void 0;
20201
+ const descriptor = caps.optionDescriptors.find((entry) => entry.id === "effort");
20202
+ if (!descriptor || descriptor.type !== "select") return void 0;
20203
+ if (!descriptor.promptInjectedValues?.includes(trimmed)) return void 0;
20204
+ return trimmed;
20205
+ }
20206
+ function applyClaudePromptEffortPrefix(text, effort) {
20207
+ const trimmed = text.trim();
20208
+ if (!trimmed) {
20209
+ return trimmed;
20210
+ }
20211
+ if (effort !== "ultrathink" || /^\/[^\s/]+(?:\s|$)/u.test(trimmed)) {
20212
+ return trimmed;
20213
+ }
20214
+ if (trimmed.startsWith("Ultrathink:")) {
20215
+ return trimmed;
20216
+ }
20217
+ return `Ultrathink:
20218
+ ${trimmed}`;
20219
+ }
20220
+ function getEffectiveClaudeAgentEffort(effort) {
20221
+ const normalized = normalizeClaudeCliEffort(effort);
20222
+ return normalized ? normalized : null;
20223
+ }
20224
+
20225
+ // src/probeModels.ts
20226
+ var toProbeCapabilities = (caps) => ({
20227
+ optionDescriptors: caps.optionDescriptors.map(
20228
+ (descriptor) => descriptor.type === "select" ? {
20229
+ id: descriptor.id,
20230
+ label: descriptor.label,
20231
+ type: "select",
20232
+ options: descriptor.options.map((option) => ({
20233
+ id: option.id,
20234
+ label: option.label,
20235
+ ...option.description === void 0 ? {} : { description: option.description },
20236
+ ...option.isDefault === void 0 ? {} : { isDefault: option.isDefault }
20237
+ }))
20238
+ } : {
20239
+ id: descriptor.id,
20240
+ label: descriptor.label,
20241
+ type: "boolean"
20242
+ }
20243
+ )
20244
+ });
20245
+ var claudeProbeModels = (input) => {
20246
+ const seen = /* @__PURE__ */ new Set();
20247
+ const out = [];
20248
+ for (const model of input.catalog) {
20249
+ if (seen.has(model.slug)) continue;
20250
+ seen.add(model.slug);
20251
+ const userLabel = input.customModels.find((entry) => entry.slug === model.slug)?.label;
20252
+ out.push({
20253
+ slug: model.slug,
20254
+ name: userLabel?.trim() || model.name,
20255
+ capabilities: toProbeCapabilities(model.capabilities)
20256
+ });
20257
+ }
20258
+ for (const model of input.customModels) {
20259
+ if (model.slug.trim().length === 0 || seen.has(model.slug)) continue;
20260
+ seen.add(model.slug);
20261
+ out.push({ slug: model.slug, name: model.label?.trim() || model.slug });
20262
+ }
20263
+ return out;
20264
+ };
20265
+
20266
+ // src/sessionSettings.ts
20267
+ function toSessionPermissionUpdates(toolName, suggestions) {
20268
+ const sessionScoped = (suggestions ?? []).map(
20269
+ (suggestion) => ({ ...suggestion, destination: "session" })
20270
+ );
20271
+ if (sessionScoped.length > 0) {
20272
+ return sessionScoped;
20273
+ }
20274
+ return [
20275
+ {
20276
+ type: "addRules",
20277
+ rules: [{ toolName }],
20278
+ behavior: "allow",
20279
+ destination: "session"
20280
+ }
20281
+ ];
20282
+ }
20283
+ var CLAUDE_SESSION_SETTING_KEYS = [
20284
+ "alwaysThinkingEnabled",
20285
+ "fastMode",
20286
+ "ultracode",
20287
+ "autoCompactWindow"
20288
+ ];
20289
+ function buildClaudeSessionSettings(input) {
20290
+ return {
20291
+ ...typeof input.thinking === "boolean" ? { alwaysThinkingEnabled: input.thinking } : {},
20292
+ ...input.fastMode ? { fastMode: true } : {},
20293
+ ...input.ultracode ? { ultracode: true } : {},
20294
+ ...input.autoCompactWindow ? { autoCompactWindow: Number(input.autoCompactWindow) } : {}
20295
+ };
20296
+ }
20297
+ function diffClaudeSessionSettings(previous, next) {
20298
+ const patch = {};
20299
+ let changed = false;
20300
+ const track = (key) => {
20301
+ if (previous[key] === next[key]) {
20302
+ return;
20303
+ }
20304
+ changed = true;
20305
+ patch[key] = next[key] ?? null;
20306
+ };
20307
+ for (const key of CLAUDE_SESSION_SETTING_KEYS) {
20308
+ track(key);
20309
+ }
20310
+ return changed ? patch : void 0;
20311
+ }
20312
+ var CLAUDE_NATIVE_MODES = [
20313
+ { id: "default", name: "Manual", description: "Always ask before making changes" },
20314
+ { id: "acceptEdits", name: "Accept edits", description: "Automatically accept all file edits" },
20315
+ { id: "plan", name: "Plan", description: "Create a plan before making changes" },
20316
+ { id: "auto", name: "Auto", description: "Claude handles permission decisions" },
20317
+ {
20318
+ id: "bypassPermissions",
20319
+ name: "Bypass permissions",
20320
+ description: "Accepts all permissions"
20321
+ }
20322
+ ];
20323
+ var CLAUDE_DEFAULT_NATIVE_MODE_ID = "default";
20324
+ var CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE = {
20325
+ "approval-required": "default",
20326
+ "auto-accept-edits": "acceptEdits",
20327
+ auto: "auto",
20328
+ "full-access": "bypassPermissions"
20329
+ };
20330
+ var CLAUDE_NATIVE_MODE_REPORT = {
20331
+ nativeModes: CLAUDE_NATIVE_MODES.map((mode) => ({
20332
+ id: mode.id,
20333
+ name: mode.name,
20334
+ description: mode.description
20335
+ })),
20336
+ currentNativeModeId: CLAUDE_DEFAULT_NATIVE_MODE_ID,
20337
+ nativeModeByRuntimeMode: CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE
20338
+ };
20339
+ function isClaudeNativeModeId(value) {
20340
+ return CLAUDE_NATIVE_MODES.some((mode) => mode.id === value);
20341
+ }
20342
+ function claudePermissionModeFor(runtimeMode, nativeModeId) {
20343
+ if (nativeModeId !== void 0 && isClaudeNativeModeId(nativeModeId)) return nativeModeId;
20344
+ return CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE[runtimeMode];
20345
+ }
20346
+ var CLAUDE_SETTING_SOURCES = [
20347
+ "user",
20348
+ "project",
20349
+ "local"
20350
+ ];
20351
+
20352
+ // src/claudeProbe.ts
20353
+ var CLAUDE_PROGRAM_NOT_CONFIGURED = "Claude program path is not configured";
20354
+ function createClaudeProbe(input) {
20355
+ const { recipe, host, modelCatalog } = input;
20356
+ const collectProcessText = async (stream) => {
20357
+ const chunks = [];
20358
+ for await (const chunk of stream) {
20359
+ chunks.push(Buffer.from(chunk));
20360
+ }
20361
+ return Buffer.concat(chunks).toString("utf8");
20362
+ };
20363
+ const probeAuth = () => {
20364
+ const hasEnvToken = (recipe.environment.ANTHROPIC_AUTH_TOKEN ?? "").trim().length > 0;
20365
+ const hasEnvApiKey = (recipe.environment.ANTHROPIC_API_KEY ?? "").trim().length > 0;
20366
+ if (hasEnvToken || hasEnvApiKey) {
20367
+ return {
20368
+ status: "authenticated",
20369
+ type: hasEnvToken ? "env-token" : "api-key",
20370
+ label: hasEnvToken ? "ANTHROPIC_AUTH_TOKEN" : "ANTHROPIC_API_KEY"
20371
+ };
20372
+ }
20373
+ const account = readClaudeAccount(resolveClaudeCredentialsDirPath(recipe.credentialsPath));
20374
+ switch (account.state) {
20375
+ case "signed-in":
20376
+ case "expired":
20377
+ return { status: "authenticated" };
20378
+ case "signed-out":
20379
+ return { status: "unauthenticated" };
20380
+ case "unreadable":
20381
+ return { status: "unknown" };
20382
+ }
20383
+ };
20384
+ let lastProbedVersion;
20385
+ const probe = async (signal) => {
20386
+ if (recipe.binaryPath.length === 0) {
20387
+ return {
20388
+ installed: false,
20389
+ version: null,
20390
+ readiness: "not-installed",
20391
+ auth: { status: "unknown" },
20392
+ detail: CLAUDE_PROGRAM_NOT_CONFIGURED
20393
+ };
20394
+ }
20395
+ if (signal?.aborted) {
20396
+ throw new Error("aborted");
20397
+ }
20398
+ let handle;
20399
+ try {
20400
+ handle = await host.process.spawn({
20401
+ program: recipe.binaryPath,
20402
+ args: ["--version"],
20403
+ owner: { bindingId: host.bindingId }
20404
+ });
20405
+ } catch (error) {
20406
+ if (error instanceof ExecutorSpawnRefusedError) throw error;
20407
+ const failure = describeExecutorSpawnFailure(error);
20408
+ return {
20409
+ installed: false,
20410
+ version: null,
20411
+ readiness: failure.readiness,
20412
+ auth: { status: "unknown" },
20413
+ detail: failure.detail
20414
+ };
20415
+ }
20416
+ const [stdout, stderr, exitCode] = await Promise.all([
20417
+ collectProcessText(handle.stdout),
20418
+ collectProcessText(handle.stderr),
20419
+ handle.exited
20420
+ ]);
20421
+ const version = parseGenericCliVersion(`${stdout}
20422
+ ${stderr}`);
20423
+ if (version !== null || exitCode === 0) {
20424
+ await modelCatalog.refresh({ signal, force: version !== lastProbedVersion });
20425
+ lastProbedVersion = version;
20426
+ }
20427
+ const models = claudeProbeModels({
20428
+ catalog: modelCatalog.current(),
20429
+ customModels: recipe.customModels
20430
+ });
20431
+ const auth = probeAuth();
20432
+ const skills = await discoverClaudeSkills(
20433
+ resolveClaudeConfigDirPath(recipe.homePath, { ...process.env, ...recipe.environment })
20434
+ );
20435
+ if (exitCode !== 0 && version === null) {
20436
+ const detail = (stderr || stdout || "claude --version failed").trim();
20437
+ return {
20438
+ installed: false,
20439
+ version: null,
20440
+ readiness: "error",
20441
+ auth,
20442
+ ...detail.length > 0 ? { detail } : {}
20443
+ };
20444
+ }
20445
+ if (auth.status === "unauthenticated") {
20446
+ return {
20447
+ installed: true,
20448
+ version,
20449
+ readiness: "error",
20450
+ auth,
20451
+ models,
20452
+ skills,
20453
+ detail: "Claude is signed out",
20454
+ // 退出登录了档位照样是那五档。不报就退回宿主四档,而那份映射少了 `plan`。
20455
+ ...CLAUDE_NATIVE_MODE_REPORT
20456
+ };
20457
+ }
20458
+ return {
20459
+ installed: true,
20460
+ version,
20461
+ readiness: "ready",
20462
+ auth,
20463
+ models,
20464
+ skills,
20465
+ ...CLAUDE_NATIVE_MODE_REPORT
20466
+ };
20467
+ };
20468
+ return probe;
20469
+ }
20470
+
20471
+ // src/claudeExecutable.ts
20472
+ import * as NodeFS2 from "node:fs";
20473
+ import * as NodeOS4 from "node:os";
20474
+ import * as NodePath5 from "node:path";
20475
+ var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat", ".ps1"]);
20476
+ var NPM_PACKAGE_ENTRY_CANDIDATES = [
20477
+ ["node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe"],
20478
+ ["node_modules", "@anthropic-ai", "claude-code", "cli.js"]
20479
+ ];
20480
+ function isExistingFile(filePath) {
20481
+ try {
20482
+ return NodeFS2.statSync(filePath).isFile();
20483
+ } catch {
20484
+ return false;
20485
+ }
20486
+ }
20487
+ function resolveClaudeSdkExecutablePath(binaryPath, environment = process.env, isFile = isExistingFile, isWindows = NodeOS4.type() === "Windows_NT") {
20488
+ if (!isWindows) {
20489
+ return binaryPath;
20490
+ }
20491
+ const resolved = resolveWindowsExecutable(binaryPath, environment) ?? binaryPath;
20492
+ const extension = NodePath5.win32.extname(resolved).toLowerCase();
20493
+ if (!WINDOWS_SHIM_EXTENSIONS.has(extension)) {
20494
+ return resolved;
20495
+ }
20496
+ const shimDirectory = NodePath5.win32.dirname(resolved);
20497
+ for (const entrySegments of NPM_PACKAGE_ENTRY_CANDIDATES) {
20498
+ const candidate = NodePath5.win32.join(shimDirectory, ...entrySegments);
20499
+ if (isFile(candidate)) {
20500
+ return candidate;
20501
+ }
20502
+ }
20503
+ return binaryPath;
20504
+ }
20505
+ function resolveWindowsExecutable(binaryPath, environment) {
20506
+ const trimmed = binaryPath.trim();
20507
+ if (trimmed.length === 0) return void 0;
20508
+ if (NodePath5.win32.isAbsolute(trimmed) && isExistingFile(trimmed)) return trimmed;
20509
+ const pathValue = environment.PATH ?? environment.Path ?? "";
20510
+ const pathext = environment.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
20511
+ const extensions = pathext.split(";").filter((ext) => ext.length > 0);
20512
+ const hasExt = NodePath5.win32.extname(trimmed).length > 0;
20513
+ for (const dir of pathValue.split(";")) {
20514
+ if (dir.trim().length === 0) continue;
20515
+ const candidates = hasExt ? [NodePath5.win32.join(dir, trimmed)] : extensions.map((ext) => NodePath5.win32.join(dir, `${trimmed}${ext}`));
20516
+ for (const candidate of candidates) {
20517
+ if (isExistingFile(candidate)) return candidate;
20518
+ }
20519
+ }
20520
+ return void 0;
20521
+ }
20522
+
20523
+ // src/claudeSignIn.ts
20524
+ var POSIX_BARE_VALUE = /^[A-Za-z0-9_@%+=:,./-]+$/;
20525
+ function quotePosixShellValue(value) {
20526
+ if (POSIX_BARE_VALUE.test(value)) return value;
20527
+ return `'${value.replaceAll("'", `'\\''`)}'`;
20528
+ }
20529
+ function quotePowerShellValue(value) {
20530
+ return `'${value.replaceAll("'", "''")}'`;
20531
+ }
20532
+ function withClaudeCredentialsDir(input) {
20533
+ const variable = CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE;
20534
+ return input.platform === "win32" ? `$env:${variable}=${quotePowerShellValue(input.credentialsDirPath)}; ${input.command}` : `${variable}=${quotePosixShellValue(input.credentialsDirPath)} ${input.command}`;
20535
+ }
20536
+ function claudeSignInCommand(credentialsPath, platform2) {
20537
+ const command = "claude auth login";
20538
+ const credentialsDirPath = resolveClaudeCredentialsDirPath(credentialsPath);
20539
+ if (credentialsDirPath === null) return command;
20540
+ return withClaudeCredentialsDir({ command, credentialsDirPath, platform: platform2 });
20541
+ }
20542
+
20543
+ // src/claudeWorkspaceInventory.ts
20544
+ import * as NodeFSP2 from "node:fs/promises";
20545
+ import * as NodePath6 from "node:path";
20546
+ var MAX_COMMAND_DEPTH = 5;
20547
+ var MAX_COMMAND_FILES = 500;
20548
+ var MAX_DESCRIPTION_LENGTH = 200;
20549
+ var firstBodyLine = (contents) => {
20550
+ const lines = contents.split(/\r?\n/);
20551
+ let index = 0;
20552
+ if (lines[0]?.trim() === "---") {
20553
+ const close = lines.findIndex((line, position) => position > 0 && line.trim() === "---");
20554
+ index = close === -1 ? lines.length : close + 1;
20555
+ }
20556
+ for (; index < lines.length; index += 1) {
20557
+ const line = (lines[index] ?? "").trim().replace(/^#+\s*/, "");
20558
+ if (line.length > 0) return line.slice(0, MAX_DESCRIPTION_LENGTH);
20559
+ }
20560
+ return void 0;
20561
+ };
20562
+ var scanCommands = async (claudeDir) => {
20563
+ const root = NodePath6.join(claudeDir, "commands");
20564
+ const commands = /* @__PURE__ */ new Map();
20565
+ const watched = [root];
20566
+ let level = [root];
20567
+ let files = 0;
20568
+ for (let depth = 0; depth < MAX_COMMAND_DEPTH && level.length > 0; depth += 1) {
20569
+ const next = [];
20570
+ for (const directory of level) {
20571
+ const entries = await NodeFSP2.readdir(directory, { withFileTypes: true }).catch(() => []);
20572
+ for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
20573
+ const entryPath = NodePath6.join(directory, entry.name);
20574
+ if (entry.isDirectory()) {
20575
+ next.push(entryPath);
20576
+ watched.push(entryPath);
20577
+ continue;
20578
+ }
20579
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".md")) continue;
20580
+ watched.push(entryPath);
20581
+ if (files >= MAX_COMMAND_FILES) continue;
20582
+ files += 1;
20583
+ const name = entry.name.slice(0, -".md".length).trim();
20584
+ if (name.length === 0 || commands.has(name)) continue;
20585
+ const contents = await NodeFSP2.readFile(entryPath, "utf8").catch(() => void 0);
20586
+ if (contents === void 0) continue;
20587
+ const frontMatter = parseSkillFrontMatter(contents);
20588
+ const description = frontMatter.description ?? firstBodyLine(contents);
20589
+ commands.set(name, {
20590
+ name,
20591
+ ...description === void 0 ? {} : { description },
20592
+ ...frontMatter.argumentHint === void 0 ? {} : { input: { hint: frontMatter.argumentHint } }
20593
+ });
20594
+ }
20595
+ }
20596
+ level = next;
20597
+ }
20598
+ return { commands: [...commands.values()], watched };
20599
+ };
20600
+ var watchSkillsRoot = async (claudeDir) => {
20601
+ const root = NodePath6.join(claudeDir, "skills");
20602
+ const entries = await NodeFSP2.readdir(root).catch(() => []);
20603
+ return [
20604
+ root,
20605
+ ...entries.flatMap((entry) => [
20606
+ NodePath6.join(root, entry),
20607
+ NodePath6.join(root, entry, "SKILL.md")
20608
+ ])
20609
+ ];
20610
+ };
20611
+ var mergeByName = (base, preferred) => {
20612
+ const merged = /* @__PURE__ */ new Map();
20613
+ for (const entry of base) merged.set(entry.name, entry);
20614
+ for (const entry of preferred) merged.set(entry.name, entry);
20615
+ return [...merged.values()].toSorted((left, right) => left.name.localeCompare(right.name));
20616
+ };
20617
+ var discoverClaudeWorkspaceInventory = async (input) => {
20618
+ const projectClaudeDir = NodePath6.join(input.cwd, ".claude");
20619
+ const [userCommands, projectCommands, skills, userSkillWatch, projectSkillWatch] = await Promise.all([
20620
+ scanCommands(input.configDirPath),
20621
+ scanCommands(projectClaudeDir),
20622
+ discoverClaudeSkills(input.configDirPath, {
20623
+ cwd: input.cwd,
20624
+ ...input.environment === void 0 ? {} : { environment: input.environment }
20625
+ }),
20626
+ watchSkillsRoot(input.configDirPath),
20627
+ watchSkillsRoot(projectClaudeDir)
20628
+ ]);
20629
+ return {
20630
+ slashCommands: mergeByName(userCommands.commands, projectCommands.commands),
20631
+ skills,
20632
+ // 项目目录本身也看:`.claude` 整个是后来才建的,它出现就该重查。
20633
+ watchPaths: [
20634
+ projectClaudeDir,
20635
+ ...userCommands.watched,
20636
+ ...projectCommands.watched,
20637
+ ...userSkillWatch,
20638
+ ...projectSkillWatch,
20639
+ // `skillOverrides` 在这几个设置文件里:开关一个技能也要重查。
20640
+ NodePath6.join(input.configDirPath, "settings.json"),
20641
+ NodePath6.join(projectClaudeDir, "settings.json"),
20642
+ NodePath6.join(projectClaudeDir, "settings.local.json")
20643
+ ]
20644
+ };
20645
+ };
20646
+ var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
20647
+ var nonEmptyString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
20648
+ var claudeSlashCommandsFromNames = (names) => Array.isArray(names) ? names.flatMap((entry) => {
20649
+ const name = nonEmptyString(entry);
20650
+ return name === void 0 ? [] : [{ name }];
20651
+ }) : [];
20652
+ var claudeSlashCommandsFromSdk = (commands) => Array.isArray(commands) ? commands.flatMap((entry) => {
20653
+ const record = asRecord(entry);
20654
+ const name = nonEmptyString(record?.["name"]);
20655
+ if (name === void 0) return [];
20656
+ const description = nonEmptyString(record?.["description"]);
20657
+ const hint = nonEmptyString(record?.["argumentHint"]);
20658
+ return [
20659
+ {
20660
+ name,
20661
+ ...description === void 0 ? {} : { description },
20662
+ ...hint === void 0 ? {} : { input: { hint } }
20663
+ }
20664
+ ];
20665
+ }) : [];
20666
+
20667
+ // src/config.ts
20668
+ var CLAUDE_AUTO_COMPACT_WINDOW_PATTERN = /^(?:|[1-9]\d{5}|1000000)$/;
20669
+ function isRecord3(value) {
20670
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20671
+ }
20672
+ function readString(value) {
20673
+ return typeof value === "string" ? value : "";
20674
+ }
20675
+ function readTrimmed(value) {
20676
+ return readString(value).trim();
20677
+ }
20678
+ function readEnvironment(value) {
20679
+ if (!Array.isArray(value)) return {};
20680
+ const env2 = {};
20681
+ for (const entry of value) {
20682
+ if (!isRecord3(entry)) continue;
20683
+ const name = readTrimmed(entry["name"]);
20684
+ if (name.length === 0) continue;
20685
+ env2[name] = readString(entry["value"]);
20686
+ }
20687
+ return env2;
20688
+ }
20689
+ function readCustomModels(value) {
20690
+ if (!Array.isArray(value)) return [];
20691
+ const models = [];
20692
+ for (const entry of value) {
20693
+ if (typeof entry === "string") {
20694
+ const slug2 = entry.trim();
20695
+ if (slug2.length > 0) models.push({ slug: slug2 });
20696
+ continue;
20697
+ }
20698
+ if (!isRecord3(entry)) continue;
20699
+ const slug = readTrimmed(entry["slug"]);
20700
+ if (slug.length === 0) continue;
20701
+ const thinkingRaw = entry["thinkingOptions"];
20702
+ const thinkingOptions = Array.isArray(thinkingRaw) ? thinkingRaw.flatMap((option) => {
20703
+ if (!isRecord3(option)) return [];
20704
+ const id2 = readTrimmed(option["id"]) || readTrimmed(option["value"]);
20705
+ const label = readTrimmed(option["label"]) || id2;
20706
+ if (id2.length === 0) return [];
20707
+ return [
20708
+ {
20709
+ id: id2,
20710
+ label,
20711
+ ...typeof option["description"] === "string" ? { description: option["description"] } : {},
20712
+ ...option["isDefault"] === true ? { isDefault: true } : {}
20713
+ }
20714
+ ];
20715
+ }) : void 0;
20716
+ const aliasesRaw = entry["aliases"];
20717
+ const aliases = Array.isArray(aliasesRaw) ? aliasesRaw.filter(
20718
+ (alias) => typeof alias === "string" && alias.trim().length > 0
20719
+ ) : void 0;
20720
+ const contextWindow = typeof entry["contextWindow"] === "number" && Number.isFinite(entry["contextWindow"]) && entry["contextWindow"] > 0 ? Math.round(entry["contextWindow"]) : void 0;
20721
+ models.push({
20722
+ slug,
20723
+ ...readTrimmed(entry["label"]) ? { label: readTrimmed(entry["label"]) } : {},
20724
+ ...thinkingOptions && thinkingOptions.length > 0 ? { thinkingOptions } : {},
20725
+ ...contextWindow !== void 0 ? { contextWindow } : {},
20726
+ ...aliases && aliases.length > 0 ? { aliases } : {}
20727
+ });
20728
+ }
20729
+ return models;
20730
+ }
20731
+ function readAutoCompactWindow(value) {
20732
+ const trimmed = readTrimmed(value);
20733
+ return CLAUDE_AUTO_COMPACT_WINDOW_PATTERN.test(trimmed) ? trimmed : "";
20734
+ }
20735
+ function parseClaudeInstanceConfig(config) {
20736
+ if (!isRecord3(config)) {
20737
+ return {
20738
+ binaryPath: "",
20739
+ homePath: "",
20740
+ credentialsPath: "",
20741
+ launchArgs: "",
20742
+ autoCompactWindow: "",
20743
+ customModels: [],
20744
+ enabled: true,
20745
+ environment: {},
20746
+ envelopeDriver: void 0
20747
+ };
20748
+ }
20749
+ const nested = isRecord3(config["config"]) ? config["config"] : void 0;
20750
+ const settings = nested ?? config;
20751
+ return {
20752
+ binaryPath: readTrimmed(settings["binaryPath"]),
20753
+ homePath: readTrimmed(settings["homePath"]) || readTrimmed(config["homePath"]),
20754
+ credentialsPath: readTrimmed(settings["credentialsPath"]) || readTrimmed(config["credentialsPath"]),
20755
+ launchArgs: readString(settings["launchArgs"]),
20756
+ autoCompactWindow: readAutoCompactWindow(settings["autoCompactWindow"]),
20757
+ customModels: readCustomModels(settings["customModels"]),
20758
+ enabled: settings["enabled"] !== false,
20759
+ environment: readEnvironment(config["environment"]),
20760
+ envelopeDriver: readTrimmed(config["driver"]) || void 0
20761
+ };
20762
+ }
20763
+ function tokenizeLaunchArgs(value) {
20764
+ const input = value.trim();
20765
+ if (!input) return [];
20766
+ const tokens = [];
20767
+ let current = "";
20768
+ let quote;
20769
+ let quoted = false;
20770
+ for (let index = 0; index < input.length; index++) {
20771
+ const char = input[index];
20772
+ if (char === void 0) continue;
20773
+ if (quote) {
20774
+ if (char === quote) {
20775
+ quote = void 0;
20776
+ quoted = true;
20777
+ } else if (char === "\\" && quote === '"') {
20778
+ const next = input[index + 1];
20779
+ if (next !== void 0 && ['"', "\\", "$", "`"].includes(next)) {
20780
+ current += next;
20781
+ index++;
20079
20782
  } else {
20080
20783
  current += char;
20081
20784
  }
@@ -20124,189 +20827,56 @@ function parseCliArgs(args, options) {
20124
20827
  continue;
20125
20828
  }
20126
20829
  if (booleanSet?.has(rest)) {
20127
- flags[rest] = null;
20128
- continue;
20129
- }
20130
- const next = tokens[i + 1];
20131
- if (next !== void 0 && !next.startsWith("--")) {
20132
- flags[rest] = next;
20133
- i++;
20134
- } else {
20135
- flags[rest] = null;
20136
- }
20137
- } else {
20138
- positionals.push(token);
20139
- }
20140
- }
20141
- return { flags, positionals };
20142
- }
20143
-
20144
- // src/events.ts
20145
- var EventBus = class {
20146
- #queued = [];
20147
- #waiters = [];
20148
- #closed = false;
20149
- push(event) {
20150
- const waiter = this.#waiters.shift();
20151
- if (waiter) {
20152
- waiter({ done: false, value: event });
20153
- return;
20154
- }
20155
- this.#queued.push(event);
20156
- }
20157
- close() {
20158
- if (this.#closed) return;
20159
- this.#closed = true;
20160
- while (this.#waiters.length > 0) {
20161
- this.#waiters.shift()?.({ done: true, value: void 0 });
20162
- }
20163
- }
20164
- [Symbol.asyncIterator]() {
20165
- return {
20166
- next: () => {
20167
- const queued = this.#queued.shift();
20168
- if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
20169
- if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
20170
- return new Promise((resolve3) => {
20171
- this.#waiters.push(resolve3);
20172
- });
20173
- }
20174
- };
20175
- }
20176
- };
20177
-
20178
- // src/model.ts
20179
- import { matchCatalogModel } from "@ycodium-ai/plugin-api/executor";
20180
- var DEFAULT_CLAUDE_MODEL_CAPABILITIES = {
20181
- optionDescriptors: []
20182
- };
20183
- var CLAUDE_CUSTOM_MODEL_THINKING_SELECT = {
20184
- id: "effort",
20185
- label: "Reasoning"
20186
- };
20187
- function parseGenericCliVersion(output) {
20188
- const match = output.match(/\bv?(\d+\.\d+\.\d+)\b/);
20189
- return match?.[1] ?? null;
20190
- }
20191
- function normalizeCustomModelSlug(slug) {
20192
- return slug.trim();
20193
- }
20194
- function knownClaudeModelCapabilities(model, catalog, customModels) {
20195
- const slug = model?.trim();
20196
- const fromCatalog = catalog.find((candidate) => candidate.slug === slug)?.capabilities;
20197
- if (fromCatalog) return fromCatalog;
20198
- const custom = customModels?.find(
20199
- (candidate) => normalizeCustomModelSlug(candidate.slug) === slug
20200
- );
20201
- if (custom?.thinkingOptions !== void 0 && custom.thinkingOptions.length > 0) {
20202
- const currentValue = custom.thinkingOptions.find((option) => option.isDefault)?.id;
20203
- return {
20204
- optionDescriptors: [
20205
- {
20206
- type: "select",
20207
- id: CLAUDE_CUSTOM_MODEL_THINKING_SELECT.id,
20208
- label: CLAUDE_CUSTOM_MODEL_THINKING_SELECT.label,
20209
- options: custom.thinkingOptions,
20210
- ...currentValue !== void 0 ? {} : {}
20211
- }
20212
- ]
20213
- };
20214
- }
20215
- return void 0;
20216
- }
20217
- function claudeModelCapabilities(model, catalog, customModels) {
20218
- return knownClaudeModelCapabilities(model, catalog, customModels) ?? DEFAULT_CLAUDE_MODEL_CAPABILITIES;
20219
- }
20220
- function isRecord3(value) {
20221
- return typeof value === "object" && value !== null && !Array.isArray(value);
20222
- }
20223
- function getModelSelectionStringOptionValue(modelSelection, optionId) {
20224
- if (!isRecord3(modelSelection)) return void 0;
20225
- const options = modelSelection["options"];
20226
- if (!Array.isArray(options)) return void 0;
20227
- for (const entry of options) {
20228
- if (!isRecord3(entry)) continue;
20229
- if (entry["id"] === optionId && typeof entry["value"] === "string") {
20230
- const trimmed = entry["value"].trim();
20231
- if (trimmed.length > 0) return trimmed;
20830
+ flags[rest] = null;
20831
+ continue;
20832
+ }
20833
+ const next = tokens[i + 1];
20834
+ if (next !== void 0 && !next.startsWith("--")) {
20835
+ flags[rest] = next;
20836
+ i++;
20837
+ } else {
20838
+ flags[rest] = null;
20839
+ }
20840
+ } else {
20841
+ positionals.push(token);
20232
20842
  }
20233
20843
  }
20234
- return void 0;
20844
+ return { flags, positionals };
20235
20845
  }
20236
- function getModelSelectionBooleanOptionValue(modelSelection, optionId) {
20237
- if (!isRecord3(modelSelection)) return void 0;
20238
- const options = modelSelection["options"];
20239
- if (!Array.isArray(options)) return void 0;
20240
- for (const entry of options) {
20241
- if (!isRecord3(entry)) continue;
20242
- if (entry["id"] === optionId && typeof entry["value"] === "boolean") {
20243
- return entry["value"];
20846
+
20847
+ // src/events.ts
20848
+ var EventBus = class {
20849
+ #queued = [];
20850
+ #waiters = [];
20851
+ #closed = false;
20852
+ push(event) {
20853
+ const waiter = this.#waiters.shift();
20854
+ if (waiter) {
20855
+ waiter({ done: false, value: event });
20856
+ return;
20244
20857
  }
20858
+ this.#queued.push(event);
20245
20859
  }
20246
- return void 0;
20247
- }
20248
- function modelIdFromSelection(selection) {
20249
- if (!isRecord3(selection)) return void 0;
20250
- const model = selection["model"];
20251
- return typeof model === "string" ? model.trim() || void 0 : void 0;
20252
- }
20253
- function currentSelectValue(descriptor, raw) {
20254
- if (raw && descriptor.options.some((option) => option.id === raw)) return raw;
20255
- return descriptor.options.find((option) => option.isDefault)?.id;
20256
- }
20257
- function resolveClaudeEffort(caps, raw) {
20258
- const descriptor = caps.optionDescriptors.find((entry) => entry.id === "effort");
20259
- if (!descriptor || descriptor.type !== "select") return void 0;
20260
- return currentSelectValue(descriptor, raw);
20261
- }
20262
- function normalizeClaudeCliEffort(effort) {
20263
- if (!effort || effort === "ultrathink") {
20264
- return void 0;
20265
- }
20266
- if (effort === "ultracode") {
20267
- return "xhigh";
20268
- }
20269
- return effort;
20270
- }
20271
- function isClaudeUltracodeEffort(effort) {
20272
- return effort === "ultracode";
20273
- }
20274
- function claudeCliModelId(input) {
20275
- const requested = input.requestedModelId.trim();
20276
- if (requested.length > 0 && (input.catalog ?? []).length === 0) return requested;
20277
- const known = [
20278
- ...(input.catalog ?? []).map((model) => model.slug),
20279
- ...input.declared ?? [],
20280
- ...(input.customModels ?? []).map((model) => normalizeCustomModelSlug(model.slug))
20281
- ];
20282
- return matchCatalogModel(input.requestedModelId, known) ?? input.placeholder;
20283
- }
20284
- function resolvePromptInjectedEffort(caps, rawEffort) {
20285
- const trimmed = rawEffort?.trim();
20286
- if (!trimmed) return void 0;
20287
- const descriptor = caps.optionDescriptors.find((entry) => entry.id === "effort");
20288
- if (!descriptor || descriptor.type !== "select") return void 0;
20289
- if (!descriptor.promptInjectedValues?.includes(trimmed)) return void 0;
20290
- return trimmed;
20291
- }
20292
- function applyClaudePromptEffortPrefix(text, effort) {
20293
- const trimmed = text.trim();
20294
- if (!trimmed) {
20295
- return trimmed;
20296
- }
20297
- if (effort !== "ultrathink" || /^\/[^\s/]+(?:\s|$)/u.test(trimmed)) {
20298
- return trimmed;
20860
+ close() {
20861
+ if (this.#closed) return;
20862
+ this.#closed = true;
20863
+ while (this.#waiters.length > 0) {
20864
+ this.#waiters.shift()?.({ done: true, value: void 0 });
20865
+ }
20299
20866
  }
20300
- if (trimmed.startsWith("Ultrathink:")) {
20301
- return trimmed;
20867
+ [Symbol.asyncIterator]() {
20868
+ return {
20869
+ next: () => {
20870
+ const queued = this.#queued.shift();
20871
+ if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
20872
+ if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
20873
+ return new Promise((resolve4) => {
20874
+ this.#waiters.push(resolve4);
20875
+ });
20876
+ }
20877
+ };
20302
20878
  }
20303
- return `Ultrathink:
20304
- ${trimmed}`;
20305
- }
20306
- function getEffectiveClaudeAgentEffort(effort) {
20307
- const normalized = normalizeClaudeCliEffort(effort);
20308
- return normalized ? normalized : null;
20309
- }
20879
+ };
20310
20880
 
20311
20881
  // src/sdkModels.ts
20312
20882
  import * as NodeOS5 from "node:os";
@@ -20355,9 +20925,9 @@ var claudeCatalogFromSdk = (models) => {
20355
20925
  };
20356
20926
  var silentPrompt = (signal) => ({
20357
20927
  [Symbol.asyncIterator]: () => ({
20358
- next: () => new Promise((resolve3) => {
20359
- if (signal.aborted) resolve3({ done: true, value: void 0 });
20360
- signal.addEventListener("abort", () => resolve3({ done: true, value: void 0 }), {
20928
+ next: () => new Promise((resolve4) => {
20929
+ if (signal.aborted) resolve4({ done: true, value: void 0 });
20930
+ signal.addEventListener("abort", () => resolve4({ done: true, value: void 0 }), {
20361
20931
  once: true
20362
20932
  });
20363
20933
  }),
@@ -20587,10 +21157,10 @@ function inputDiffStat(input) {
20587
21157
  let deletions = 0;
20588
21158
  for (const edit of input.edits) {
20589
21159
  if (edit === null || typeof edit !== "object" || Array.isArray(edit)) return void 0;
20590
- const stat = replacementDiffStat(edit, budget);
20591
- if (!stat) return void 0;
20592
- additions += stat.additions;
20593
- deletions += stat.deletions;
21160
+ const stat2 = replacementDiffStat(edit, budget);
21161
+ if (!stat2) return void 0;
21162
+ additions += stat2.additions;
21163
+ deletions += stat2.deletions;
20594
21164
  }
20595
21165
  return { additions, deletions };
20596
21166
  }
@@ -20976,7 +21546,7 @@ function terminalResultError(reason, failureHint) {
20976
21546
  case "turn_setup_failed":
20977
21547
  return "Claude could not start the turn.";
20978
21548
  case "blocking_limit":
20979
- return "Claude stopped: a usage limit blocked the request.";
21549
+ return failureHint ?? "Claude stopped: a usage limit blocked the request.";
20980
21550
  case "rapid_refill_breaker":
20981
21551
  return "Claude stopped: the context refilled too quickly after compaction.";
20982
21552
  case "prompt_too_long":
@@ -21011,10 +21581,8 @@ function resultOutcome(result, failureHint) {
21011
21581
  return { status: "completed", errorMessage };
21012
21582
  }
21013
21583
  if (isInterruptedResult(result)) return { status: "interrupted", errorMessage };
21014
- return {
21015
- status: resultErrorsText(result).includes("cancel") ? "cancelled" : "failed",
21016
- errorMessage
21017
- };
21584
+ const cancelled = resultErrorsText(result).includes("cancel");
21585
+ return cancelled ? { status: "cancelled", errorMessage } : { status: "failed", errorMessage: errorMessage ?? failureHint };
21018
21586
  }
21019
21587
  function streamKindFromDeltaType(deltaType) {
21020
21588
  return deltaType.includes("thinking") ? "reasoning_text" : "assistant_text";
@@ -21111,92 +21679,6 @@ function describeUnknownSdkMessage(kind, message) {
21111
21679
  return preview ? `${kind} \u2014 ${preview}` : `${kind} (no displayable text content)`;
21112
21680
  }
21113
21681
 
21114
- // src/sessionSettings.ts
21115
- function toSessionPermissionUpdates(toolName, suggestions) {
21116
- const sessionScoped = (suggestions ?? []).map(
21117
- (suggestion) => ({ ...suggestion, destination: "session" })
21118
- );
21119
- if (sessionScoped.length > 0) {
21120
- return sessionScoped;
21121
- }
21122
- return [
21123
- {
21124
- type: "addRules",
21125
- rules: [{ toolName }],
21126
- behavior: "allow",
21127
- destination: "session"
21128
- }
21129
- ];
21130
- }
21131
- var CLAUDE_SESSION_SETTING_KEYS = [
21132
- "alwaysThinkingEnabled",
21133
- "fastMode",
21134
- "ultracode",
21135
- "autoCompactWindow"
21136
- ];
21137
- function buildClaudeSessionSettings(input) {
21138
- return {
21139
- ...typeof input.thinking === "boolean" ? { alwaysThinkingEnabled: input.thinking } : {},
21140
- ...input.fastMode ? { fastMode: true } : {},
21141
- ...input.ultracode ? { ultracode: true } : {},
21142
- ...input.autoCompactWindow ? { autoCompactWindow: Number(input.autoCompactWindow) } : {}
21143
- };
21144
- }
21145
- function diffClaudeSessionSettings(previous, next) {
21146
- const patch = {};
21147
- let changed = false;
21148
- const track = (key) => {
21149
- if (previous[key] === next[key]) {
21150
- return;
21151
- }
21152
- changed = true;
21153
- patch[key] = next[key] ?? null;
21154
- };
21155
- for (const key of CLAUDE_SESSION_SETTING_KEYS) {
21156
- track(key);
21157
- }
21158
- return changed ? patch : void 0;
21159
- }
21160
- var CLAUDE_NATIVE_MODES = [
21161
- { id: "default", name: "Manual", description: "Always ask before making changes" },
21162
- { id: "acceptEdits", name: "Accept edits", description: "Automatically accept all file edits" },
21163
- { id: "plan", name: "Plan", description: "Create a plan before making changes" },
21164
- { id: "auto", name: "Auto", description: "Claude handles permission decisions" },
21165
- {
21166
- id: "bypassPermissions",
21167
- name: "Bypass permissions",
21168
- description: "Accepts all permissions"
21169
- }
21170
- ];
21171
- var CLAUDE_DEFAULT_NATIVE_MODE_ID = "default";
21172
- var CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE = {
21173
- "approval-required": "default",
21174
- "auto-accept-edits": "acceptEdits",
21175
- auto: "auto",
21176
- "full-access": "bypassPermissions"
21177
- };
21178
- var CLAUDE_NATIVE_MODE_REPORT = {
21179
- nativeModes: CLAUDE_NATIVE_MODES.map((mode) => ({
21180
- id: mode.id,
21181
- name: mode.name,
21182
- description: mode.description
21183
- })),
21184
- currentNativeModeId: CLAUDE_DEFAULT_NATIVE_MODE_ID,
21185
- nativeModeByRuntimeMode: CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE
21186
- };
21187
- function isClaudeNativeModeId(value) {
21188
- return CLAUDE_NATIVE_MODES.some((mode) => mode.id === value);
21189
- }
21190
- function claudePermissionModeFor(runtimeMode, nativeModeId) {
21191
- if (nativeModeId !== void 0 && isClaudeNativeModeId(nativeModeId)) return nativeModeId;
21192
- return CLAUDE_NATIVE_MODE_BY_RUNTIME_MODE[runtimeMode];
21193
- }
21194
- var CLAUDE_SETTING_SOURCES = [
21195
- "user",
21196
- "project",
21197
- "local"
21198
- ];
21199
-
21200
21682
  // src/readOnlyDirectories.ts
21201
21683
  var GLOB_METACHARACTERS = /[*?[\]{}]/;
21202
21684
  function toClaudeAbsolutePath(directory) {
@@ -21222,8 +21704,8 @@ function claudeReadAllowRules(directories) {
21222
21704
  // src/sessions.ts
21223
21705
  import * as NodeFS3 from "node:fs";
21224
21706
  import * as NodeOS6 from "node:os";
21225
- import * as NodePath6 from "node:path";
21226
- import * as NodeProcess from "node:process";
21707
+ import * as NodePath7 from "node:path";
21708
+ import * as NodeProcess2 from "node:process";
21227
21709
 
21228
21710
  // ../../packages/ycodium-jsonl-stream/src/jsonlStream.ts
21229
21711
  var DEFAULT_MAX_RETAINED_CHARS = 8e6;
@@ -21851,7 +22333,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21851
22333
  const scanned = matched.length > 0 ? matched : dirs;
21852
22334
  const found = [];
21853
22335
  for (const dir of scanned) {
21854
- const projectDir = NodePath6.join(transcriptDir, dir);
22336
+ const projectDir = NodePath7.join(transcriptDir, dir);
21855
22337
  let names;
21856
22338
  try {
21857
22339
  names = NodeFS3.readdirSync(projectDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(TRANSCRIPT_SUFFIX)).map((entry) => entry.name);
@@ -21859,17 +22341,17 @@ function listTranscriptFiles(transcriptDir, directory) {
21859
22341
  continue;
21860
22342
  }
21861
22343
  for (const name of names) {
21862
- let stat;
22344
+ let stat2;
21863
22345
  try {
21864
- stat = NodeFS3.statSync(NodePath6.join(projectDir, name));
22346
+ stat2 = NodeFS3.statSync(NodePath7.join(projectDir, name));
21865
22347
  } catch {
21866
22348
  continue;
21867
22349
  }
21868
22350
  found.push({
21869
- path: NodePath6.join(projectDir, name),
22351
+ path: NodePath7.join(projectDir, name),
21870
22352
  sessionId: name.slice(0, -TRANSCRIPT_SUFFIX.length),
21871
- size: stat.size,
21872
- mtimeMs: Number.isFinite(stat.mtimeMs) ? stat.mtimeMs : 0
22353
+ size: stat2.size,
22354
+ mtimeMs: Number.isFinite(stat2.mtimeMs) ? stat2.mtimeMs : 0
21873
22355
  });
21874
22356
  }
21875
22357
  }
@@ -21877,7 +22359,7 @@ function listTranscriptFiles(transcriptDir, directory) {
21877
22359
  }
21878
22360
  function readClaudeLiveSessionIds(configDir) {
21879
22361
  const live = /* @__PURE__ */ new Set();
21880
- const markerDir = NodePath6.join(configDir, "sessions");
22362
+ const markerDir = NodePath7.join(configDir, "sessions");
21881
22363
  let entries;
21882
22364
  try {
21883
22365
  entries = NodeFS3.readdirSync(markerDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name);
@@ -21887,7 +22369,7 @@ function readClaudeLiveSessionIds(configDir) {
21887
22369
  for (const entry of entries) {
21888
22370
  let marker;
21889
22371
  try {
21890
- marker = JSON.parse(NodeFS3.readFileSync(NodePath6.join(markerDir, entry), "utf8"));
22372
+ marker = JSON.parse(NodeFS3.readFileSync(NodePath7.join(markerDir, entry), "utf8"));
21891
22373
  } catch {
21892
22374
  continue;
21893
22375
  }
@@ -21895,7 +22377,7 @@ function readClaudeLiveSessionIds(configDir) {
21895
22377
  const typed = marker;
21896
22378
  if (typeof typed.pid !== "number" || typeof typed.sessionId !== "string") continue;
21897
22379
  try {
21898
- NodeProcess.kill(typed.pid, 0);
22380
+ NodeProcess2.kill(typed.pid, 0);
21899
22381
  } catch {
21900
22382
  continue;
21901
22383
  }
@@ -21941,7 +22423,7 @@ async function scanClaudeSessions(input) {
21941
22423
  function resolveTranscriptPath(input) {
21942
22424
  const fileName = `${input.sessionId}${TRANSCRIPT_SUFFIX}`;
21943
22425
  if (input.cwd !== void 0 && input.cwd.length > 0) {
21944
- const direct = NodePath6.join(
22426
+ const direct = NodePath7.join(
21945
22427
  input.transcriptDir,
21946
22428
  encodeClaudeProjectDirName(input.cwd),
21947
22429
  fileName
@@ -21958,7 +22440,7 @@ function resolveTranscriptPath(input) {
21958
22440
  return null;
21959
22441
  }
21960
22442
  for (const dir of dirs) {
21961
- const candidate = NodePath6.join(input.transcriptDir, dir, fileName);
22443
+ const candidate = NodePath7.join(input.transcriptDir, dir, fileName);
21962
22444
  try {
21963
22445
  if (NodeFS3.statSync(candidate).isFile()) return candidate;
21964
22446
  } catch {
@@ -22417,69 +22899,28 @@ function normalizeClaudeTaskProgressTokenUsage(value, memory) {
22417
22899
  const usage = value;
22418
22900
  const snapshot = makeClaudeTokenUsageSnapshot({
22419
22901
  activeTokens,
22420
- ...memory.lastKnownContextWindow !== void 0 ? { contextWindow: memory.lastKnownContextWindow } : {},
22421
- totalProcessedTokens: Math.max(
22422
- totalTokens,
22423
- memory.lastKnownTotalProcessedTokens ?? totalTokens
22424
- )
22425
- });
22426
- if (!snapshot) {
22427
- return void 0;
22428
- }
22429
- const toolUses = finiteNonNegativeInteger(usage.tool_uses);
22430
- const durationMs = finiteNonNegativeInteger(usage.duration_ms);
22431
- return {
22432
- ...snapshot,
22433
- ...toolUses !== void 0 ? { toolUses } : {},
22434
- ...durationMs !== void 0 ? { durationMs } : {}
22435
- };
22436
- }
22437
-
22438
- // src/probeModels.ts
22439
- var toProbeCapabilities = (caps) => ({
22440
- optionDescriptors: caps.optionDescriptors.map(
22441
- (descriptor) => descriptor.type === "select" ? {
22442
- id: descriptor.id,
22443
- label: descriptor.label,
22444
- type: "select",
22445
- options: descriptor.options.map((option) => ({
22446
- id: option.id,
22447
- label: option.label,
22448
- ...option.description === void 0 ? {} : { description: option.description },
22449
- ...option.isDefault === void 0 ? {} : { isDefault: option.isDefault }
22450
- }))
22451
- } : {
22452
- id: descriptor.id,
22453
- label: descriptor.label,
22454
- type: "boolean"
22455
- }
22456
- )
22457
- });
22458
- var claudeProbeModels = (input) => {
22459
- const seen = /* @__PURE__ */ new Set();
22460
- const out = [];
22461
- for (const model of input.catalog) {
22462
- if (seen.has(model.slug)) continue;
22463
- seen.add(model.slug);
22464
- const userLabel = input.customModels.find((entry) => entry.slug === model.slug)?.label;
22465
- out.push({
22466
- slug: model.slug,
22467
- name: userLabel?.trim() || model.name,
22468
- capabilities: toProbeCapabilities(model.capabilities)
22469
- });
22470
- }
22471
- for (const model of input.customModels) {
22472
- if (model.slug.trim().length === 0 || seen.has(model.slug)) continue;
22473
- seen.add(model.slug);
22474
- out.push({ slug: model.slug, name: model.label?.trim() || model.slug });
22902
+ ...memory.lastKnownContextWindow !== void 0 ? { contextWindow: memory.lastKnownContextWindow } : {},
22903
+ totalProcessedTokens: Math.max(
22904
+ totalTokens,
22905
+ memory.lastKnownTotalProcessedTokens ?? totalTokens
22906
+ )
22907
+ });
22908
+ if (!snapshot) {
22909
+ return void 0;
22475
22910
  }
22476
- return out;
22477
- };
22911
+ const toolUses = finiteNonNegativeInteger(usage.tool_uses);
22912
+ const durationMs = finiteNonNegativeInteger(usage.duration_ms);
22913
+ return {
22914
+ ...snapshot,
22915
+ ...toolUses !== void 0 ? { toolUses } : {},
22916
+ ...durationMs !== void 0 ? { durationMs } : {}
22917
+ };
22918
+ }
22478
22919
 
22479
22920
  // src/textGeneration.ts
22480
- import * as NodeFSP2 from "node:fs/promises";
22921
+ import * as NodeFSP3 from "node:fs/promises";
22481
22922
  import * as NodeOS7 from "node:os";
22482
- import * as NodePath7 from "node:path";
22923
+ import * as NodePath8 from "node:path";
22483
22924
  import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
22484
22925
 
22485
22926
  // src/textGenerationUtils.ts
@@ -22948,16 +23389,16 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22948
23389
  return decodeStructuredOutput(stdout, input.operation);
22949
23390
  };
22950
23391
  if (input.operation !== "generateThreadTitle") return runIn(input.cwd);
22951
- const scratchDirectory = await NodeFSP2.mkdtemp(
22952
- NodePath7.join(NodeOS7.tmpdir(), "ycodium-claude-title-")
23392
+ const scratchDirectory = await NodeFSP3.mkdtemp(
23393
+ NodePath8.join(NodeOS7.tmpdir(), "ycodium-claude-title-")
22953
23394
  );
22954
23395
  try {
22955
23396
  return await runIn(scratchDirectory);
22956
23397
  } finally {
22957
- await NodeFSP2.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
23398
+ await NodeFSP3.rm(scratchDirectory, { recursive: true, force: true }).catch(() => void 0);
22958
23399
  }
22959
23400
  };
22960
- const asRecord = (value, operation) => {
23401
+ const asRecord2 = (value, operation) => {
22961
23402
  if (!isRecord5(value))
22962
23403
  throw new Error(`${operation}: Claude returned invalid structured output.`);
22963
23404
  return value;
@@ -22965,7 +23406,7 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22965
23406
  return {
22966
23407
  generateCommitMessage: async (raw, signal) => {
22967
23408
  try {
22968
- const input = asRecord(raw, "generateCommitMessage");
23409
+ const input = asRecord2(raw, "generateCommitMessage");
22969
23410
  const includeBranch = input["includeBranch"] === true;
22970
23411
  const { prompt, outputSchema } = buildCommitMessagePrompt({
22971
23412
  branch: typeof input["branch"] === "string" ? input["branch"] : null,
@@ -22974,7 +23415,7 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22974
23415
  includeBranch,
22975
23416
  ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
22976
23417
  });
22977
- const generated = asRecord(
23418
+ const generated = asRecord2(
22978
23419
  await runClaudeJson({
22979
23420
  operation: "generateCommitMessage",
22980
23421
  cwd: readString3(input["cwd"]) || ".",
@@ -22993,114 +23434,799 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
22993
23434
  } catch (error) {
22994
23435
  throw normalizeCliError("claude", error, "Failed to generate commit message");
22995
23436
  }
22996
- },
22997
- generatePrContent: async (raw, signal) => {
22998
- try {
22999
- const input = asRecord(raw, "generatePrContent");
23000
- const { prompt, outputSchema } = buildPrContentPrompt({
23001
- baseBranch: readString3(input["baseBranch"]),
23002
- headBranch: readString3(input["headBranch"]),
23003
- commitSummary: readString3(input["commitSummary"]),
23004
- diffSummary: readString3(input["diffSummary"]),
23005
- diffPatch: readString3(input["diffPatch"]),
23006
- ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
23007
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
23008
- });
23009
- const generated = asRecord(
23010
- await runClaudeJson({
23011
- operation: "generatePrContent",
23012
- cwd: readString3(input["cwd"]) || ".",
23013
- prompt,
23014
- outputSchema,
23015
- modelSelection: input["modelSelection"],
23016
- ...signal === void 0 ? {} : { signal }
23017
- }),
23018
- "generatePrContent"
23019
- );
23020
- return {
23021
- title: sanitizePrTitle(readString3(generated["title"])),
23022
- body: readString3(generated["body"]).trim()
23023
- };
23024
- } catch (error) {
23025
- throw normalizeCliError("claude", error, "Failed to generate change request content");
23437
+ },
23438
+ generatePrContent: async (raw, signal) => {
23439
+ try {
23440
+ const input = asRecord2(raw, "generatePrContent");
23441
+ const { prompt, outputSchema } = buildPrContentPrompt({
23442
+ baseBranch: readString3(input["baseBranch"]),
23443
+ headBranch: readString3(input["headBranch"]),
23444
+ commitSummary: readString3(input["commitSummary"]),
23445
+ diffSummary: readString3(input["diffSummary"]),
23446
+ diffPatch: readString3(input["diffPatch"]),
23447
+ ...typeof input["changeRequestTemplate"] === "string" ? { changeRequestTemplate: input["changeRequestTemplate"] } : {},
23448
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
23449
+ });
23450
+ const generated = asRecord2(
23451
+ await runClaudeJson({
23452
+ operation: "generatePrContent",
23453
+ cwd: readString3(input["cwd"]) || ".",
23454
+ prompt,
23455
+ outputSchema,
23456
+ modelSelection: input["modelSelection"],
23457
+ ...signal === void 0 ? {} : { signal }
23458
+ }),
23459
+ "generatePrContent"
23460
+ );
23461
+ return {
23462
+ title: sanitizePrTitle(readString3(generated["title"])),
23463
+ body: readString3(generated["body"]).trim()
23464
+ };
23465
+ } catch (error) {
23466
+ throw normalizeCliError("claude", error, "Failed to generate change request content");
23467
+ }
23468
+ },
23469
+ generateBranchName: async (raw, signal) => {
23470
+ try {
23471
+ const input = asRecord2(raw, "generateBranchName");
23472
+ const { prompt, outputSchema } = buildBranchNamePrompt({
23473
+ message: readString3(input["message"]),
23474
+ ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
23475
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
23476
+ });
23477
+ const generated = asRecord2(
23478
+ await runClaudeJson({
23479
+ operation: "generateBranchName",
23480
+ cwd: readString3(input["cwd"]) || ".",
23481
+ prompt,
23482
+ outputSchema,
23483
+ modelSelection: input["modelSelection"],
23484
+ ...signal === void 0 ? {} : { signal }
23485
+ }),
23486
+ "generateBranchName"
23487
+ );
23488
+ return { branch: sanitizeBranchFragment(readString3(generated["branch"])) };
23489
+ } catch (error) {
23490
+ throw normalizeCliError("claude", error, "Failed to generate branch name");
23491
+ }
23492
+ },
23493
+ generateThreadTitle: async (raw, signal) => {
23494
+ try {
23495
+ const input = asRecord2(raw, "generateThreadTitle");
23496
+ const { prompt, outputSchema } = buildThreadTitlePrompt({
23497
+ message: readString3(input["message"]),
23498
+ ...typeof input["linkedContext"] === "string" && input["linkedContext"].length > 0 ? { linkedContext: input["linkedContext"] } : {},
23499
+ ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
23500
+ ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
23501
+ ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
23502
+ });
23503
+ const generated = asRecord2(
23504
+ await runClaudeJson({
23505
+ operation: "generateThreadTitle",
23506
+ cwd: readString3(input["cwd"]) || ".",
23507
+ prompt,
23508
+ outputSchema,
23509
+ modelSelection: input["modelSelection"],
23510
+ ...signal === void 0 ? {} : { signal }
23511
+ }),
23512
+ "generateThreadTitle"
23513
+ );
23514
+ return {
23515
+ title: sanitizeThreadTitle(readString3(generated["title"])),
23516
+ ...generated["needsRefinement"] === true ? { needsRefinement: true } : {}
23517
+ };
23518
+ } catch (error) {
23519
+ throw normalizeCliError("claude", error, "Failed to generate thread title");
23520
+ }
23521
+ },
23522
+ summarizeHandover: async (raw, signal) => {
23523
+ try {
23524
+ const input = asRecord2(raw, "summarizeHandover");
23525
+ const { prompt, outputSchema } = buildHandoverSummaryPrompt({
23526
+ transcript: readString3(input["transcript"])
23527
+ });
23528
+ const generated = asRecord2(
23529
+ await runClaudeJson({
23530
+ operation: "summarizeHandover",
23531
+ cwd: readString3(input["cwd"]) || ".",
23532
+ prompt,
23533
+ outputSchema,
23534
+ modelSelection: input["modelSelection"],
23535
+ ...signal === void 0 ? {} : { signal }
23536
+ }),
23537
+ "summarizeHandover"
23538
+ );
23539
+ return { summary: readString3(generated["summary"]).trim() };
23540
+ } catch (error) {
23541
+ throw normalizeCliError("claude", error, "Failed to summarize handover");
23542
+ }
23543
+ }
23544
+ };
23545
+ }
23546
+
23547
+ // src/usageRead/index.ts
23548
+ import * as NodePath12 from "node:path";
23549
+
23550
+ // src/usageRead/quota.ts
23551
+ import * as NodeFS4 from "node:fs";
23552
+ import * as NodePath9 from "node:path";
23553
+ import { mergeLaunchEnv as mergeLaunchEnv2, setsOwnModelConnection } from "@ycodium-ai/plugin-api/executor";
23554
+ import {
23555
+ parseRetryAfterMs
23556
+ } from "@ycodium-ai/plugin-api/usage";
23557
+ var CLAUDE_QUOTA_URL = "https://api.anthropic.com/api/oauth/usage";
23558
+ var REQUEST_TIMEOUT_MS = 1e4;
23559
+ var HOUR_MS = 60 * 60 * 1e3;
23560
+ var WINDOWS = [
23561
+ { field: "five_hour", id: "session", labelKey: "usage.window.session", windowMs: 5 * HOUR_MS },
23562
+ { field: "seven_day", id: "weekly", labelKey: "usage.window.weekly", windowMs: 7 * 24 * HOUR_MS }
23563
+ ];
23564
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
23565
+ var launchEnvironment = (recipe) => mergeLaunchEnv2({
23566
+ inherited: process.env,
23567
+ instanceTable: recipe.environment,
23568
+ handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
23569
+ });
23570
+ var claudeCredentialFileCandidates = (recipe) => {
23571
+ const environment = launchEnvironment(recipe);
23572
+ const credentialsDir = resolveClaudeCredentialsDirPath(
23573
+ environment[CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE] ?? ""
23574
+ );
23575
+ if (credentialsDir !== null) return [NodePath9.join(credentialsDir, ".credentials.json")];
23576
+ const configDir = resolveClaudeConfigDirPath(recipe.homePath, environment);
23577
+ const home = resolveClaudeHomePath(recipe.homePath);
23578
+ return [
23579
+ .../* @__PURE__ */ new Set([
23580
+ NodePath9.join(configDir, ".credentials.json"),
23581
+ NodePath9.join(home, ".claude", ".credentials.json"),
23582
+ NodePath9.join(home, ".credentials.json")
23583
+ ])
23584
+ ];
23585
+ };
23586
+ var readString4 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
23587
+ var readLoginCredential = (candidates) => {
23588
+ for (const path of candidates) {
23589
+ let raw;
23590
+ try {
23591
+ raw = NodeFS4.readFileSync(path, "utf8");
23592
+ } catch {
23593
+ continue;
23594
+ }
23595
+ if (raw.trim().length === 0) continue;
23596
+ let parsed;
23597
+ try {
23598
+ parsed = JSON.parse(raw);
23599
+ } catch {
23600
+ continue;
23601
+ }
23602
+ const oauth = isRecord6(parsed) ? parsed["claudeAiOauth"] : void 0;
23603
+ if (!isRecord6(oauth)) continue;
23604
+ const accessToken = readString4(oauth["accessToken"]);
23605
+ if (accessToken === void 0) continue;
23606
+ return {
23607
+ accessToken,
23608
+ subscriptionType: readString4(oauth["subscriptionType"]),
23609
+ rateLimitTier: readString4(oauth["rateLimitTier"]),
23610
+ path
23611
+ };
23612
+ }
23613
+ return null;
23614
+ };
23615
+ var claudePlanName = (credential) => {
23616
+ const type4 = credential.subscriptionType;
23617
+ if (type4 === void 0) return void 0;
23618
+ const name = `${type4.charAt(0).toUpperCase()}${type4.slice(1)}`;
23619
+ const multiplier = /(?:^|_)(\d+x)$/.exec(credential.rateLimitTier ?? "")?.[1];
23620
+ return multiplier === void 0 ? name : `${name} ${multiplier}`;
23621
+ };
23622
+ var toIso = (value) => {
23623
+ if (typeof value !== "string") return null;
23624
+ const parsed = Date.parse(value);
23625
+ return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
23626
+ };
23627
+ var readWindows = (body) => WINDOWS.flatMap((spec) => {
23628
+ const row = body[spec.field];
23629
+ if (!isRecord6(row)) return [];
23630
+ const utilization = row["utilization"];
23631
+ if (typeof utilization !== "number" || !Number.isFinite(utilization)) return [];
23632
+ return [
23633
+ {
23634
+ id: spec.id,
23635
+ label: { key: spec.labelKey },
23636
+ usedPercent: Math.min(100, Math.max(0, utilization)),
23637
+ resetsAt: toIso(row["resets_at"]),
23638
+ windowMs: spec.windowMs
23639
+ }
23640
+ ];
23641
+ });
23642
+ var readClaudeQuota = async (request) => {
23643
+ const { recipe } = request;
23644
+ const connectivity = CLAUDE_CARD.modelConnectivity;
23645
+ const environmentRows = Object.entries(recipe.environment).map(([name, value]) => ({
23646
+ name,
23647
+ value
23648
+ }));
23649
+ if (connectivity !== void 0 && setsOwnModelConnection(connectivity, environmentRows)) {
23650
+ const changesEndpoint = setsOwnModelConnection(
23651
+ {
23652
+ baseUrlEnvKey: connectivity.baseUrlEnvKey,
23653
+ backendSwitchEnvKeys: connectivity.backendSwitchEnvKeys
23654
+ },
23655
+ environmentRows
23656
+ );
23657
+ return {
23658
+ status: "unavailable",
23659
+ reason: changesEndpoint ? "customEndpoint" : "unsupportedLogin"
23660
+ };
23661
+ }
23662
+ const credential = readLoginCredential(claudeCredentialFileCandidates(recipe));
23663
+ if (credential === null) return { status: "unavailable", reason: "missingCredentials" };
23664
+ const now = request.now ?? Date.now;
23665
+ let response;
23666
+ try {
23667
+ response = await request.fetch(CLAUDE_QUOTA_URL, {
23668
+ method: "GET",
23669
+ headers: {
23670
+ Accept: "application/json",
23671
+ "anthropic-beta": "oauth-2025-04-20",
23672
+ Authorization: `Bearer ${credential.accessToken}`
23673
+ },
23674
+ redirect: "error",
23675
+ signal: request.signal === void 0 ? AbortSignal.timeout(REQUEST_TIMEOUT_MS) : AbortSignal.any([request.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)])
23676
+ });
23677
+ } catch (cause) {
23678
+ if (request.signal?.aborted === true) throw cause;
23679
+ return { status: "unavailable", reason: "unreachable" };
23680
+ }
23681
+ const release = () => {
23682
+ void response.body?.cancel().catch(() => void 0);
23683
+ };
23684
+ if (response.status === 429) {
23685
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"), now());
23686
+ release();
23687
+ return {
23688
+ status: "unavailable",
23689
+ reason: "rateLimited",
23690
+ ...retryAfterMs === null ? {} : { retryAfterMs }
23691
+ };
23692
+ }
23693
+ if (response.status === 401 || response.status === 403) {
23694
+ release();
23695
+ return { status: "unavailable", reason: "authRejected" };
23696
+ }
23697
+ if (response.status < 200 || response.status >= 300) {
23698
+ release();
23699
+ return { status: "unavailable", reason: "unreachable" };
23700
+ }
23701
+ let body;
23702
+ try {
23703
+ body = JSON.parse(await response.text());
23704
+ } catch {
23705
+ return { status: "unavailable", reason: "unreachable" };
23706
+ }
23707
+ if (!isRecord6(body)) return { status: "unavailable", reason: "unreachable" };
23708
+ const plan = claudePlanName(credential);
23709
+ return {
23710
+ status: "available",
23711
+ ...plan === void 0 ? {} : { plan },
23712
+ windows: readWindows(body),
23713
+ balances: [],
23714
+ accountKey: credential.path
23715
+ };
23716
+ };
23717
+
23718
+ // src/usageRead/scanCache.ts
23719
+ import * as NodePath11 from "node:path";
23720
+
23721
+ // src/usageRead/transcriptScan.ts
23722
+ import * as NodeFS5 from "node:fs";
23723
+ import * as NodePath10 from "node:path";
23724
+
23725
+ // src/usageRead/transcriptLine.ts
23726
+ var int = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
23727
+ var parseTimestampMs = (value) => {
23728
+ if (typeof value !== "string") return null;
23729
+ const parsed = Date.parse(value);
23730
+ return Number.isNaN(parsed) ? null : parsed;
23731
+ };
23732
+ var mightCarryUsage = (line) => line.includes('"usage"');
23733
+ var parseClaudeLine = (line) => {
23734
+ let parsed;
23735
+ try {
23736
+ parsed = JSON.parse(line);
23737
+ } catch {
23738
+ return null;
23739
+ }
23740
+ if (typeof parsed !== "object" || parsed === null) return null;
23741
+ const record = parsed;
23742
+ if (record["type"] !== "assistant") return null;
23743
+ const message = record["message"];
23744
+ if (typeof message !== "object" || message === null) return null;
23745
+ const messageRecord = message;
23746
+ const usage = messageRecord["usage"];
23747
+ if (typeof usage !== "object" || usage === null) return null;
23748
+ const usageRecord = usage;
23749
+ const timestampMs = parseTimestampMs(record["timestamp"]);
23750
+ if (timestampMs === null) return null;
23751
+ const model = typeof messageRecord["model"] === "string" ? messageRecord["model"] : "";
23752
+ if (model.length === 0) return null;
23753
+ const messageId = typeof messageRecord["id"] === "string" ? messageRecord["id"] : null;
23754
+ const requestId = typeof record["requestId"] === "string" ? record["requestId"] : null;
23755
+ const dedupeKey = messageId === null && requestId === null ? null : `${messageId ?? ""}:${requestId ?? ""}`;
23756
+ const cost = record["costUSD"];
23757
+ return {
23758
+ timestampMs,
23759
+ model,
23760
+ sessionId: typeof record["sessionId"] === "string" ? record["sessionId"] : "",
23761
+ totals: {
23762
+ uncachedInputTokens: int(usageRecord["input_tokens"]),
23763
+ cachedInputTokens: int(usageRecord["cache_read_input_tokens"]),
23764
+ cacheCreationTokens: int(usageRecord["cache_creation_input_tokens"]),
23765
+ outputTokens: int(usageRecord["output_tokens"]),
23766
+ reasoningTokens: 0
23767
+ },
23768
+ reportedCostUsd: typeof cost === "number" && Number.isFinite(cost) ? cost : null,
23769
+ dedupeKey
23770
+ };
23771
+ };
23772
+
23773
+ // src/usageRead/transcriptScan.ts
23774
+ var TRANSCRIPT_SUFFIX2 = ".jsonl";
23775
+ async function listTranscriptFiles2(root, sinceMs) {
23776
+ const found = [];
23777
+ const walk = async (dir) => {
23778
+ let entries;
23779
+ try {
23780
+ entries = await NodeFS5.promises.readdir(dir, { withFileTypes: true });
23781
+ } catch {
23782
+ return;
23783
+ }
23784
+ for (const entry of entries) {
23785
+ const child = NodePath10.join(dir, entry.name);
23786
+ if (entry.isDirectory()) {
23787
+ await walk(child);
23788
+ continue;
23789
+ }
23790
+ if (!entry.isFile() || !entry.name.endsWith(TRANSCRIPT_SUFFIX2)) continue;
23791
+ let stat2;
23792
+ try {
23793
+ stat2 = await NodeFS5.promises.stat(child);
23794
+ } catch {
23795
+ continue;
23796
+ }
23797
+ if (stat2.mtimeMs < sinceMs) continue;
23798
+ found.push({ path: child, size: stat2.size, mtimeMs: stat2.mtimeMs });
23799
+ }
23800
+ };
23801
+ await walk(root);
23802
+ return found;
23803
+ }
23804
+ async function statTranscriptRoot(root) {
23805
+ try {
23806
+ const stat2 = await NodeFS5.promises.stat(root);
23807
+ if (!stat2.isDirectory()) return null;
23808
+ return { volumeId: stat2.ino === 0 ? "" : `${String(stat2.dev)}:${String(stat2.ino)}` };
23809
+ } catch {
23810
+ return null;
23811
+ }
23812
+ }
23813
+ var GUARD_LENGTH = 64;
23814
+ var NEWLINE = 10;
23815
+ var CARRIAGE_RETURN = 13;
23816
+ var fnv1a = (bytes) => {
23817
+ let hash = 2166136261;
23818
+ for (let index = 0; index < bytes.length; index += 1) {
23819
+ hash ^= bytes[index];
23820
+ hash = Math.imul(hash, 16777619);
23821
+ }
23822
+ return hash >>> 0;
23823
+ };
23824
+ var asBuffer = (chunk) => typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
23825
+ async function readMatchingGuard(filePath, position) {
23826
+ const { guardLength, resumeOffset } = position;
23827
+ if (guardLength <= 0 || guardLength > GUARD_LENGTH || guardLength > resumeOffset) return null;
23828
+ try {
23829
+ const chunks = [];
23830
+ for await (const chunk of NodeFS5.createReadStream(filePath, {
23831
+ start: resumeOffset - guardLength,
23832
+ end: resumeOffset - 1
23833
+ })) {
23834
+ chunks.push(asBuffer(chunk));
23835
+ }
23836
+ const window2 = Buffer.concat(chunks);
23837
+ return window2.length === guardLength && fnv1a(window2) === position.guardHash ? window2 : null;
23838
+ } catch {
23839
+ return null;
23840
+ }
23841
+ }
23842
+ async function readTranscriptRecords(filePath, resumeFrom) {
23843
+ try {
23844
+ let resumed = false;
23845
+ let start = 0;
23846
+ let guardWindow = Buffer.alloc(0);
23847
+ if (resumeFrom !== void 0 && resumeFrom.resumeOffset > 0) {
23848
+ const window2 = await readMatchingGuard(filePath, resumeFrom);
23849
+ if (window2 !== null) {
23850
+ start = resumeFrom.resumeOffset;
23851
+ guardWindow = window2;
23852
+ resumed = true;
23853
+ }
23854
+ }
23855
+ const parseLine = (line, out) => {
23856
+ if (!mightCarryUsage(line)) return;
23857
+ const record = parseClaudeLine(line);
23858
+ if (record !== null) out.push(record);
23859
+ };
23860
+ const toLineString = (lineBuffer) => {
23861
+ const content = lineBuffer.length > 0 && lineBuffer[lineBuffer.length - 1] === CARRIAGE_RETURN ? lineBuffer.subarray(0, -1) : lineBuffer;
23862
+ return content.toString("utf8");
23863
+ };
23864
+ const records = [];
23865
+ let resumeOffset = start;
23866
+ let pendingChunks = [];
23867
+ for await (const raw of NodeFS5.createReadStream(filePath, start > 0 ? { start } : void 0)) {
23868
+ const chunk = asBuffer(raw);
23869
+ if (!chunk.includes(NEWLINE)) {
23870
+ pendingChunks.push(chunk);
23871
+ continue;
23872
+ }
23873
+ const buffer = pendingChunks.length === 0 ? chunk : Buffer.concat([...pendingChunks, chunk]);
23874
+ pendingChunks = [];
23875
+ let lineStart = 0;
23876
+ for (; ; ) {
23877
+ const newlineIndex = buffer.indexOf(NEWLINE, lineStart);
23878
+ if (newlineIndex === -1) break;
23879
+ parseLine(toLineString(buffer.subarray(lineStart, newlineIndex)), records);
23880
+ lineStart = newlineIndex + 1;
23881
+ }
23882
+ resumeOffset += lineStart;
23883
+ const consumed = buffer.subarray(0, lineStart);
23884
+ guardWindow = consumed.length >= GUARD_LENGTH ? Buffer.from(consumed.subarray(-GUARD_LENGTH)) : Buffer.concat([guardWindow, consumed]).subarray(-GUARD_LENGTH);
23885
+ if (lineStart < buffer.length) pendingChunks.push(buffer.subarray(lineStart));
23886
+ }
23887
+ const tailRecords = [];
23888
+ if (pendingChunks.length > 0) {
23889
+ const pending = pendingChunks.length === 1 ? pendingChunks[0] : Buffer.concat(pendingChunks);
23890
+ if (pending.length > 0) parseLine(toLineString(pending), tailRecords);
23891
+ }
23892
+ return {
23893
+ records,
23894
+ tailRecords,
23895
+ position: {
23896
+ resumeOffset,
23897
+ guardLength: guardWindow.length,
23898
+ guardHash: guardWindow.length > 0 ? fnv1a(guardWindow) : 0
23899
+ },
23900
+ resumed
23901
+ };
23902
+ } catch {
23903
+ return null;
23904
+ }
23905
+ }
23906
+
23907
+ // src/usageRead/scanCache.ts
23908
+ var USAGE_SCAN_CACHE_VERSION = 1;
23909
+ function encodeScanCache(cache) {
23910
+ const models = [];
23911
+ const sessions = [];
23912
+ const modelIndex = /* @__PURE__ */ new Map();
23913
+ const sessionIndex = /* @__PURE__ */ new Map();
23914
+ const intern = (table, index, value) => {
23915
+ const existing = index.get(value);
23916
+ if (existing !== void 0) return existing;
23917
+ const next = table.length;
23918
+ table.push(value);
23919
+ index.set(value, next);
23920
+ return next;
23921
+ };
23922
+ const serializeRecord = (record) => [
23923
+ record.timestampMs,
23924
+ intern(models, modelIndex, record.model),
23925
+ intern(sessions, sessionIndex, record.sessionId),
23926
+ record.totals.uncachedInputTokens,
23927
+ record.totals.cachedInputTokens,
23928
+ record.totals.cacheCreationTokens,
23929
+ record.totals.outputTokens,
23930
+ record.totals.reasoningTokens,
23931
+ record.dedupeKey,
23932
+ record.reportedCostUsd
23933
+ ];
23934
+ const files = {};
23935
+ for (const [path, entry] of cache) {
23936
+ files[path] = {
23937
+ s: entry.size,
23938
+ m: entry.mtimeMs,
23939
+ r: entry.records.map(serializeRecord),
23940
+ t: entry.tailRecords.map(serializeRecord),
23941
+ o: entry.position.resumeOffset,
23942
+ gl: entry.position.guardLength,
23943
+ gh: entry.position.guardHash
23944
+ };
23945
+ }
23946
+ return { version: USAGE_SCAN_CACHE_VERSION, models, sessions, files };
23947
+ }
23948
+ function decodeScanCache(document) {
23949
+ const cache = /* @__PURE__ */ new Map();
23950
+ if (typeof document !== "object" || document === null) return cache;
23951
+ const root = document;
23952
+ if (root.version !== USAGE_SCAN_CACHE_VERSION) return cache;
23953
+ if (!Array.isArray(root.models) || !Array.isArray(root.sessions)) return cache;
23954
+ if (typeof root.files !== "object" || root.files === null) return cache;
23955
+ if (!root.models.every((value) => typeof value === "string")) return cache;
23956
+ if (!root.sessions.every((value) => typeof value === "string")) return cache;
23957
+ const models = root.models;
23958
+ const sessions = root.sessions;
23959
+ const decodeRecords = (rows) => {
23960
+ const records = [];
23961
+ for (const row of rows) {
23962
+ if (!Array.isArray(row) || row.length < 10) return null;
23963
+ const [
23964
+ timestampMs,
23965
+ modelIndex,
23966
+ sessionIndex,
23967
+ uncached,
23968
+ cached,
23969
+ cacheCreation,
23970
+ output,
23971
+ reasoning,
23972
+ dedupeKey,
23973
+ reportedCostUsd
23974
+ ] = row;
23975
+ const model = typeof modelIndex === "number" ? models[modelIndex] : void 0;
23976
+ if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs) || model === void 0 || !Number.isFinite(uncached) || !Number.isFinite(cached) || !Number.isFinite(cacheCreation) || !Number.isFinite(output) || !Number.isFinite(reasoning)) {
23977
+ return null;
23978
+ }
23979
+ records.push({
23980
+ timestampMs,
23981
+ model,
23982
+ sessionId: (typeof sessionIndex === "number" ? sessions[sessionIndex] : void 0) ?? "",
23983
+ totals: {
23984
+ uncachedInputTokens: uncached,
23985
+ cachedInputTokens: cached,
23986
+ cacheCreationTokens: cacheCreation,
23987
+ outputTokens: output,
23988
+ reasoningTokens: reasoning
23989
+ },
23990
+ reportedCostUsd: typeof reportedCostUsd === "number" ? reportedCostUsd : null,
23991
+ dedupeKey: typeof dedupeKey === "string" ? dedupeKey : null
23992
+ });
23993
+ }
23994
+ return records;
23995
+ };
23996
+ for (const [path, raw] of Object.entries(root.files)) {
23997
+ if (typeof raw !== "object" || raw === null) continue;
23998
+ const entry = raw;
23999
+ if (typeof entry.s !== "number" || typeof entry.m !== "number") continue;
24000
+ if (!Array.isArray(entry.r) || !Array.isArray(entry.t)) continue;
24001
+ if (typeof entry.o !== "number" || !Number.isSafeInteger(entry.o) || entry.o < 0 || typeof entry.gl !== "number" || !Number.isSafeInteger(entry.gl) || entry.gl < 0 || entry.gl > GUARD_LENGTH || entry.gl > entry.o || typeof entry.gh !== "number" || !Number.isFinite(entry.gh)) {
24002
+ continue;
24003
+ }
24004
+ const records = decodeRecords(entry.r);
24005
+ const tailRecords = decodeRecords(entry.t);
24006
+ if (records === null || tailRecords === null) continue;
24007
+ cache.set(path, {
24008
+ size: entry.s,
24009
+ mtimeMs: entry.m,
24010
+ records,
24011
+ tailRecords,
24012
+ position: { resumeOffset: entry.o, guardLength: entry.gl, guardHash: entry.gh }
24013
+ });
24014
+ }
24015
+ return cache;
24016
+ }
24017
+ function pruneScanCache(cache, options) {
24018
+ let removed = 0;
24019
+ for (const [path, entry] of cache) {
24020
+ const agedOut = entry.mtimeMs < options.retentionCutoffMs;
24021
+ const underWalkedRoot = options.walkedRoots.some((root) => {
24022
+ const relative2 = NodePath11.relative(root, path);
24023
+ return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${NodePath11.sep}`) && !NodePath11.isAbsolute(relative2);
24024
+ });
24025
+ const deleted = underWalkedRoot && entry.mtimeMs >= options.windowStartMs && !options.livePaths.has(path);
24026
+ if (agedOut || deleted) {
24027
+ cache.delete(path);
24028
+ removed += 1;
24029
+ }
24030
+ }
24031
+ return removed;
24032
+ }
24033
+ function dedupeWithinFile(records, seen = /* @__PURE__ */ new Set()) {
24034
+ const kept = [];
24035
+ for (const record of records) {
24036
+ if (record.dedupeKey !== null) {
24037
+ if (seen.has(record.dedupeKey)) continue;
24038
+ seen.add(record.dedupeKey);
24039
+ }
24040
+ kept.push(record);
24041
+ }
24042
+ return kept;
24043
+ }
24044
+
24045
+ // src/usageRead/tokenUsage.ts
24046
+ var MTIME_SLACK_MS = 36 * 60 * 60 * 1e3;
24047
+ var CACHE_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
24048
+ var SCAN_CACHE_KEY_PREFIX = "claude-usage-scan:";
24049
+ var runtimes = /* @__PURE__ */ new Map();
24050
+ var runtimeFor = (root) => {
24051
+ let runtime = runtimes.get(root);
24052
+ if (runtime === void 0) {
24053
+ runtime = { fileCache: /* @__PURE__ */ new Map(), loaded: void 0, dirty: false, queue: Promise.resolve() };
24054
+ runtimes.set(root, runtime);
24055
+ }
24056
+ return runtime;
24057
+ };
24058
+ var ensureLoaded = (runtime, root, cache) => {
24059
+ runtime.loaded ??= (async () => {
24060
+ if (cache === void 0) return;
24061
+ const cached = await cache.getJson(`${SCAN_CACHE_KEY_PREFIX}${root}`).catch(() => null);
24062
+ if (cached === null) return;
24063
+ for (const [path, entry] of decodeScanCache(cached.value)) runtime.fileCache.set(path, entry);
24064
+ })();
24065
+ return runtime.loaded;
24066
+ };
24067
+ var readFileRecords = async (runtime, filePath, size, mtimeMs) => {
24068
+ const cached = runtime.fileCache.get(filePath);
24069
+ if (cached && cached.size === size && cached.mtimeMs === mtimeMs) {
24070
+ return cached.tailRecords.length === 0 ? cached.records : [...cached.records, ...cached.tailRecords];
24071
+ }
24072
+ const resumeFrom = cached !== void 0 && size > cached.size ? cached.position : void 0;
24073
+ const parsed = await readTranscriptRecords(filePath, resumeFrom);
24074
+ if (parsed === null) return null;
24075
+ const base = parsed.resumed && cached !== void 0 ? cached.records : [];
24076
+ const seen = /* @__PURE__ */ new Set();
24077
+ const records = dedupeWithinFile([...base, ...parsed.records], seen);
24078
+ const tailRecords = dedupeWithinFile(parsed.tailRecords, seen);
24079
+ runtime.fileCache.set(filePath, {
24080
+ size,
24081
+ mtimeMs,
24082
+ records,
24083
+ tailRecords,
24084
+ position: parsed.position
24085
+ });
24086
+ runtime.dirty = true;
24087
+ return tailRecords.length === 0 ? records : [...records, ...tailRecords];
24088
+ };
24089
+ var createClaudeTokenReader = (options) => {
24090
+ const { configDir, transcriptRoot, cache } = options;
24091
+ const scan = async (input) => {
24092
+ const sinceMs = Date.parse(input.since);
24093
+ const untilMs = Date.parse(input.until);
24094
+ if (Number.isNaN(sinceMs) || Number.isNaN(untilMs)) {
24095
+ throw new Error(`readTokenUsage: invalid window '${input.since}'..'${input.until}'`);
24096
+ }
24097
+ const root = await statTranscriptRoot(transcriptRoot);
24098
+ if (root === null) {
24099
+ return {
24100
+ records: [],
24101
+ source: {
24102
+ homePath: configDir,
24103
+ status: "missing",
24104
+ scannedFiles: 0,
24105
+ skippedFiles: 0,
24106
+ message: "No Claude Code session records found on this machine."
24107
+ }
24108
+ };
24109
+ }
24110
+ const runtime = runtimeFor(transcriptRoot);
24111
+ await ensureLoaded(runtime, transcriptRoot, cache);
24112
+ const startedAtMs = Date.now();
24113
+ const windowStartMs = sinceMs - MTIME_SLACK_MS;
24114
+ const files = (await listTranscriptFiles2(transcriptRoot, windowStartMs)).toSorted(
24115
+ (a, b3) => a.path < b3.path ? -1 : a.path > b3.path ? 1 : 0
24116
+ );
24117
+ let scannedFiles = 0;
24118
+ let skippedFiles = 0;
24119
+ let failedFiles = 0;
24120
+ const livePaths = /* @__PURE__ */ new Set();
24121
+ const seen = /* @__PURE__ */ new Set();
24122
+ const records = [];
24123
+ for (const file of files) {
24124
+ input.signal?.throwIfAborted();
24125
+ livePaths.add(file.path);
24126
+ const fileRecords = await readFileRecords(runtime, file.path, file.size, file.mtimeMs);
24127
+ if (fileRecords === null) {
24128
+ failedFiles += 1;
24129
+ continue;
24130
+ }
24131
+ if (fileRecords.length === 0) {
24132
+ skippedFiles += 1;
24133
+ continue;
23026
24134
  }
23027
- },
23028
- generateBranchName: async (raw, signal) => {
23029
- try {
23030
- const input = asRecord(raw, "generateBranchName");
23031
- const { prompt, outputSchema } = buildBranchNamePrompt({
23032
- message: readString3(input["message"]),
23033
- ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
23034
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
24135
+ scannedFiles += 1;
24136
+ for (const record of fileRecords) {
24137
+ if (record.dedupeKey !== null) {
24138
+ if (seen.has(record.dedupeKey)) continue;
24139
+ seen.add(record.dedupeKey);
24140
+ }
24141
+ if (record.timestampMs < sinceMs || record.timestampMs >= untilMs) continue;
24142
+ records.push({
24143
+ at: new Date(record.timestampMs).toISOString(),
24144
+ model: record.model,
24145
+ ...record.sessionId.length > 0 ? { sessionId: record.sessionId } : {},
24146
+ totals: record.totals,
24147
+ ...record.reportedCostUsd === null ? {} : { reportedCostUsd: record.reportedCostUsd }
23035
24148
  });
23036
- const generated = asRecord(
23037
- await runClaudeJson({
23038
- operation: "generateBranchName",
23039
- cwd: readString3(input["cwd"]) || ".",
23040
- prompt,
23041
- outputSchema,
23042
- modelSelection: input["modelSelection"],
23043
- ...signal === void 0 ? {} : { signal }
23044
- }),
23045
- "generateBranchName"
23046
- );
23047
- return { branch: sanitizeBranchFragment(readString3(generated["branch"])) };
23048
- } catch (error) {
23049
- throw normalizeCliError("claude", error, "Failed to generate branch name");
23050
24149
  }
23051
- },
23052
- generateThreadTitle: async (raw, signal) => {
24150
+ }
24151
+ const pruned = pruneScanCache(runtime.fileCache, {
24152
+ livePaths,
24153
+ walkedRoots: [transcriptRoot],
24154
+ windowStartMs,
24155
+ retentionCutoffMs: startedAtMs - CACHE_RETENTION_MS
24156
+ });
24157
+ if (pruned > 0) runtime.dirty = true;
24158
+ if (runtime.dirty && cache !== void 0) {
23053
24159
  try {
23054
- const input = asRecord(raw, "generateThreadTitle");
23055
- const { prompt, outputSchema } = buildThreadTitlePrompt({
23056
- message: readString3(input["message"]),
23057
- ...typeof input["linkedContext"] === "string" && input["linkedContext"].length > 0 ? { linkedContext: input["linkedContext"] } : {},
23058
- ...typeof input["previousTitle"] === "string" ? { previousTitle: input["previousTitle"] } : {},
23059
- ...Array.isArray(input["attachments"]) ? { attachments: input["attachments"] } : {},
23060
- ...isRecord5(input["policy"]) ? { policy: input["policy"] } : {}
23061
- });
23062
- const generated = asRecord(
23063
- await runClaudeJson({
23064
- operation: "generateThreadTitle",
23065
- cwd: readString3(input["cwd"]) || ".",
23066
- prompt,
23067
- outputSchema,
23068
- modelSelection: input["modelSelection"],
23069
- ...signal === void 0 ? {} : { signal }
23070
- }),
23071
- "generateThreadTitle"
24160
+ await cache.setJson(
24161
+ `${SCAN_CACHE_KEY_PREFIX}${transcriptRoot}`,
24162
+ encodeScanCache(runtime.fileCache)
23072
24163
  );
23073
- return {
23074
- title: sanitizeThreadTitle(readString3(generated["title"])),
23075
- ...generated["needsRefinement"] === true ? { needsRefinement: true } : {}
23076
- };
23077
- } catch (error) {
23078
- throw normalizeCliError("claude", error, "Failed to generate thread title");
24164
+ runtime.dirty = false;
24165
+ } catch {
23079
24166
  }
23080
- },
23081
- summarizeHandover: async (raw, signal) => {
23082
- try {
23083
- const input = asRecord(raw, "summarizeHandover");
23084
- const { prompt, outputSchema } = buildHandoverSummaryPrompt({
23085
- transcript: readString3(input["transcript"])
23086
- });
23087
- const generated = asRecord(
23088
- await runClaudeJson({
23089
- operation: "summarizeHandover",
23090
- cwd: readString3(input["cwd"]) || ".",
23091
- prompt,
23092
- outputSchema,
23093
- modelSelection: input["modelSelection"],
23094
- ...signal === void 0 ? {} : { signal }
23095
- }),
23096
- "summarizeHandover"
23097
- );
23098
- return { summary: readString3(generated["summary"]).trim() };
23099
- } catch (error) {
23100
- throw normalizeCliError("claude", error, "Failed to summarize handover");
24167
+ }
24168
+ return {
24169
+ records,
24170
+ source: {
24171
+ homePath: configDir,
24172
+ volumeId: root.volumeId,
24173
+ status: failedFiles > 0 ? "partial" : "ok",
24174
+ scannedFiles,
24175
+ skippedFiles,
24176
+ ...failedFiles > 0 ? { message: `${String(failedFiles)} session record file(s) could not be read.` } : {}
23101
24177
  }
24178
+ };
24179
+ };
24180
+ return {
24181
+ readTokenUsage: (input) => {
24182
+ const runtime = runtimeFor(transcriptRoot);
24183
+ const run = runtime.queue.then(() => scan(input));
24184
+ runtime.queue = run.catch(() => void 0);
24185
+ return run;
23102
24186
  }
23103
24187
  };
24188
+ };
24189
+
24190
+ // src/usageRead/index.ts
24191
+ var createClaudeUsage = (input) => {
24192
+ const { recipe, host } = input;
24193
+ const configDir = resolveClaudeConfigDirPath(recipe.homePath, {
24194
+ ...process.env,
24195
+ ...recipe.environment
24196
+ });
24197
+ const tokens = createClaudeTokenReader({
24198
+ configDir,
24199
+ transcriptRoot: NodePath12.join(configDir, "projects"),
24200
+ cache: input.cache
24201
+ });
24202
+ return {
24203
+ readQuota: ({ signal }) => readClaudeQuota({ recipe, fetch: host.fetch, signal }),
24204
+ readTokenUsage: tokens.readTokenUsage
24205
+ };
24206
+ };
24207
+
24208
+ // src/claudeSkillDispatch.ts
24209
+ var SKILL_MENTION_PATTERN = /(^|\s)\$([a-zA-Z][a-zA-Z0-9:_-]*)(?=\s|$)/g;
24210
+ function planClaudeSkillDispatch(prompt, skillNames) {
24211
+ const mentions = [...prompt.matchAll(SKILL_MENTION_PATTERN)].flatMap((match) => {
24212
+ const name = match[2] ?? "";
24213
+ if (!skillNames.has(name)) return [];
24214
+ const start = (match.index ?? 0) + (match[1]?.length ?? 0);
24215
+ return [{ name, start, end: start + name.length + 1 }];
24216
+ });
24217
+ const last = mentions.at(-1);
24218
+ if (last === void 0) return void 0;
24219
+ const leading = prompt.slice(0, last.start);
24220
+ const trailing = prompt.slice(last.end);
24221
+ const leadingWithInlineSlashes = mentions.slice(0, -1).reduceRight(
24222
+ (text, mention) => `${text.slice(0, mention.start)}/${text.slice(mention.start + 1)}`,
24223
+ leading
24224
+ ).trimEnd();
24225
+ return {
24226
+ leadingText: leadingWithInlineSlashes.length > 0 ? leadingWithInlineSlashes : void 0,
24227
+ commandText: `/${last.name}${trailing}`.trimEnd(),
24228
+ skillName: last.name
24229
+ };
23104
24230
  }
23105
24231
 
23106
24232
  // src/imageMime.ts
@@ -23183,9 +24309,9 @@ var PromptQueue = class {
23183
24309
  const queued = this.#queued.shift();
23184
24310
  if (queued !== void 0) return Promise.resolve(queued);
23185
24311
  if (this.#closed) return Promise.resolve(void 0);
23186
- return new Promise((resolve3) => {
24312
+ return new Promise((resolve4) => {
23187
24313
  this.#waiters.push((result) => {
23188
- resolve3(result.done ? void 0 : result.value);
24314
+ resolve4(result.done ? void 0 : result.value);
23189
24315
  });
23190
24316
  });
23191
24317
  }
@@ -23266,6 +24392,10 @@ function nativeProviderRefs(_context, options) {
23266
24392
  }
23267
24393
  async function buildUserMessageFromTurn(input) {
23268
24394
  const sdkContent = [];
24395
+ const dispatch = input.skillNames === void 0 ? void 0 : planClaudeSkillDispatch(input.text, input.skillNames);
24396
+ if (dispatch?.leadingText !== void 0) {
24397
+ sdkContent.push({ type: "text", text: dispatch.leadingText });
24398
+ }
23269
24399
  for (const attachment of input.attachments ?? []) {
23270
24400
  if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) continue;
23271
24401
  const rec = attachment;
@@ -23284,7 +24414,9 @@ async function buildUserMessageFromTurn(input) {
23284
24414
  const bytes = await input.attachmentsApi.read(id2);
23285
24415
  sdkContent.push(buildClaudeImageContentBlock({ mimeType, bytes }));
23286
24416
  }
23287
- if (input.text.length > 0) {
24417
+ if (dispatch !== void 0) {
24418
+ sdkContent.push({ type: "text", text: dispatch.commandText });
24419
+ } else if (input.text.length > 0) {
23288
24420
  sdkContent.push({ type: "text", text: input.text });
23289
24421
  }
23290
24422
  return buildUserMessage({ sdkContent });
@@ -23304,10 +24436,10 @@ async function persistProducedImage(input) {
23304
24436
  }
23305
24437
 
23306
24438
  // src/claudeInstance.ts
23307
- function compactEnv(env) {
24439
+ function compactEnv(env2) {
23308
24440
  const out = {};
23309
- if (!env) return out;
23310
- for (const [key, value] of Object.entries(env)) {
24441
+ if (!env2) return out;
24442
+ for (const [key, value] of Object.entries(env2)) {
23311
24443
  if (value !== void 0) out[key] = value;
23312
24444
  }
23313
24445
  return out;
@@ -23329,7 +24461,6 @@ function toClaudeMcpServers(entries) {
23329
24461
  }
23330
24462
  return servers;
23331
24463
  }
23332
- var CLAUDE_PROGRAM_NOT_CONFIGURED = "Claude program path is not configured";
23333
24464
  function createClaudeAdapter(recipe, host, options) {
23334
24465
  const boundInstanceId = options?.instanceId ?? host.instanceId ?? "claudeAgent";
23335
24466
  const claudeHomeEnvironment = makeClaudeEnvironment(recipe, options?.environment);
@@ -23345,7 +24476,7 @@ function createClaudeAdapter(recipe, host, options) {
23345
24476
  if (recipe.binaryPath.length === 0) throw new Error(CLAUDE_PROGRAM_NOT_CONFIGURED);
23346
24477
  return sdkCreateQuery(input);
23347
24478
  };
23348
- const buildLaunchEnvironment = () => mergeLaunchEnv2({
24479
+ const buildLaunchEnvironment = () => mergeLaunchEnv3({
23349
24480
  inherited: { ...process.env, ...options?.environment },
23350
24481
  instanceTable: recipe.environment,
23351
24482
  handOverrides: claudeHomeVariables(resolveClaudeHomeLayout(recipe))
@@ -23738,7 +24869,10 @@ function createClaudeAdapter(recipe, host, options) {
23738
24869
  compactedSinceLatestAssistantUsage: false,
23739
24870
  retrying: false,
23740
24871
  authenticationFailed: false,
23741
- nextSyntheticAssistantBlockIndex: -1
24872
+ nextSyntheticAssistantBlockIndex: -1,
24873
+ rejectedRateLimits: /* @__PURE__ */ new Map(),
24874
+ announcedUsageLimits: /* @__PURE__ */ new Set(),
24875
+ latestAssistantRateLimited: false
23742
24876
  };
23743
24877
  context.session = {
23744
24878
  ...context.session,
@@ -24325,6 +25459,10 @@ function createClaudeAdapter(recipe, host, options) {
24325
25459
  if (message.type !== "assistant") {
24326
25460
  return;
24327
25461
  }
25462
+ const rateLimitParent = message.parent_tool_use_id;
25463
+ if (context.turnState && (rateLimitParent === null || rateLimitParent === void 0)) {
25464
+ context.turnState.latestAssistantRateLimited = message.error === "rate_limit";
25465
+ }
24328
25466
  if (isClaudeSyntheticApiErrorMessage(message)) {
24329
25467
  if (context.turnState && message.error === "authentication_failed") {
24330
25468
  context.turnState.authenticationFailed = true;
@@ -24396,14 +25534,22 @@ function createClaudeAdapter(recipe, host, options) {
24396
25534
  return;
24397
25535
  }
24398
25536
  context.compactionInProgress = false;
24399
- const { status, errorMessage } = resultOutcome(
25537
+ const outcome = resultOutcome(
24400
25538
  message,
24401
25539
  claudeFailureHint({
24402
25540
  authenticationFailed: context.turnState?.authenticationFailed === true,
24403
25541
  environment: buildLaunchEnvironment(),
24404
- cwd: NodePath8.resolve(context.session.cwd ?? ".")
25542
+ cwd: NodePath13.resolve(context.session.cwd ?? "."),
25543
+ ...context.turnState === void 0 ? {} : {
25544
+ usageLimit: {
25545
+ rejected: context.turnState.rejectedRateLimits,
25546
+ latestAssistantRateLimited: context.turnState.latestAssistantRateLimited,
25547
+ nowMs: Date.now()
25548
+ }
25549
+ }
24405
25550
  })
24406
25551
  );
25552
+ const { status, errorMessage } = outcome;
24407
25553
  if (status === "failed") {
24408
25554
  await emitRuntimeError(context, errorMessage ?? "Claude turn failed.");
24409
25555
  }
@@ -24510,7 +25656,7 @@ function createClaudeAdapter(recipe, host, options) {
24510
25656
  }
24511
25657
  }
24512
25658
  switch (message.subtype) {
24513
- case "init":
25659
+ case "init": {
24514
25660
  offerRuntimeEvent(context, {
24515
25661
  ...base,
24516
25662
  type: "session.configured",
@@ -24518,6 +25664,27 @@ function createClaudeAdapter(recipe, host, options) {
24518
25664
  config: message
24519
25665
  }
24520
25666
  });
25667
+ const threadId = context.session.threadId;
25668
+ const names = claudeSlashCommandsFromNames(message.slash_commands);
25669
+ if (names.length > 0) host.reportSessionInventory?.({ threadId, slashCommands: names });
25670
+ void context.query.supportedCommands?.().then(
25671
+ (commands) => {
25672
+ const detailed = claudeSlashCommandsFromSdk(commands);
25673
+ if (detailed.length > 0) {
25674
+ host.reportSessionInventory?.({ threadId, slashCommands: detailed });
25675
+ }
25676
+ },
25677
+ () => void 0
25678
+ );
25679
+ return;
25680
+ }
25681
+ case "commands_changed":
25682
+ if (Array.isArray(message.commands)) {
25683
+ host.reportSessionInventory?.({
25684
+ threadId: context.session.threadId,
25685
+ slashCommands: claudeSlashCommandsFromSdk(message.commands)
25686
+ });
25687
+ }
24521
25688
  return;
24522
25689
  case "status": {
24523
25690
  const compacting = message.status === "compacting";
@@ -24825,7 +25992,6 @@ function createClaudeAdapter(recipe, host, options) {
24825
25992
  // deliberately so they don't masquerade as unknown-subtype warnings.
24826
25993
  case "local_command_output":
24827
25994
  case "plugin_install":
24828
- case "commands_changed":
24829
25995
  case "memory_recall":
24830
25996
  case "elicitation_complete":
24831
25997
  return;
@@ -24925,6 +26091,25 @@ function createClaudeAdapter(recipe, host, options) {
24925
26091
  rateLimits: message
24926
26092
  }
24927
26093
  });
26094
+ const info = message.rate_limit_info;
26095
+ const turn = context.turnState;
26096
+ if (info && turn) {
26097
+ const type4 = info.rateLimitType ?? "unknown";
26098
+ if (isBlockingRateLimit(info)) {
26099
+ turn.rejectedRateLimits.set(type4, info.resetsAt);
26100
+ const key = rateLimitKey(info);
26101
+ if (!turn.announcedUsageLimits.has(key)) {
26102
+ turn.announcedUsageLimits.add(key);
26103
+ await emitRuntimeWarning(
26104
+ context,
26105
+ describeUsageLimitPause(info, Date.parse(stamp.createdAt)),
26106
+ info
26107
+ );
26108
+ }
26109
+ } else if (isClearingRateLimit(info)) {
26110
+ turn.rejectedRateLimits.delete(type4);
26111
+ }
26112
+ }
24928
26113
  return;
24929
26114
  }
24930
26115
  };
@@ -25071,7 +26256,7 @@ function createClaudeAdapter(recipe, host, options) {
25071
26256
  if (streamFiber) {
25072
26257
  await Promise.race([
25073
26258
  streamFiber.catch(() => void 0),
25074
- new Promise((resolve3) => setTimeout(resolve3, 1e3))
26259
+ new Promise((resolve4) => setTimeout(resolve4, 1e3))
25075
26260
  ]);
25076
26261
  }
25077
26262
  const updatedAt = nowIso();
@@ -25282,7 +26467,7 @@ function createClaudeAdapter(recipe, host, options) {
25282
26467
  return await Promise.race([
25283
26468
  contextDeferred.promise,
25284
26469
  new Promise(
25285
- (resolve3) => setTimeout(() => resolve3(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
26470
+ (resolve4) => setTimeout(() => resolve4(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
25286
26471
  )
25287
26472
  ]);
25288
26473
  } catch {
@@ -25817,7 +27002,10 @@ function createClaudeAdapter(recipe, host, options) {
25817
27002
  compactedSinceLatestAssistantUsage: false,
25818
27003
  retrying: false,
25819
27004
  authenticationFailed: false,
25820
- nextSyntheticAssistantBlockIndex: -1
27005
+ nextSyntheticAssistantBlockIndex: -1,
27006
+ rejectedRateLimits: /* @__PURE__ */ new Map(),
27007
+ announcedUsageLimits: /* @__PURE__ */ new Set(),
27008
+ latestAssistantRateLimited: false
25821
27009
  };
25822
27010
  const updatedAt = nowIso();
25823
27011
  context.turnState = turnState;
@@ -25839,10 +27027,18 @@ function createClaudeAdapter(recipe, host, options) {
25839
27027
  providerRefs: {}
25840
27028
  });
25841
27029
  }
27030
+ const cliEnvironment = { ...process.env, ...recipe.environment };
27031
+ const skills = await discoverClaudeSkills(
27032
+ resolveClaudeConfigDirPath(recipe.homePath, cliEnvironment, context.session.cwd),
27033
+ { environment: cliEnvironment, ...context.session.cwd ? { cwd: context.session.cwd } : {} }
27034
+ );
25842
27035
  const message = await buildUserMessageFromTurn({
25843
27036
  text: buildPromptText(turnCapsForPrompt, input, boundInstanceId),
25844
27037
  attachments: input.attachments,
25845
- attachmentsApi: options?.attachments
27038
+ attachmentsApi: options?.attachments,
27039
+ skillNames: new Set(
27040
+ skills.filter((skill) => skill.enabled && skill.userInvocable !== false).map((skill) => skill.name)
27041
+ )
25846
27042
  });
25847
27043
  validateSteeringTarget();
25848
27044
  context.promptQueue.push({
@@ -25870,7 +27066,7 @@ function createClaudeAdapter(recipe, host, options) {
25870
27066
  context.query.interrupt().then(() => settled.promise).then(() => {
25871
27067
  acknowledged = true;
25872
27068
  }),
25873
- new Promise((resolve3) => setTimeout(resolve3, CLAUDE_INTERRUPT_GRACE_MS))
27069
+ new Promise((resolve4) => setTimeout(resolve4, CLAUDE_INTERRUPT_GRACE_MS))
25874
27070
  ]);
25875
27071
  } catch (cause) {
25876
27072
  throw toRequestError(threadId, "turn/interrupt", cause);
@@ -26000,7 +27196,7 @@ function createClaudeAdapter(recipe, host, options) {
26000
27196
  function claudeAdapterBaseEnvironment(recipe) {
26001
27197
  return { ...process.env, ...recipe.environment };
26002
27198
  }
26003
- function createClaudeInstance(config, host, attachments, overrides) {
27199
+ function createClaudeInstance(config, host, attachments, overrides, usageCache) {
26004
27200
  const recipe = parseClaudeInstanceConfig(config);
26005
27201
  const adapter = createClaudeAdapter(recipe, host, {
26006
27202
  instanceId: host.instanceId,
@@ -26009,118 +27205,7 @@ function createClaudeInstance(config, host, attachments, overrides) {
26009
27205
  environment: claudeAdapterBaseEnvironment(recipe),
26010
27206
  log: host.log
26011
27207
  });
26012
- const collectProcessText = async (stream) => {
26013
- const chunks = [];
26014
- for await (const chunk of stream) {
26015
- chunks.push(Buffer.from(chunk));
26016
- }
26017
- return Buffer.concat(chunks).toString("utf8");
26018
- };
26019
- const probeAuth = () => {
26020
- const hasEnvToken = (recipe.environment.ANTHROPIC_AUTH_TOKEN ?? "").trim().length > 0;
26021
- const hasEnvApiKey = (recipe.environment.ANTHROPIC_API_KEY ?? "").trim().length > 0;
26022
- if (hasEnvToken || hasEnvApiKey) {
26023
- return {
26024
- status: "authenticated",
26025
- type: hasEnvToken ? "env-token" : "api-key",
26026
- label: hasEnvToken ? "ANTHROPIC_AUTH_TOKEN" : "ANTHROPIC_API_KEY"
26027
- };
26028
- }
26029
- const account = readClaudeAccount(resolveClaudeCredentialsDirPath(recipe.credentialsPath));
26030
- switch (account.state) {
26031
- case "signed-in":
26032
- case "expired":
26033
- return { status: "authenticated" };
26034
- case "signed-out":
26035
- return { status: "unauthenticated" };
26036
- case "unreadable":
26037
- return { status: "unknown" };
26038
- }
26039
- };
26040
- let lastProbedVersion;
26041
- const probe = async (signal) => {
26042
- if (recipe.binaryPath.length === 0) {
26043
- return {
26044
- installed: false,
26045
- version: null,
26046
- readiness: "not-installed",
26047
- auth: { status: "unknown" },
26048
- detail: CLAUDE_PROGRAM_NOT_CONFIGURED
26049
- };
26050
- }
26051
- if (signal?.aborted) {
26052
- throw new Error("aborted");
26053
- }
26054
- let handle;
26055
- try {
26056
- handle = await host.process.spawn({
26057
- program: recipe.binaryPath,
26058
- args: ["--version"],
26059
- owner: { bindingId: host.bindingId }
26060
- });
26061
- } catch (error) {
26062
- if (error instanceof ExecutorSpawnRefusedError) throw error;
26063
- const failure = describeExecutorSpawnFailure(error);
26064
- return {
26065
- installed: false,
26066
- version: null,
26067
- readiness: failure.readiness,
26068
- auth: { status: "unknown" },
26069
- detail: failure.detail
26070
- };
26071
- }
26072
- const [stdout, stderr, exitCode] = await Promise.all([
26073
- collectProcessText(handle.stdout),
26074
- collectProcessText(handle.stderr),
26075
- handle.exited
26076
- ]);
26077
- const version = parseGenericCliVersion(`${stdout}
26078
- ${stderr}`);
26079
- if (version !== null || exitCode === 0) {
26080
- await adapter.modelCatalog.refresh({ signal, force: version !== lastProbedVersion });
26081
- lastProbedVersion = version;
26082
- }
26083
- const models = claudeProbeModels({
26084
- catalog: adapter.modelCatalog.current(),
26085
- customModels: recipe.customModels
26086
- });
26087
- const auth = probeAuth();
26088
- const skills = await discoverClaudeSkills(
26089
- resolveClaudeConfigDirPath(recipe.homePath, { ...process.env, ...recipe.environment })
26090
- );
26091
- if (exitCode !== 0 && version === null) {
26092
- const detail = (stderr || stdout || "claude --version failed").trim();
26093
- return {
26094
- installed: false,
26095
- version: null,
26096
- readiness: "error",
26097
- auth,
26098
- ...detail.length > 0 ? { detail } : {}
26099
- };
26100
- }
26101
- if (auth.status === "unauthenticated") {
26102
- return {
26103
- installed: true,
26104
- version,
26105
- readiness: "error",
26106
- auth,
26107
- models,
26108
- skills,
26109
- detail: "Claude is signed out",
26110
- // 退出登录了档位照样是那五档。不报就退回宿主四档,而那份映射少了 `plan`。
26111
- ...CLAUDE_NATIVE_MODE_REPORT
26112
- };
26113
- }
26114
- return {
26115
- installed: true,
26116
- version,
26117
- readiness: "ready",
26118
- auth,
26119
- models,
26120
- skills,
26121
- ...CLAUDE_NATIVE_MODE_REPORT
26122
- };
26123
- };
27208
+ const probe = createClaudeProbe({ recipe, host, modelCatalog: adapter.modelCatalog });
26124
27209
  const resolveTranscriptRoots = (directory) => {
26125
27210
  const configDir = resolveClaudeConfigDirPath(
26126
27211
  recipe.homePath,
@@ -26129,7 +27214,7 @@ ${stderr}`);
26129
27214
  );
26130
27215
  return {
26131
27216
  configDir,
26132
- transcriptDir: NodePath8.join(configDir, "projects")
27217
+ transcriptDir: NodePath13.join(configDir, "projects")
26133
27218
  };
26134
27219
  };
26135
27220
  return {
@@ -26146,8 +27231,18 @@ ${stderr}`);
26146
27231
  readThread: adapter.readThread,
26147
27232
  events: adapter.events,
26148
27233
  dispose: adapter.dispose,
27234
+ // 项目里的命令与技能:探测没有工作区,只能报用户配置目录那份;这里按目录扫。
27235
+ workspaceInventory: ({ cwd }) => {
27236
+ const cliEnvironment = { ...process.env, ...recipe.environment };
27237
+ return discoverClaudeWorkspaceInventory({
27238
+ configDirPath: resolveClaudeConfigDirPath(recipe.homePath, cliEnvironment, cwd),
27239
+ cwd,
27240
+ environment: cliEnvironment
27241
+ });
27242
+ },
26149
27243
  textGeneration: createClaudeTextGeneration(recipe, host, adapter.modelCatalog.current),
26150
27244
  continuationKey: claudeContinuationKey(recipe.homePath),
27245
+ usage: createClaudeUsage({ recipe, host, cache: usageCache }),
26151
27246
  sessions: {
26152
27247
  scan: async (input) => {
26153
27248
  const roots = resolveTranscriptRoots(input.directory);
@@ -26188,12 +27283,13 @@ ${stderr}`);
26188
27283
 
26189
27284
  // src/index.ts
26190
27285
  var capturedAttachments;
27286
+ var capturedCache;
26191
27287
  var executors = [
26192
27288
  {
26193
27289
  card: CLAUDE_CARD,
26194
27290
  createInstance: async (config, host) => {
26195
27291
  host.log.info("createInstance");
26196
- return createClaudeInstance(config, host, capturedAttachments);
27292
+ return createClaudeInstance(config, host, capturedAttachments, void 0, capturedCache);
26197
27293
  }
26198
27294
  }
26199
27295
  ];
@@ -26203,6 +27299,7 @@ var index_default = definePlugin({
26203
27299
  executors,
26204
27300
  activate(ctx) {
26205
27301
  capturedAttachments = ctx.attachments;
27302
+ capturedCache = ctx.cache;
26206
27303
  ctx.log.info("claude executor ready");
26207
27304
  }
26208
27305
  });