@ycodium-ai/agent-claude 0.2.2672 → 0.2.2690

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +234 -49
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -19900,18 +19900,37 @@ function withClaudeCredentialsDir(input) {
19900
19900
  const variable = CLAUDE_CREDENTIALS_DIR_ENVIRONMENT_VARIABLE;
19901
19901
  return input.platform === "win32" ? `$env:${variable}=${quotePowerShellValue(input.credentialsDirPath)}; ${input.command}` : `${variable}=${quotePosixShellValue(input.credentialsDirPath)} ${input.command}`;
19902
19902
  }
19903
- function claudeSignInCommand(credentialsPath, platform) {
19903
+ function claudeSignInCommand(credentialsPath, platform2) {
19904
19904
  const command = "claude auth login";
19905
19905
  const credentialsDirPath = resolveClaudeCredentialsDirPath(credentialsPath);
19906
19906
  if (credentialsDirPath === null) return command;
19907
- return withClaudeCredentialsDir({ command, credentialsDirPath, platform });
19907
+ return withClaudeCredentialsDir({ command, credentialsDirPath, platform: platform2 });
19908
19908
  }
19909
19909
 
19910
19910
  // src/claudeSkills.ts
19911
19911
  import * as NodeFSP from "node:fs/promises";
19912
19912
  import * as NodePath5 from "node:path";
19913
- var FRONT_MATTER_KEY = /^(name|description):[ \t]*(.*)$/;
19913
+ import * as NodeProcess from "node:process";
19914
+ var FRONT_MATTER_KEY = /^(description|disable-model-invocation|user-invocable):[ \t]*(.*)$/;
19914
19915
  var BLOCK_SCALAR = /^[|>][+-]?$/;
19916
+ function parseFrontMatterBoolean(value) {
19917
+ switch (value?.trim().toLowerCase()) {
19918
+ case "true":
19919
+ case "yes":
19920
+ case "on":
19921
+ case "y":
19922
+ case "1":
19923
+ return true;
19924
+ case "false":
19925
+ case "no":
19926
+ case "off":
19927
+ case "n":
19928
+ case "0":
19929
+ return false;
19930
+ default:
19931
+ return void 0;
19932
+ }
19933
+ }
19915
19934
  function parseSkillFrontMatter(contents) {
19916
19935
  const lines = contents.split(/\r?\n/);
19917
19936
  if (lines[0]?.trim() !== "---") return {};
@@ -19933,30 +19952,156 @@ function parseSkillFrontMatter(contents) {
19933
19952
  }
19934
19953
  value = block.join(value.startsWith("|") ? "\n" : " ");
19935
19954
  } else {
19955
+ if (key !== "description") value = value.replace(/\s+#.*$/, "");
19936
19956
  value = value.replace(/^(["'])(.*)\1$/, "$2").trim();
19937
19957
  }
19938
19958
  if (value.length > 0 && values[key] === void 0) values[key] = value;
19939
19959
  }
19940
- return values;
19960
+ const description = values["description"];
19961
+ return {
19962
+ ...description === void 0 ? {} : { description },
19963
+ ...parseFrontMatterBoolean(values["disable-model-invocation"]) === true ? { userInvocationOnly: true } : {},
19964
+ ...parseFrontMatterBoolean(values["user-invocable"]) === false ? { userInvocable: false } : {}
19965
+ };
19941
19966
  }
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);
19967
+ function claudeManagedSettingsPath(platform2, environment) {
19968
+ if (platform2 === "darwin") {
19969
+ return "/Library/Application Support/ClaudeCode/managed-settings.json";
19970
+ }
19971
+ if (platform2 === "win32") {
19972
+ const programData = environment["PROGRAMDATA"]?.trim();
19973
+ return programData ? NodePath5.join(programData, "ClaudeCode", "managed-settings.json") : void 0;
19974
+ }
19975
+ return "/etc/claude-code/managed-settings.json";
19976
+ }
19977
+ function skillOverrideSettingsPaths(configDirPath, cwd, platform2, environment, repositoryRoot) {
19978
+ const managedPath = claudeManagedSettingsPath(platform2, environment);
19979
+ const root = repositoryRoot !== void 0 && repositoryRoot !== cwd ? repositoryRoot : void 0;
19980
+ return [
19981
+ NodePath5.join(configDirPath, "settings.json"),
19982
+ ...cwd ? [
19983
+ NodePath5.join(cwd, ".claude", "settings.json"),
19984
+ NodePath5.join(cwd, ".claude", "settings.local.json")
19985
+ ] : [],
19986
+ ...root ? [NodePath5.join(root, ".claude", "settings.local.json")] : [],
19987
+ ...managedPath ? [managedPath] : []
19988
+ ];
19989
+ }
19990
+ async function findRepositoryRoot(cwd) {
19991
+ let current = NodePath5.resolve(cwd);
19992
+ for (; ; ) {
19993
+ const found = await NodeFSP.stat(NodePath5.join(current, ".git")).then(
19994
+ () => true,
19995
+ () => false
19996
+ );
19997
+ if (found) return current;
19998
+ const parent = NodePath5.dirname(current);
19999
+ if (parent === current) return void 0;
20000
+ current = parent;
20001
+ }
20002
+ }
20003
+ function parseLenientJson(text) {
20004
+ let output = "";
20005
+ let index = 0;
20006
+ while (index < text.length) {
20007
+ const char = text[index];
20008
+ const next = text[index + 1];
20009
+ if (char === '"') {
20010
+ let end = index + 1;
20011
+ while (end < text.length && text[end] !== '"') end += text[end] === "\\" ? 2 : 1;
20012
+ output += text.slice(index, end + 1);
20013
+ index = end + 1;
20014
+ } else if (char === "/" && next === "/") {
20015
+ while (index < text.length && text[index] !== "\n") index += 1;
20016
+ } else if (char === "/" && next === "*") {
20017
+ const end = text.indexOf("*/", index + 2);
20018
+ index = end === -1 ? text.length : end + 2;
20019
+ } else {
20020
+ output += char;
20021
+ index += 1;
20022
+ }
20023
+ }
20024
+ return JSON.parse(output.replace(/,(\s*[}\]])/g, "$1"));
20025
+ }
20026
+ var SKILL_OVERRIDE_BY_VALUE = {
20027
+ off: { enabled: false, userInvocationOnly: false },
20028
+ // 与 `disable-model-invocation` 一样对 agent 隐藏,所以与普通开关分开记。
20029
+ "user-invocable-only": { enabled: true, userInvocationOnly: true },
20030
+ on: { enabled: true, userInvocationOnly: false },
20031
+ "name-only": { enabled: true, userInvocationOnly: false }
20032
+ };
20033
+ function readOverridesFromSettings(text) {
20034
+ let parsed;
20035
+ try {
20036
+ parsed = parseLenientJson(text);
20037
+ } catch {
20038
+ return void 0;
20039
+ }
20040
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
20041
+ const overrides = parsed["skillOverrides"];
20042
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
20043
+ return void 0;
20044
+ }
20045
+ const result = /* @__PURE__ */ new Map();
20046
+ for (const [name, value] of Object.entries(overrides)) {
20047
+ const override = typeof value === "string" ? SKILL_OVERRIDE_BY_VALUE[value] : void 0;
20048
+ if (override === void 0) return void 0;
20049
+ result.set(name, override);
20050
+ }
20051
+ return result;
20052
+ }
20053
+ async function readSkillOverrides(configDirPath, cwd, platform2, environment) {
20054
+ const repositoryRoot = cwd === void 0 ? void 0 : await findRepositoryRoot(cwd);
20055
+ const merged = /* @__PURE__ */ new Map();
20056
+ for (const settingsPath of skillOverrideSettingsPaths(
20057
+ configDirPath,
20058
+ cwd,
20059
+ platform2,
20060
+ environment,
20061
+ repositoryRoot
20062
+ )) {
20063
+ const contents = await NodeFSP.readFile(settingsPath, "utf8").catch(() => void 0);
19949
20064
  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
- });
20065
+ for (const [name, override] of readOverridesFromSettings(contents) ?? []) {
20066
+ merged.set(name, override);
20067
+ }
20068
+ }
20069
+ return merged;
20070
+ }
20071
+ async function discoverClaudeSkills(configDirPath, options = {}) {
20072
+ const { cwd } = options;
20073
+ const overrides = await readSkillOverrides(
20074
+ configDirPath,
20075
+ cwd,
20076
+ options.platform ?? NodeProcess.platform,
20077
+ options.environment ?? NodeProcess.env
20078
+ );
20079
+ const roots = [
20080
+ { directory: NodePath5.join(configDirPath, "skills"), scope: "user" },
20081
+ ...cwd ? [{ directory: NodePath5.join(cwd, ".claude", "skills"), scope: "project" }] : []
20082
+ ];
20083
+ const skills = /* @__PURE__ */ new Map();
20084
+ for (const root of roots) {
20085
+ const entries = await NodeFSP.readdir(root.directory).catch(() => []);
20086
+ for (const entry of entries.toSorted()) {
20087
+ const name = entry.trim();
20088
+ if (name.length === 0 || skills.has(name)) continue;
20089
+ const skillPath = NodePath5.join(root.directory, entry, "SKILL.md");
20090
+ const contents = await NodeFSP.readFile(skillPath, "utf8").catch(() => void 0);
20091
+ if (contents === void 0) continue;
20092
+ const frontMatter = parseSkillFrontMatter(contents);
20093
+ const override = overrides.get(name);
20094
+ const userInvocationOnly = frontMatter.userInvocationOnly === true || override?.userInvocationOnly === true;
20095
+ skills.set(name, {
20096
+ name,
20097
+ path: skillPath,
20098
+ enabled: override?.enabled ?? true,
20099
+ scope: root.scope,
20100
+ ...frontMatter.description === void 0 ? {} : { description: frontMatter.description },
20101
+ ...userInvocationOnly ? { userInvocationOnly: true } : {},
20102
+ ...frontMatter.userInvocable === false ? { userInvocable: false } : {}
20103
+ });
20104
+ }
19960
20105
  }
19961
20106
  return [...skills.values()].toSorted((left, right) => left.name.localeCompare(right.name));
19962
20107
  }
@@ -19974,14 +20119,14 @@ function readTrimmed(value) {
19974
20119
  }
19975
20120
  function readEnvironment(value) {
19976
20121
  if (!Array.isArray(value)) return {};
19977
- const env = {};
20122
+ const env2 = {};
19978
20123
  for (const entry of value) {
19979
20124
  if (!isRecord2(entry)) continue;
19980
20125
  const name = readTrimmed(entry["name"]);
19981
20126
  if (name.length === 0) continue;
19982
- env[name] = readString(entry["value"]);
20127
+ env2[name] = readString(entry["value"]);
19983
20128
  }
19984
- return env;
20129
+ return env2;
19985
20130
  }
19986
20131
  function readCustomModels(value) {
19987
20132
  if (!Array.isArray(value)) return [];
@@ -20167,8 +20312,8 @@ var EventBus = class {
20167
20312
  const queued = this.#queued.shift();
20168
20313
  if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
20169
20314
  if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
20170
- return new Promise((resolve3) => {
20171
- this.#waiters.push(resolve3);
20315
+ return new Promise((resolve4) => {
20316
+ this.#waiters.push(resolve4);
20172
20317
  });
20173
20318
  }
20174
20319
  };
@@ -20355,9 +20500,9 @@ var claudeCatalogFromSdk = (models) => {
20355
20500
  };
20356
20501
  var silentPrompt = (signal) => ({
20357
20502
  [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 }), {
20503
+ next: () => new Promise((resolve4) => {
20504
+ if (signal.aborted) resolve4({ done: true, value: void 0 });
20505
+ signal.addEventListener("abort", () => resolve4({ done: true, value: void 0 }), {
20361
20506
  once: true
20362
20507
  });
20363
20508
  }),
@@ -20587,10 +20732,10 @@ function inputDiffStat(input) {
20587
20732
  let deletions = 0;
20588
20733
  for (const edit of input.edits) {
20589
20734
  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;
20735
+ const stat2 = replacementDiffStat(edit, budget);
20736
+ if (!stat2) return void 0;
20737
+ additions += stat2.additions;
20738
+ deletions += stat2.deletions;
20594
20739
  }
20595
20740
  return { additions, deletions };
20596
20741
  }
@@ -21223,7 +21368,7 @@ function claudeReadAllowRules(directories) {
21223
21368
  import * as NodeFS3 from "node:fs";
21224
21369
  import * as NodeOS6 from "node:os";
21225
21370
  import * as NodePath6 from "node:path";
21226
- import * as NodeProcess from "node:process";
21371
+ import * as NodeProcess2 from "node:process";
21227
21372
 
21228
21373
  // ../../packages/ycodium-jsonl-stream/src/jsonlStream.ts
21229
21374
  var DEFAULT_MAX_RETAINED_CHARS = 8e6;
@@ -21859,17 +22004,17 @@ function listTranscriptFiles(transcriptDir, directory) {
21859
22004
  continue;
21860
22005
  }
21861
22006
  for (const name of names) {
21862
- let stat;
22007
+ let stat2;
21863
22008
  try {
21864
- stat = NodeFS3.statSync(NodePath6.join(projectDir, name));
22009
+ stat2 = NodeFS3.statSync(NodePath6.join(projectDir, name));
21865
22010
  } catch {
21866
22011
  continue;
21867
22012
  }
21868
22013
  found.push({
21869
22014
  path: NodePath6.join(projectDir, name),
21870
22015
  sessionId: name.slice(0, -TRANSCRIPT_SUFFIX.length),
21871
- size: stat.size,
21872
- mtimeMs: Number.isFinite(stat.mtimeMs) ? stat.mtimeMs : 0
22016
+ size: stat2.size,
22017
+ mtimeMs: Number.isFinite(stat2.mtimeMs) ? stat2.mtimeMs : 0
21873
22018
  });
21874
22019
  }
21875
22020
  }
@@ -21895,7 +22040,7 @@ function readClaudeLiveSessionIds(configDir) {
21895
22040
  const typed = marker;
21896
22041
  if (typeof typed.pid !== "number" || typeof typed.sessionId !== "string") continue;
21897
22042
  try {
21898
- NodeProcess.kill(typed.pid, 0);
22043
+ NodeProcess2.kill(typed.pid, 0);
21899
22044
  } catch {
21900
22045
  continue;
21901
22046
  }
@@ -23103,6 +23248,30 @@ function createClaudeTextGeneration(recipe, host, modelCatalog) {
23103
23248
  };
23104
23249
  }
23105
23250
 
23251
+ // src/claudeSkillDispatch.ts
23252
+ var SKILL_MENTION_PATTERN = /(^|\s)\$([a-zA-Z][a-zA-Z0-9:_-]*)(?=\s|$)/g;
23253
+ function planClaudeSkillDispatch(prompt, skillNames) {
23254
+ const mentions = [...prompt.matchAll(SKILL_MENTION_PATTERN)].flatMap((match) => {
23255
+ const name = match[2] ?? "";
23256
+ if (!skillNames.has(name)) return [];
23257
+ const start = (match.index ?? 0) + (match[1]?.length ?? 0);
23258
+ return [{ name, start, end: start + name.length + 1 }];
23259
+ });
23260
+ const last = mentions.at(-1);
23261
+ if (last === void 0) return void 0;
23262
+ const leading = prompt.slice(0, last.start);
23263
+ const trailing = prompt.slice(last.end);
23264
+ const leadingWithInlineSlashes = mentions.slice(0, -1).reduceRight(
23265
+ (text, mention) => `${text.slice(0, mention.start)}/${text.slice(mention.start + 1)}`,
23266
+ leading
23267
+ ).trimEnd();
23268
+ return {
23269
+ leadingText: leadingWithInlineSlashes.length > 0 ? leadingWithInlineSlashes : void 0,
23270
+ commandText: `/${last.name}${trailing}`.trimEnd(),
23271
+ skillName: last.name
23272
+ };
23273
+ }
23274
+
23106
23275
  // src/imageMime.ts
23107
23276
  var IMAGE_EXTENSION_BY_MIME_TYPE = {
23108
23277
  "image/avif": ".avif",
@@ -23183,9 +23352,9 @@ var PromptQueue = class {
23183
23352
  const queued = this.#queued.shift();
23184
23353
  if (queued !== void 0) return Promise.resolve(queued);
23185
23354
  if (this.#closed) return Promise.resolve(void 0);
23186
- return new Promise((resolve3) => {
23355
+ return new Promise((resolve4) => {
23187
23356
  this.#waiters.push((result) => {
23188
- resolve3(result.done ? void 0 : result.value);
23357
+ resolve4(result.done ? void 0 : result.value);
23189
23358
  });
23190
23359
  });
23191
23360
  }
@@ -23266,6 +23435,10 @@ function nativeProviderRefs(_context, options) {
23266
23435
  }
23267
23436
  async function buildUserMessageFromTurn(input) {
23268
23437
  const sdkContent = [];
23438
+ const dispatch = input.skillNames === void 0 ? void 0 : planClaudeSkillDispatch(input.text, input.skillNames);
23439
+ if (dispatch?.leadingText !== void 0) {
23440
+ sdkContent.push({ type: "text", text: dispatch.leadingText });
23441
+ }
23269
23442
  for (const attachment of input.attachments ?? []) {
23270
23443
  if (!attachment || typeof attachment !== "object" || Array.isArray(attachment)) continue;
23271
23444
  const rec = attachment;
@@ -23284,7 +23457,9 @@ async function buildUserMessageFromTurn(input) {
23284
23457
  const bytes = await input.attachmentsApi.read(id2);
23285
23458
  sdkContent.push(buildClaudeImageContentBlock({ mimeType, bytes }));
23286
23459
  }
23287
- if (input.text.length > 0) {
23460
+ if (dispatch !== void 0) {
23461
+ sdkContent.push({ type: "text", text: dispatch.commandText });
23462
+ } else if (input.text.length > 0) {
23288
23463
  sdkContent.push({ type: "text", text: input.text });
23289
23464
  }
23290
23465
  return buildUserMessage({ sdkContent });
@@ -23304,10 +23479,10 @@ async function persistProducedImage(input) {
23304
23479
  }
23305
23480
 
23306
23481
  // src/claudeInstance.ts
23307
- function compactEnv(env) {
23482
+ function compactEnv(env2) {
23308
23483
  const out = {};
23309
- if (!env) return out;
23310
- for (const [key, value] of Object.entries(env)) {
23484
+ if (!env2) return out;
23485
+ for (const [key, value] of Object.entries(env2)) {
23311
23486
  if (value !== void 0) out[key] = value;
23312
23487
  }
23313
23488
  return out;
@@ -25071,7 +25246,7 @@ function createClaudeAdapter(recipe, host, options) {
25071
25246
  if (streamFiber) {
25072
25247
  await Promise.race([
25073
25248
  streamFiber.catch(() => void 0),
25074
- new Promise((resolve3) => setTimeout(resolve3, 1e3))
25249
+ new Promise((resolve4) => setTimeout(resolve4, 1e3))
25075
25250
  ]);
25076
25251
  }
25077
25252
  const updatedAt = nowIso();
@@ -25282,7 +25457,7 @@ function createClaudeAdapter(recipe, host, options) {
25282
25457
  return await Promise.race([
25283
25458
  contextDeferred.promise,
25284
25459
  new Promise(
25285
- (resolve3) => setTimeout(() => resolve3(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
25460
+ (resolve4) => setTimeout(() => resolve4(void 0), CLAUDE_SESSION_CONTEXT_WAIT_MS)
25286
25461
  )
25287
25462
  ]);
25288
25463
  } catch {
@@ -25839,10 +26014,20 @@ function createClaudeAdapter(recipe, host, options) {
25839
26014
  providerRefs: {}
25840
26015
  });
25841
26016
  }
26017
+ const cliEnvironment = { ...process.env, ...recipe.environment };
26018
+ const skills = await discoverClaudeSkills(
26019
+ resolveClaudeConfigDirPath(recipe.homePath, cliEnvironment, context.session.cwd),
26020
+ { environment: cliEnvironment, ...context.session.cwd ? { cwd: context.session.cwd } : {} }
26021
+ );
25842
26022
  const message = await buildUserMessageFromTurn({
25843
26023
  text: buildPromptText(turnCapsForPrompt, input, boundInstanceId),
25844
26024
  attachments: input.attachments,
25845
- attachmentsApi: options?.attachments
26025
+ attachmentsApi: options?.attachments,
26026
+ skillNames: new Set(
26027
+ skills.filter(
26028
+ (skill) => skill.scope === "user" && skill.enabled && skill.userInvocable !== false
26029
+ ).map((skill) => skill.name)
26030
+ )
25846
26031
  });
25847
26032
  validateSteeringTarget();
25848
26033
  context.promptQueue.push({
@@ -25870,7 +26055,7 @@ function createClaudeAdapter(recipe, host, options) {
25870
26055
  context.query.interrupt().then(() => settled.promise).then(() => {
25871
26056
  acknowledged = true;
25872
26057
  }),
25873
- new Promise((resolve3) => setTimeout(resolve3, CLAUDE_INTERRUPT_GRACE_MS))
26058
+ new Promise((resolve4) => setTimeout(resolve4, CLAUDE_INTERRUPT_GRACE_MS))
25874
26059
  ]);
25875
26060
  } catch (cause) {
25876
26061
  throw toRequestError(threadId, "turn/interrupt", cause);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-claude",
3
- "version": "0.2.2672",
3
+ "version": "0.2.2690",
4
4
  "description": "Claude executor: Claude Code CLI via @anthropic-ai/claude-agent-sdk.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -25,7 +25,7 @@
25
25
  "esbuild": "0.28.1",
26
26
  "vite-plus": "0.2.2",
27
27
  "ycodium-executor-testing": "0.0.0",
28
- "@ycodium-ai/plugin-api": "0.2.2672",
28
+ "@ycodium-ai/plugin-api": "0.2.2690",
29
29
  "ycodium-jsonl-stream": "0.0.0"
30
30
  },
31
31
  "ycodium": {