@hcmai/cli 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -523,8 +523,8 @@ var init_TerminalRenderer = __esm({
523
523
  }
524
524
  }
525
525
  /** 来源行(仅子代理触发,agentPath ≥2 段时显示 `↑ 来自 a › b`,对齐 Web)。 */
526
- originLine(path4) {
527
- const segs = path4.split(".").filter(Boolean);
526
+ originLine(path7) {
527
+ const segs = path7.split(".").filter(Boolean);
528
528
  if (segs.length < 2) return "";
529
529
  const names = segs.map((k) => agentDisplayName(k));
530
530
  return chalk.gray(` \u2191 \u6765\u81EA ${names.join(" \u203A ")}`) + "\n";
@@ -1458,10 +1458,10 @@ function ReplApp(props) {
1458
1458
  });
1459
1459
  const t0 = Date.now();
1460
1460
  try {
1461
- const result = await presenter.consume(handle.events);
1461
+ const result2 = await presenter.consume(handle.events);
1462
1462
  const stats = presenter.completionStats;
1463
- if (result.status === "completed") {
1464
- bridge.showCompleted(Date.now() - t0, result.traceId, convRef.current ?? "", presenter.tokenCount || void 0, stats);
1463
+ if (result2.status === "completed") {
1464
+ bridge.showCompleted(Date.now() - t0, result2.traceId, convRef.current ?? "", presenter.tokenCount || void 0, stats);
1465
1465
  }
1466
1466
  if (presenter.tokenCount) setSessionTokens((prev) => prev + presenter.tokenCount);
1467
1467
  if (convRef.current) {
@@ -1706,8 +1706,8 @@ function agentLabel(agentId) {
1706
1706
  if (/^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(agentId)) return `agent \u2026${agentId.slice(-4)}`;
1707
1707
  return agentDisplayName(agentId);
1708
1708
  }
1709
- function originLine(path4) {
1710
- const segs = (path4 ?? "").split(".").filter(Boolean);
1709
+ function originLine(path7) {
1710
+ const segs = (path7 ?? "").split(".").filter(Boolean);
1711
1711
  if (segs.length < 2) return null;
1712
1712
  return `\u6765\u81EA ${segs.map((k) => agentDisplayName(k)).join(" \u203A ")}`;
1713
1713
  }
@@ -2639,12 +2639,12 @@ async function queryCommand(model, args) {
2639
2639
  if (args.offset) dsl.offset = Number(args.offset);
2640
2640
  if (!dsl.limit) dsl.limit = 20;
2641
2641
  const client3 = HcmClient.fromAuthContext(await getActiveAuthContextFromCli(args));
2642
- const result = await client3.query(model, dsl);
2642
+ const result2 = await client3.query(model, dsl);
2643
2643
  const fmt = args.output ?? "table";
2644
- const out = formatRows(result.rows, { format: fmt });
2644
+ const out = formatRows(result2.rows, { format: fmt });
2645
2645
  process.stdout.write(out + "\n");
2646
- if (fmt === "table" && result.total !== void 0) {
2647
- process.stdout.write(`Showing ${result.rows.length} of ${result.total} results.
2646
+ if (fmt === "table" && result2.total !== void 0) {
2647
+ process.stdout.write(`Showing ${result2.rows.length} of ${result2.total} results.
2648
2648
  `);
2649
2649
  }
2650
2650
  process.exit(0);
@@ -2709,17 +2709,17 @@ async function actionCommand(spec, args) {
2709
2709
  }
2710
2710
  }
2711
2711
  const client3 = HcmClient2.fromAuthContext(await getActiveAuthContextFromCli(args));
2712
- const result = await client3.action(model, action, input);
2712
+ const result2 = await client3.action(model, action, input);
2713
2713
  const fmt = args.output ?? "json";
2714
- if (fmt !== "json" && result.taskId) {
2714
+ if (fmt !== "json" && result2.taskId) {
2715
2715
  process.stdout.write(
2716
- `\u23F3 task created: ${result.taskId} (use \`hcm task ${result.taskId}\` to follow up \u2014 coming Phase 1)
2716
+ `\u23F3 task created: ${result2.taskId} (use \`hcm task ${result2.taskId}\` to follow up \u2014 coming Phase 1)
2717
2717
  `
2718
2718
  );
2719
2719
  } else if (fmt !== "json") {
2720
2720
  process.stdout.write("\u2705 action succeeded\n");
2721
2721
  }
2722
- process.stdout.write(formatObject(result, { format: fmt }) + "\n");
2722
+ process.stdout.write(formatObject(result2, { format: fmt }) + "\n");
2723
2723
  process.exit(0);
2724
2724
  } catch (e) {
2725
2725
  const err = e;
@@ -2808,11 +2808,11 @@ async function updateCommand(model, args) {
2808
2808
  });
2809
2809
  }
2810
2810
  const client3 = HcmClient4.fromAuthContext(await getActiveAuthContextFromCli(args));
2811
- const result = await client3.update(model, args.id, data);
2811
+ const result2 = await client3.update(model, args.id, data);
2812
2812
  const fmt = args.output ?? "json";
2813
2813
  if (fmt !== "json") process.stdout.write(`\u2705 ${model}#${args.id} \u5DF2\u66F4\u65B0
2814
2814
  `);
2815
- process.stdout.write(formatObject3(result, { format: fmt }) + "\n");
2815
+ process.stdout.write(formatObject3(result2, { format: fmt }) + "\n");
2816
2816
  process.exit(0);
2817
2817
  } catch (e) {
2818
2818
  const err = e;
@@ -2893,11 +2893,11 @@ async function settingSetCommand(domain, args) {
2893
2893
  process.exit(0);
2894
2894
  }
2895
2895
  const c = await client(args);
2896
- const result = await patchSettingDomain(c.raw(), domain, items);
2896
+ const result2 = await patchSettingDomain(c.raw(), domain, items);
2897
2897
  const fmt = args.output ?? "json";
2898
2898
  if (fmt !== "json") process.stdout.write(`\u2705 ${domain} \u5DF2\u66F4\u65B0 ${items.length} \u9879
2899
2899
  `);
2900
- process.stdout.write(formatObject4(result, { format: fmt }) + "\n");
2900
+ process.stdout.write(formatObject4(result2, { format: fmt }) + "\n");
2901
2901
  process.exit(0);
2902
2902
  } catch (e) {
2903
2903
  fail(e, args);
@@ -2914,11 +2914,11 @@ async function settingResetCommand(domain, namespace, key, args) {
2914
2914
  }
2915
2915
  const c = await client(args);
2916
2916
  const revision = args.revision === void 0 ? void 0 : Number(args.revision);
2917
- const result = await resetSettingItem(c.raw(), domain, namespace, key, revision);
2917
+ const result2 = await resetSettingItem(c.raw(), domain, namespace, key, revision);
2918
2918
  const fmt = args.output ?? "json";
2919
2919
  if (fmt !== "json") process.stdout.write(`\u2705 ${namespace}.${key} \u5DF2\u56DE\u843D\u5230\u9ED8\u8BA4\u503C
2920
2920
  `);
2921
- process.stdout.write(formatObject4(result, { format: fmt }) + "\n");
2921
+ process.stdout.write(formatObject4(result2, { format: fmt }) + "\n");
2922
2922
  process.exit(0);
2923
2923
  } catch (e) {
2924
2924
  fail(e, args);
@@ -3011,8 +3011,8 @@ import {
3011
3011
  formatObject as formatObject7,
3012
3012
  fromAxiosError as fromAxiosError8
3013
3013
  } from "@hcmai/sdk";
3014
- function collectYmlFiles(path4) {
3015
- if (statSync(path4).isFile()) return [path4];
3014
+ function collectYmlFiles(path7) {
3015
+ if (statSync(path7).isFile()) return [path7];
3016
3016
  const out = [];
3017
3017
  const walk = (dir) => {
3018
3018
  for (const name of readdirSync(dir).sort()) {
@@ -3021,7 +3021,7 @@ function collectYmlFiles(path4) {
3021
3021
  else if (full.endsWith(".yml") || full.endsWith(".yaml")) out.push(full);
3022
3022
  }
3023
3023
  };
3024
- walk(path4);
3024
+ walk(path7);
3025
3025
  return out.sort();
3026
3026
  }
3027
3027
  var DRY_RUN_CLIENT = {
@@ -3029,9 +3029,9 @@ var DRY_RUN_CLIENT = {
3029
3029
  create: async () => ({ id: "" }),
3030
3030
  update: async () => ({})
3031
3031
  };
3032
- async function importCommand(path4, args) {
3032
+ async function importCommand(path7, args) {
3033
3033
  try {
3034
- const files = collectYmlFiles(path4);
3034
+ const files = collectYmlFiles(path7);
3035
3035
  const fixtures = files.map(
3036
3036
  (f) => parseFixture(readFileSync2(f, "utf8"), relative(process.cwd(), f))
3037
3037
  );
@@ -3041,16 +3041,16 @@ async function importCommand(path4, args) {
3041
3041
  onError: args.onError === "stop" ? "stop" : "continue"
3042
3042
  };
3043
3043
  const client3 = opts.dryRun ? DRY_RUN_CLIENT : HcmClient7.fromAuthContext(await getActiveAuthContextFromCli(args));
3044
- const result = await runImport(client3, fixtures, opts);
3045
- if (args.report) writeFileSync(args.report, JSON.stringify(result, null, 2));
3044
+ const result2 = await runImport(client3, fixtures, opts);
3045
+ if (args.report) writeFileSync(args.report, JSON.stringify(result2, null, 2));
3046
3046
  const fmt = args.output ?? "json";
3047
3047
  const tag = opts.dryRun ? "\u{1F50D} dry-run" : "\u{1F4E6} import";
3048
3048
  process.stdout.write(
3049
- `${tag}: created=${result.created} updated=${result.updated} skipped=${result.skipped} failed=${result.failed}
3049
+ `${tag}: created=${result2.created} updated=${result2.updated} skipped=${result2.skipped} failed=${result2.failed}
3050
3050
  `
3051
3051
  );
3052
- process.stdout.write(formatObject7(result, { format: fmt }) + "\n");
3053
- process.exit(result.failed > 0 ? 3 : 0);
3052
+ process.stdout.write(formatObject7(result2, { format: fmt }) + "\n");
3053
+ process.exit(result2.failed > 0 ? 3 : 0);
3054
3054
  } catch (e) {
3055
3055
  const err = e;
3056
3056
  if (err?.isAxiosError) exitWithError(fromAxiosError8(err, args.profile));
@@ -3333,13 +3333,13 @@ async function printConversationList(args) {
3333
3333
  const ctx = await getActiveAuthContextFromCli(args);
3334
3334
  const client3 = HcmClient10.fromAuthContext(ctx);
3335
3335
  const limit = parsePositiveInt(args.limit, 10, "--limit");
3336
- const result = await fetchRecentConversations(client3.raw(), limit);
3336
+ const result2 = await fetchRecentConversations(client3.raw(), limit);
3337
3337
  const fmt = args.output ?? "text";
3338
3338
  if (fmt === "json") {
3339
- process.stdout.write(formatObject9(result, { format: "json" }) + "\n");
3339
+ process.stdout.write(formatObject9(result2, { format: "json" }) + "\n");
3340
3340
  return;
3341
3341
  }
3342
- const rows = result.rows.map((row) => ({
3342
+ const rows = result2.rows.map((row) => ({
3343
3343
  UPDATED: formatShortDate(row.lastActiveAt ?? row.updateTime ?? row.createTime),
3344
3344
  MSGS: row.messageCount ?? 0,
3345
3345
  TOKENS: row.totalTokens ?? 0,
@@ -3347,8 +3347,8 @@ async function printConversationList(args) {
3347
3347
  CONVERSATION: row.id ?? "-"
3348
3348
  }));
3349
3349
  process.stdout.write(formatRows3(rows, { format: "table" }) + "\n");
3350
- if (result.total !== void 0) {
3351
- process.stdout.write(`Showing ${result.rows.length} of ${result.total} conversations.
3350
+ if (result2.total !== void 0) {
3351
+ process.stdout.write(`Showing ${result2.rows.length} of ${result2.total} conversations.
3352
3352
  `);
3353
3353
  }
3354
3354
  }
@@ -3620,9 +3620,9 @@ async function chatCommand(prompt, args) {
3620
3620
  process.once("SIGINT", sigintHandler);
3621
3621
  const t0 = Date.now();
3622
3622
  try {
3623
- const result = await presenter.consume(handle.events);
3623
+ const result2 = await presenter.consume(handle.events);
3624
3624
  const stats = presenter.completionStats;
3625
- renderer.showCompleted(Date.now() - t0, result.traceId, handle.conversationId, presenter.tokenCount || void 0, {
3625
+ renderer.showCompleted(Date.now() - t0, result2.traceId, handle.conversationId, presenter.tokenCount || void 0, {
3626
3626
  inputTokens: stats.inputTokens,
3627
3627
  outputTokens: stats.outputTokens,
3628
3628
  toolCount: stats.toolCount,
@@ -3631,8 +3631,8 @@ async function chatCommand(prompt, args) {
3631
3631
  await ws.close();
3632
3632
  await saveConversationState2(stateKey, {
3633
3633
  lastConversationId: handle.conversationId,
3634
- lastStreamId: result.streamId,
3635
- lastTraceId: result.traceId ?? void 0,
3634
+ lastStreamId: result2.streamId,
3635
+ lastTraceId: result2.traceId ?? void 0,
3636
3636
  lastAgentKey: args.agent,
3637
3637
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3638
3638
  });
@@ -3644,7 +3644,7 @@ async function chatCommand(prompt, args) {
3644
3644
  resolve3();
3645
3645
  }
3646
3646
  });
3647
- process.exit(result.status === "interrupted" ? 130 : 0);
3647
+ process.exit(result2.status === "interrupted" ? 130 : 0);
3648
3648
  } catch (e) {
3649
3649
  process.removeListener("SIGINT", sigintHandler);
3650
3650
  await ws.close();
@@ -4136,10 +4136,10 @@ async function withClient(args, fn) {
4136
4136
  const client3 = HcmClient13.fromAuthContext(await getActiveAuthContextFromCli(args));
4137
4137
  return fn(client3);
4138
4138
  }
4139
- function emit(result, args, human) {
4139
+ function emit(result2, args, human) {
4140
4140
  const fmt = args.output ?? "json";
4141
4141
  if (fmt !== "json") process.stdout.write(human + "\n");
4142
- process.stdout.write(formatObject11(result, { format: fmt }) + "\n");
4142
+ process.stdout.write(formatObject11(result2, { format: fmt }) + "\n");
4143
4143
  process.exit(0);
4144
4144
  }
4145
4145
  function fail2(e, args) {
@@ -4158,20 +4158,20 @@ async function cacheStatsCommand(model, args) {
4158
4158
  }
4159
4159
  async function cacheClearCommand(model, args) {
4160
4160
  try {
4161
- const result = await withClient(args, (c) => clearModelCache(c.raw(), model, args.id));
4162
- emit(result, args, args.id ? `\u{1F9F9} \u5DF2\u6E05 ${model}#${args.id} \u7F13\u5B58` : `\u{1F9F9} \u5DF2\u6E05 ${model} \u5168\u90E8\u5B9E\u4F53\u7F13\u5B58`);
4161
+ const result2 = await withClient(args, (c) => clearModelCache(c.raw(), model, args.id));
4162
+ emit(result2, args, args.id ? `\u{1F9F9} \u5DF2\u6E05 ${model}#${args.id} \u7F13\u5B58` : `\u{1F9F9} \u5DF2\u6E05 ${model} \u5168\u90E8\u5B9E\u4F53\u7F13\u5B58`);
4163
4163
  } catch (e) {
4164
4164
  fail2(e, args);
4165
4165
  }
4166
4166
  }
4167
4167
  async function cacheClearMetaCommand(model, args) {
4168
4168
  try {
4169
- const result = await withClient(
4169
+ const result2 = await withClient(
4170
4170
  args,
4171
4171
  (c) => clearMetaCache(c.raw(), model, { type: args.type, state: args.state })
4172
4172
  );
4173
4173
  const scope = args.type ? `${args.type}${args.state ? `.${args.state}` : ""}` : "\u5168\u90E8";
4174
- emit(result, args, `\u{1F9F9} \u5DF2\u6E05 ${model} \u5143\u6570\u636E\u7F13\u5B58\uFF08${scope}\uFF09`);
4174
+ emit(result2, args, `\u{1F9F9} \u5DF2\u6E05 ${model} \u5143\u6570\u636E\u7F13\u5B58\uFF08${scope}\uFF09`);
4175
4175
  } catch (e) {
4176
4176
  fail2(e, args);
4177
4177
  }
@@ -4224,20 +4224,21 @@ async function uploadCommand(filePath, args) {
4224
4224
  init_exit();
4225
4225
  import { promises as fsp } from "fs";
4226
4226
  import os2 from "os";
4227
- import path2 from "path";
4228
- import { createHash } from "crypto";
4227
+ import path4 from "path";
4228
+ import { createHash as createHash3 } from "crypto";
4229
4229
  import {
4230
4230
  createHttpClient as createHttpClient2,
4231
4231
  fetchSkillCatalog,
4232
+ fetchSkillFile,
4232
4233
  fetchSkillMarkdown,
4233
4234
  fetchSkillReference,
4234
4235
  resolveChironBase,
4235
- assertSafeSkillId,
4236
+ assertSafeSkillId as assertSafeSkillId2,
4236
4237
  assertSafeReferencePath,
4237
4238
  normalizeReferences,
4238
4239
  parseSkillRequirements,
4239
4240
  parseSkillFrontmatter,
4240
- resolveSkillInstallOrder,
4241
+ resolveSkillInstallOrder as resolveSkillInstallOrder2,
4241
4242
  resolveAllSkillsInstallOrder,
4242
4243
  flattenSkills,
4243
4244
  matchSkills,
@@ -4247,12 +4248,796 @@ import {
4247
4248
  loadEnv as loadEnv5,
4248
4249
  loadGlobalConfig as loadGlobalConfig6,
4249
4250
  resolveActiveEnv as resolveActiveEnv3,
4251
+ CliError as CliError16,
4252
+ CliErrorCode as CliErrorCode15
4253
+ } from "@hcmai/sdk";
4254
+
4255
+ // src/commands/skills-sync.ts
4256
+ import { createHash } from "crypto";
4257
+ import { promises as fs4 } from "fs";
4258
+ import path2 from "path";
4259
+ import {
4250
4260
  CliError as CliError14,
4251
- CliErrorCode as CliErrorCode13
4261
+ CliErrorCode as CliErrorCode13,
4262
+ assertSafeSkillFilePath,
4263
+ assertSafeSkillId,
4264
+ resolveSkillInstallOrder
4252
4265
  } from "@hcmai/sdk";
4266
+ async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fetchFile) {
4267
+ const order = resolveSkillInstallOrder(catalog, requestedSkill);
4268
+ for (const skill of order) {
4269
+ if (skill.actionSurface !== "cli") {
4270
+ throw invalid(`\u6280\u80FD ${skill.id} \u5C1A\u672A\u6536\u655B\u5230 hcm CLI\uFF0C\u4E0D\u80FD\u540C\u6B65\u4E3A WorkBuddy \u6B63\u5F0F\u6267\u884C Skill`);
4271
+ }
4272
+ }
4273
+ return Promise.all(
4274
+ order.map(async (skill) => {
4275
+ let files;
4276
+ if (skill.files === void 0) {
4277
+ const markdown2 = await fetchMarkdown(skill.id);
4278
+ const content = Buffer.from(markdown2, "utf8");
4279
+ files = [
4280
+ {
4281
+ path: "SKILL.md",
4282
+ content,
4283
+ bytes: content.length,
4284
+ sha256: sha256(content)
4285
+ }
4286
+ ];
4287
+ } else {
4288
+ if (!Array.isArray(skill.files) || skill.files.length === 0) {
4289
+ throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u76EE\u5F55\u4E3A\u7A7A`);
4290
+ }
4291
+ if (!fetchFile) throw invalid(`\u6280\u80FD ${skill.id} \u63D0\u4F9B\u5B8C\u6574\u76EE\u5F55\uFF0C\u4F46\u8C03\u7528\u65B9\u6CA1\u6709\u6587\u4EF6\u4E0B\u8F7D\u80FD\u529B`);
4292
+ const seen = /* @__PURE__ */ new Set();
4293
+ for (const file of skill.files) {
4294
+ assertSafeSkillFilePath(file.path);
4295
+ if (seen.has(file.path))
4296
+ throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u5305\u542B\u91CD\u590D\u8DEF\u5F84: ${file.path}`);
4297
+ seen.add(file.path);
4298
+ if (!Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
4299
+ throw invalid(`\u6280\u80FD ${skill.id} \u7684\u6587\u4EF6\u76EE\u5F55\u5143\u6570\u636E\u975E\u6CD5: ${file.path}`);
4300
+ }
4301
+ }
4302
+ if (!seen.has("SKILL.md")) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
4303
+ files = await Promise.all(
4304
+ skill.files.map(async (file) => {
4305
+ const content = Buffer.from(await fetchFile(skill.id, file.path));
4306
+ if (content.length !== file.bytes) {
4307
+ throw invalid(
4308
+ `\u6280\u80FD ${skill.id}/${file.path} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${file.bytes}\uFF0C\u5B9E\u9645=${content.length}`
4309
+ );
4310
+ }
4311
+ const digest = sha256(content);
4312
+ if (digest !== file.sha256) {
4313
+ throw invalid(`\u6280\u80FD ${skill.id}/${file.path} \u6458\u8981\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4`);
4314
+ }
4315
+ return { path: file.path, content, bytes: content.length, sha256: digest };
4316
+ })
4317
+ );
4318
+ }
4319
+ const root = files.find((file) => file.path === "SKILL.md");
4320
+ if (!root) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
4321
+ const markdown = root.content.toString("utf8");
4322
+ if (!markdown.trim()) throw invalid(`\u6280\u80FD ${skill.id} \u5185\u5BB9\u4E3A\u7A7A`);
4323
+ validateWorkBuddyFrontmatter(markdown, skill.id);
4324
+ const bytes = root.bytes;
4325
+ if (Number.isFinite(skill.bytes) && skill.bytes > 0 && skill.bytes !== bytes) {
4326
+ throw invalid(`\u6280\u80FD ${skill.id} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${skill.bytes}\uFF0C\u5B9E\u9645=${bytes}`);
4327
+ }
4328
+ return { id: skill.id, markdown, bytes, sha256: root.sha256, files };
4329
+ })
4330
+ );
4331
+ }
4332
+ async function syncWorkBuddyProject(input) {
4333
+ const project = path2.resolve(input.project);
4334
+ const metaDir = path2.join(project, ".hcm-delivery");
4335
+ const skillsDir = path2.join(project, ".codebuddy", "skills");
4336
+ const lockPath = path2.join(metaDir, "chiron.lock.json");
4337
+ assertSafeSkillId(input.requestedSkill);
4338
+ validatePreparedDocuments(input.documents, input.requestedSkill);
4339
+ validatePreparedRuntime(input.runtime);
4340
+ await recoverInterruptedSyncs(project, metaDir, lockPath);
4341
+ const oldLock = await readLock(lockPath);
4342
+ const skillEntries = input.documents.flatMap(
4343
+ (document) => document.files.map((file) => [ownedFile(document.id, file.path), file])
4344
+ );
4345
+ const runtimeEntries = input.runtime.files.map((file) => [file.path, file]);
4346
+ const desiredEntries = [
4347
+ ...skillEntries,
4348
+ ...runtimeEntries
4349
+ ];
4350
+ const desiredFiles = desiredEntries.map(([file]) => file);
4351
+ const desiredByFile = new Map(desiredEntries);
4352
+ const oldOwned = new Set(oldLock?.ownedFiles ?? []);
4353
+ const oldDigestByFile = lockDigests(oldLock);
4354
+ for (const file of oldOwned) validateOwnedFile(file);
4355
+ for (const file of desiredFiles) {
4356
+ const absolute = path2.join(project, file);
4357
+ const current = await readOptionalBytes(absolute);
4358
+ if (current !== void 0 && !oldOwned.has(file)) {
4359
+ throw invalid(`\u76EE\u6807\u6587\u4EF6\u4E0D\u5C5E\u4E8E\u4E0A\u4E00\u4EFD Chiron \u9501\uFF0C\u62D2\u7EDD\u8986\u76D6\u987E\u95EE\u6587\u4EF6\uFF1A${file}`);
4360
+ }
4361
+ }
4362
+ for (const file of oldOwned) {
4363
+ const absolute = path2.join(project, file);
4364
+ const current = await readOptionalBytes(absolute);
4365
+ if (current === void 0) continue;
4366
+ const expected = oldDigestByFile.get(file);
4367
+ if (!expected || sha256(current) !== expected.sha256 || !await fileModeMatches(absolute, expected.mode)) {
4368
+ throw invalid(`\u4E0A\u4E00\u4EFD\u9501\u62E5\u6709\u7684\u751F\u6210\u6587\u4EF6\u5DF2\u88AB\u4EBA\u5DE5\u4FEE\u6539\uFF0C\u62D2\u7EDD\u8986\u76D6\u6216\u6E05\u7406\uFF1A${file}`);
4369
+ }
4370
+ }
4371
+ const newLock = {
4372
+ schemaVersion: 1,
4373
+ source: {
4374
+ endpointOriginSha256: sha256(normalizeEndpoint(input.endpoint)),
4375
+ catalogPath: "/skills.json",
4376
+ catalogSha256: sha256(stableJson2(input.catalog))
4377
+ },
4378
+ selection: { requestedSkill: input.requestedSkill },
4379
+ resolvedSkills: input.documents.map(({ id, bytes, sha256: digest, files: skillFiles }) => ({
4380
+ id,
4381
+ bytes,
4382
+ sha256: digest,
4383
+ files: skillFiles.map(({ path: filePath, bytes: fileBytes, sha256: fileSha256 }) => ({
4384
+ path: filePath,
4385
+ bytes: fileBytes,
4386
+ sha256: fileSha256
4387
+ }))
4388
+ })),
4389
+ runtime: {
4390
+ capabilities: input.runtime.capabilities,
4391
+ capabilitiesSha256: input.runtime.capabilitiesSha256,
4392
+ launch: input.runtime.launch,
4393
+ files: input.runtime.files.map(({ path: filePath, bytes, sha256: digest, mode }) => ({
4394
+ path: filePath,
4395
+ bytes,
4396
+ sha256: digest,
4397
+ mode
4398
+ }))
4399
+ },
4400
+ ownedFiles: desiredFiles,
4401
+ syncedAt: input.now ?? (/* @__PURE__ */ new Date()).toISOString()
4402
+ };
4403
+ if (oldLock && sameLockContent(oldLock, newLock) && await desiredFilesMatch(project, desiredByFile)) {
4404
+ return result(false, input, project, skillsDir, lockPath, [], []);
4405
+ }
4406
+ const obsolete = [...oldOwned].filter((file) => !desiredByFile.has(file));
4407
+ await fs4.mkdir(metaDir, { recursive: true });
4408
+ const staging = await fs4.mkdtemp(path2.join(metaDir, ".staging-"));
4409
+ const stagedFiles = path2.join(staging, "new");
4410
+ const backups = path2.join(staging, "backup");
4411
+ const stagedLock = path2.join(staging, "chiron.lock.json");
4412
+ const journalPath = path2.join(staging, "journal.json");
4413
+ let journalReady = false;
4414
+ try {
4415
+ for (const [file, preparedFile2] of desiredByFile) {
4416
+ const staged = path2.join(stagedFiles, file);
4417
+ await fs4.mkdir(path2.dirname(staged), { recursive: true });
4418
+ await fs4.writeFile(
4419
+ staged,
4420
+ preparedFile2.content,
4421
+ preparedFile2.mode === void 0 ? void 0 : { mode: preparedFile2.mode }
4422
+ );
4423
+ }
4424
+ await fs4.writeFile(stagedLock, `${JSON.stringify(newLock, null, 2)}
4425
+ `, "utf8");
4426
+ const affected = [.../* @__PURE__ */ new Set([...desiredFiles, ...obsolete])];
4427
+ for (const file of affected) {
4428
+ const target = path2.join(project, file);
4429
+ if (!await exists(target)) continue;
4430
+ const backup = path2.join(backups, file);
4431
+ await fs4.mkdir(path2.dirname(backup), { recursive: true });
4432
+ await fs4.copyFile(target, backup);
4433
+ }
4434
+ const oldLockExisted = await exists(lockPath);
4435
+ if (oldLockExisted) {
4436
+ const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
4437
+ await fs4.mkdir(path2.dirname(backupLock), { recursive: true });
4438
+ await fs4.copyFile(lockPath, backupLock);
4439
+ }
4440
+ const journal = {
4441
+ schemaVersion: 1,
4442
+ project,
4443
+ affectedFiles: affected,
4444
+ oldLockExisted
4445
+ };
4446
+ await fs4.writeFile(journalPath, `${JSON.stringify(journal, null, 2)}
4447
+ `, "utf8");
4448
+ journalReady = true;
4449
+ for (const file of desiredFiles) {
4450
+ const target = path2.join(project, file);
4451
+ await fs4.mkdir(path2.dirname(target), { recursive: true });
4452
+ await fs4.rm(target, { force: true });
4453
+ await fs4.rename(path2.join(stagedFiles, file), target);
4454
+ }
4455
+ for (const file of obsolete) await fs4.unlink(path2.join(project, file)).catch(ignoreMissing);
4456
+ await fs4.rm(lockPath, { force: true });
4457
+ await fs4.rename(stagedLock, lockPath);
4458
+ await fs4.unlink(journalPath);
4459
+ journalReady = false;
4460
+ } catch (cause) {
4461
+ if (journalReady) await recoverStaging(project, staging, lockPath);
4462
+ throw cause;
4463
+ } finally {
4464
+ await fs4.rm(staging, { recursive: true, force: true });
4465
+ }
4466
+ return result(
4467
+ true,
4468
+ input,
4469
+ project,
4470
+ skillsDir,
4471
+ lockPath,
4472
+ input.documents.map((document) => document.id),
4473
+ [...new Set(obsolete.map(idFromOwnedFile).filter((id) => id !== void 0))]
4474
+ );
4475
+ }
4476
+ async function recoverInterruptedSyncs(project, metaDir, lockPath) {
4477
+ let entries;
4478
+ try {
4479
+ entries = await fs4.readdir(metaDir, { withFileTypes: true });
4480
+ } catch (cause) {
4481
+ if (cause.code === "ENOENT") return;
4482
+ throw cause;
4483
+ }
4484
+ for (const entry of entries) {
4485
+ if (!entry.isDirectory() || !entry.name.startsWith(".staging-")) continue;
4486
+ const staging = path2.join(metaDir, entry.name);
4487
+ if (await exists(path2.join(staging, "journal.json"))) {
4488
+ await recoverStaging(project, staging, lockPath);
4489
+ }
4490
+ await fs4.rm(staging, { recursive: true, force: true });
4491
+ }
4492
+ }
4493
+ async function recoverStaging(project, staging, lockPath) {
4494
+ const journalPath = path2.join(staging, "journal.json");
4495
+ const journal = parseJournal(await fs4.readFile(journalPath, "utf8"), project);
4496
+ const backups = path2.join(staging, "backup");
4497
+ for (const file of journal.affectedFiles) {
4498
+ const target = path2.join(project, file);
4499
+ const backup = path2.join(backups, file);
4500
+ if (await exists(backup)) {
4501
+ await fs4.mkdir(path2.dirname(target), { recursive: true });
4502
+ await fs4.rm(target, { force: true });
4503
+ await fs4.rename(backup, target);
4504
+ } else {
4505
+ await fs4.rm(target, { force: true });
4506
+ }
4507
+ }
4508
+ const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
4509
+ if (journal.oldLockExisted) {
4510
+ if (!await exists(backupLock)) throw invalid(`\u540C\u6B65\u6062\u590D\u7F3A\u5C11\u65E7\u9501\u5907\u4EFD\uFF1A${backupLock}`);
4511
+ await fs4.mkdir(path2.dirname(lockPath), { recursive: true });
4512
+ await fs4.rm(lockPath, { force: true });
4513
+ await fs4.rename(backupLock, lockPath);
4514
+ } else {
4515
+ await fs4.rm(lockPath, { force: true });
4516
+ }
4517
+ }
4518
+ function parseJournal(content, project) {
4519
+ let journal;
4520
+ try {
4521
+ journal = JSON.parse(content);
4522
+ } catch (cause) {
4523
+ throw new CliError14({
4524
+ code: CliErrorCode13.CONFIG_PARSE_ERROR,
4525
+ message: "Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u635F\u574F",
4526
+ cause
4527
+ });
4528
+ }
4529
+ if (journal.schemaVersion !== 1 || journal.project !== project || !Array.isArray(journal.affectedFiles) || typeof journal.oldLockExisted !== "boolean") {
4530
+ throw invalid("Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u5408\u540C\u4E0D\u5339\u914D\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6062\u590D\u8303\u56F4");
4531
+ }
4532
+ for (const file of journal.affectedFiles) validateOwnedFile(file);
4533
+ return journal;
4534
+ }
4535
+ function validatePreparedDocuments(documents, requestedSkill) {
4536
+ const seen = /* @__PURE__ */ new Set();
4537
+ for (const document of documents) {
4538
+ assertSafeSkillId(document.id);
4539
+ if (seen.has(document.id)) throw invalid(`\u540C\u6B65\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
4540
+ seen.add(document.id);
4541
+ if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
4542
+ const paths = /* @__PURE__ */ new Set();
4543
+ for (const file of document.files) {
4544
+ assertSafeSkillFilePath(file.path);
4545
+ if (paths.has(file.path)) throw invalid(`\u6280\u80FD ${document.id} \u5305\u542B\u91CD\u590D\u6587\u4EF6: ${file.path}`);
4546
+ paths.add(file.path);
4547
+ if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256) {
4548
+ throw invalid(`\u6280\u80FD ${document.id}/${file.path} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
4549
+ }
4550
+ }
4551
+ const root = document.files.find((file) => file.path === "SKILL.md");
4552
+ if (!root || !root.content.equals(Buffer.from(document.markdown, "utf8")) || root.bytes !== document.bytes || root.sha256 !== document.sha256) {
4553
+ throw invalid(`\u6280\u80FD ${document.id} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
4554
+ }
4555
+ }
4556
+ if (!seen.has(requestedSkill)) throw invalid(`\u540C\u6B65\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${requestedSkill}`);
4557
+ }
4558
+ function validatePreparedRuntime(runtime) {
4559
+ if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0) {
4560
+ throw invalid("WorkBuddy CLI \u80FD\u529B\u96C6\u5408\u4E3A\u7A7A");
4561
+ }
4562
+ const capabilities = /* @__PURE__ */ new Set();
4563
+ for (const capability of runtime.capabilities) {
4564
+ if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
4565
+ throw invalid(`WorkBuddy CLI \u80FD\u529B\u6807\u8BC6\u975E\u6CD5\u6216\u91CD\u590D\uFF1A${capability}`);
4566
+ }
4567
+ capabilities.add(capability);
4568
+ }
4569
+ if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
4570
+ `)) {
4571
+ throw invalid("WorkBuddy CLI \u80FD\u529B\u6458\u8981\u4E0D\u4E00\u81F4");
4572
+ }
4573
+ if (!validDigest(runtime.launch.executableSha256)) {
4574
+ throw invalid("WorkBuddy CLI \u53EF\u6267\u884C\u6587\u4EF6\u6458\u8981\u975E\u6CD5");
4575
+ }
4576
+ if (runtime.launch.kind === "node-entry" && !validDigest(runtime.launch.entrySha256)) {
4577
+ throw invalid("WorkBuddy CLI \u5165\u53E3\u6458\u8981\u975E\u6CD5");
4578
+ }
4579
+ if (!Array.isArray(runtime.files) || runtime.files.length !== 2) {
4580
+ throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5FC5\u987B\u540C\u65F6\u5305\u542B Unix \u4E0E Windows \u6587\u4EF6");
4581
+ }
4582
+ const files = /* @__PURE__ */ new Set();
4583
+ for (const file of runtime.files) {
4584
+ validateRuntimeFile(file);
4585
+ if (files.has(file.path)) throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u91CD\u590D\uFF1A${file.path}`);
4586
+ files.add(file.path);
4587
+ }
4588
+ if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
4589
+ throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5408\u540C\u4E0D\u5B8C\u6574");
4590
+ }
4591
+ }
4592
+ function validateRuntimeFile(file) {
4593
+ validateOwnedFile(file.path);
4594
+ if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256 || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
4595
+ throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u6458\u8981\u6216\u6743\u9650\u4E0D\u4E00\u81F4\uFF1A${file.path}`);
4596
+ }
4597
+ }
4598
+ function validateWorkBuddyFrontmatter(markdown, skillId) {
4599
+ const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(markdown);
4600
+ if (!match) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 YAML frontmatter`);
4601
+ const frontmatter = match[1] ?? "";
4602
+ const name = /^name:\s*['"]?([^'"\s]+)['"]?\s*$/m.exec(frontmatter)?.[1];
4603
+ const description = /^description:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim();
4604
+ if (name !== skillId) throw invalid(`\u6280\u80FD ${skillId} \u7684 frontmatter name \u4E0D\u4E00\u81F4`);
4605
+ if (!description) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 description\uFF0CWorkBuddy \u65E0\u6CD5\u53D1\u73B0`);
4606
+ }
4607
+ async function readLock(lockPath) {
4608
+ const content = await readOptional(lockPath);
4609
+ if (content === void 0) return void 0;
4610
+ try {
4611
+ const lock = JSON.parse(content);
4612
+ if (lock.schemaVersion !== 1 || !lock.source || !lock.selection?.requestedSkill || !Array.isArray(lock.resolvedSkills) || !Array.isArray(lock.ownedFiles)) {
4613
+ throw new Error("schema mismatch");
4614
+ }
4615
+ assertSafeSkillId(lock.selection.requestedSkill);
4616
+ for (const skill of lock.resolvedSkills) {
4617
+ assertSafeSkillId(skill.id);
4618
+ if (!Number.isInteger(skill.bytes) || skill.bytes < 0 || !/^[a-f0-9]{64}$/.test(skill.sha256)) {
4619
+ throw new Error("invalid skill digest");
4620
+ }
4621
+ if (skill.files !== void 0) {
4622
+ if (!Array.isArray(skill.files) || skill.files.length === 0)
4623
+ throw new Error("invalid files");
4624
+ const seen = /* @__PURE__ */ new Set();
4625
+ for (const file of skill.files) {
4626
+ assertSafeSkillFilePath(file.path);
4627
+ if (seen.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
4628
+ throw new Error("invalid file digest");
4629
+ }
4630
+ seen.add(file.path);
4631
+ }
4632
+ if (!seen.has("SKILL.md")) throw new Error("missing SKILL.md");
4633
+ }
4634
+ }
4635
+ if (lock.runtime !== void 0) validateLockedRuntime(lock.runtime);
4636
+ for (const file of lock.ownedFiles) validateOwnedFile(file);
4637
+ return lock;
4638
+ } catch (cause) {
4639
+ throw new CliError14({
4640
+ code: CliErrorCode13.CONFIG_PARSE_ERROR,
4641
+ message: `Chiron \u9501\u6587\u4EF6\u635F\u574F\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6587\u4EF6\u6240\u6709\u6743\uFF1A${lockPath}`,
4642
+ cause
4643
+ });
4644
+ }
4645
+ }
4646
+ function validateLockedRuntime(runtime) {
4647
+ if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
4648
+ `) || !runtime.launch || !validDigest(runtime.launch.executableSha256) || !Array.isArray(runtime.files) || runtime.files.length !== 2) {
4649
+ throw new Error("invalid runtime");
4650
+ }
4651
+ if (runtime.launch.kind === "node-entry") {
4652
+ if (!validDigest(runtime.launch.entrySha256)) throw new Error("invalid runtime entry");
4653
+ } else if (runtime.launch.kind !== "executable") {
4654
+ throw new Error("invalid runtime launch kind");
4655
+ }
4656
+ const capabilities = /* @__PURE__ */ new Set();
4657
+ for (const capability of runtime.capabilities) {
4658
+ if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
4659
+ throw new Error("invalid runtime capability");
4660
+ }
4661
+ capabilities.add(capability);
4662
+ }
4663
+ const files = /* @__PURE__ */ new Set();
4664
+ for (const file of runtime.files) {
4665
+ validateOwnedFile(file.path);
4666
+ if (files.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !validDigest(file.sha256) || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
4667
+ throw new Error("invalid runtime file");
4668
+ }
4669
+ files.add(file.path);
4670
+ }
4671
+ if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
4672
+ throw new Error("missing runtime file");
4673
+ }
4674
+ }
4675
+ function sameLockContent(left, right) {
4676
+ const withoutTime = (lock) => ({ ...lock, syncedAt: void 0 });
4677
+ return stableJson2(withoutTime(left)) === stableJson2(withoutTime(right));
4678
+ }
4679
+ async function desiredFilesMatch(project, desired) {
4680
+ for (const [file, preparedFile2] of desired) {
4681
+ const absolute = path2.join(project, file);
4682
+ const current = await readOptionalBytes(absolute);
4683
+ if (current === void 0 || sha256(current) !== preparedFile2.sha256 || !await fileModeMatches(absolute, preparedFile2.mode)) {
4684
+ return false;
4685
+ }
4686
+ }
4687
+ return true;
4688
+ }
4689
+ function result(changed, input, project, skillsDir, lockPath, installed, removed) {
4690
+ return {
4691
+ changed,
4692
+ requestedSkill: input.requestedSkill,
4693
+ resolvedSkills: input.documents.map((document) => document.id),
4694
+ project,
4695
+ skillsDir,
4696
+ lockPath,
4697
+ runnerPath: path2.join(project, ".hcm-delivery", "bin", "hcm"),
4698
+ capabilitiesSha256: input.runtime.capabilitiesSha256,
4699
+ installed,
4700
+ removed
4701
+ };
4702
+ }
4703
+ function ownedFile(id, relativePath = "SKILL.md") {
4704
+ assertSafeSkillId(id);
4705
+ assertSafeSkillFilePath(relativePath);
4706
+ return `.codebuddy/skills/${id}/${relativePath}`;
4707
+ }
4708
+ function idFromOwnedFile(file) {
4709
+ validateOwnedFile(file);
4710
+ if (!file.startsWith(".codebuddy/skills/")) return void 0;
4711
+ return file.split("/")[2] ?? "";
4712
+ }
4713
+ function validateOwnedFile(file) {
4714
+ if (file === ".hcm-delivery/bin/hcm" || file === ".hcm-delivery/bin/hcm.cmd") return;
4715
+ const match = /^\.codebuddy\/skills\/([a-z0-9][a-z0-9-]{0,63})\/(.+)$/.exec(file);
4716
+ if (!match) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
4717
+ const id = match[1];
4718
+ const relativePath = match[2];
4719
+ if (!id || !relativePath) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
4720
+ assertSafeSkillId(id);
4721
+ try {
4722
+ assertSafeSkillFilePath(relativePath);
4723
+ } catch {
4724
+ throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
4725
+ }
4726
+ }
4727
+ function normalizeEndpoint(endpoint) {
4728
+ let url;
4729
+ try {
4730
+ url = new URL(endpoint);
4731
+ } catch (cause) {
4732
+ throw new CliError14({
4733
+ code: CliErrorCode13.INVALID_ARGUMENT,
4734
+ message: `\u975E\u6CD5 endpoint\uFF1A${endpoint}`,
4735
+ cause
4736
+ });
4737
+ }
4738
+ url.username = "";
4739
+ url.password = "";
4740
+ url.search = "";
4741
+ url.hash = "";
4742
+ url.pathname = url.pathname.replace(/\/+$/, "");
4743
+ return url.toString().replace(/\/$/, "");
4744
+ }
4745
+ function sha256(value) {
4746
+ return createHash("sha256").update(value).digest("hex");
4747
+ }
4748
+ function stableJson2(value) {
4749
+ if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
4750
+ if (value && typeof value === "object") {
4751
+ const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
4752
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
4753
+ }
4754
+ return JSON.stringify(value);
4755
+ }
4756
+ async function readOptional(file) {
4757
+ try {
4758
+ return await fs4.readFile(file, "utf8");
4759
+ } catch (cause) {
4760
+ if (cause.code === "ENOENT") return void 0;
4761
+ throw cause;
4762
+ }
4763
+ }
4764
+ async function readOptionalBytes(file) {
4765
+ try {
4766
+ return await fs4.readFile(file);
4767
+ } catch (cause) {
4768
+ if (cause.code === "ENOENT") return void 0;
4769
+ throw cause;
4770
+ }
4771
+ }
4772
+ async function exists(file) {
4773
+ try {
4774
+ await fs4.access(file);
4775
+ return true;
4776
+ } catch (cause) {
4777
+ if (cause.code === "ENOENT") return false;
4778
+ throw cause;
4779
+ }
4780
+ }
4781
+ function lockDigests(lock) {
4782
+ const digests = /* @__PURE__ */ new Map();
4783
+ for (const skill of lock?.resolvedSkills ?? []) {
4784
+ if (skill.files === void 0) {
4785
+ digests.set(ownedFile(skill.id), { sha256: skill.sha256 });
4786
+ continue;
4787
+ }
4788
+ for (const file of skill.files) {
4789
+ digests.set(ownedFile(skill.id, file.path), { sha256: file.sha256 });
4790
+ }
4791
+ }
4792
+ for (const file of lock?.runtime?.files ?? []) {
4793
+ digests.set(file.path, { sha256: file.sha256, mode: file.mode });
4794
+ }
4795
+ return digests;
4796
+ }
4797
+ async function fileModeMatches(file, expected) {
4798
+ if (expected === void 0 || process.platform === "win32") return true;
4799
+ const stat = await fs4.stat(file);
4800
+ return (stat.mode & 511) === expected;
4801
+ }
4802
+ function validDigest(value) {
4803
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
4804
+ }
4805
+ function ignoreMissing(cause) {
4806
+ if (cause.code !== "ENOENT") throw cause;
4807
+ }
4808
+ function invalid(message) {
4809
+ return new CliError14({ code: CliErrorCode13.INVALID_ARGUMENT, message });
4810
+ }
4811
+
4812
+ // src/commands/skills-runtime.ts
4813
+ import { execFile } from "child_process";
4814
+ import { createHash as createHash2 } from "crypto";
4815
+ import { createReadStream, promises as fs5 } from "fs";
4816
+ import path3 from "path";
4817
+ import { promisify } from "util";
4818
+ import { CliError as CliError15, CliErrorCode as CliErrorCode14 } from "@hcmai/sdk";
4819
+ var execFileAsync = promisify(execFile);
4820
+ var REQUIRED_CAPABILITIES = [
4821
+ "hcm.help.skills",
4822
+ "hcm.skills.help.sync",
4823
+ "hcm.skills.sync.option.project",
4824
+ "hcm.skills.sync.option.target"
4825
+ ];
4826
+ async function prepareWorkBuddyRuntime(input = {}) {
4827
+ const candidate = input.candidate?.trim() || input.currentEntry || process.argv[1];
4828
+ if (!candidate) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D HCM CLI \u5165\u53E3\uFF1B\u8BF7\u4F20 --cli <path>");
4829
+ const launch = await resolveLaunch(candidate, input.nodeExecutable ?? process.execPath);
4830
+ const launchTarget = launch.kind === "node-entry" ? launch.entry : launch.executable;
4831
+ for (const forbidden2 of input.forbiddenCandidatePaths ?? []) {
4832
+ if (await comparablePath(forbidden2) === launchTarget) {
4833
+ throw invalid2("\u5019\u9009 HCM CLI \u4E0D\u80FD\u6307\u5411\u5F85\u751F\u6210\u7684\u9879\u76EE\u7A33\u5B9A\u5165\u53E3");
4834
+ }
4835
+ }
4836
+ if (!(input.currentEntryHasRequiredCapabilities && !input.candidate?.trim())) {
4837
+ const beforeProbe = await launchIdentity(launch);
4838
+ const syncHelp = await runProbe(launch, ["skills", "sync", "--help"], input.timeoutMs).catch(
4839
+ () => ""
4840
+ );
4841
+ const missing = [];
4842
+ if (!/\bhcm\s+skills\s+sync\b/.test(syncHelp)) {
4843
+ missing.push(REQUIRED_CAPABILITIES[0], REQUIRED_CAPABILITIES[1]);
4844
+ }
4845
+ if (!/--project(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[2]);
4846
+ if (!/--target(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[3]);
4847
+ if (missing.length > 0) {
4848
+ throw invalid2(`\u5019\u9009 HCM CLI \u7F3A\u5C11 WorkBuddy \u6240\u9700\u80FD\u529B\uFF1A${missing.join(", ")}`);
4849
+ }
4850
+ if (await launchIdentity(launch) !== beforeProbe) {
4851
+ throw invalid2("\u5019\u9009 HCM CLI \u5728\u80FD\u529B\u63A2\u9488\u671F\u95F4\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u8BD5\u540C\u6B65");
4852
+ }
4853
+ }
4854
+ const capabilities = [...REQUIRED_CAPABILITIES];
4855
+ const capabilitiesSha256 = sha2562(`${capabilities.join("\n")}
4856
+ `);
4857
+ const executableSha256 = await sha256File(launch.executable);
4858
+ const launchRecord = launch.kind === "node-entry" ? {
4859
+ kind: "node-entry",
4860
+ executableSha256,
4861
+ entrySha256: await sha256File(launch.entry)
4862
+ } : { kind: "executable", executableSha256 };
4863
+ const unixContent = Buffer.from(unixLauncher(launch), "utf8");
4864
+ const windowsContent = Buffer.from(windowsLauncher(launch), "utf8");
4865
+ return {
4866
+ capabilities,
4867
+ capabilitiesSha256,
4868
+ launch: launchRecord,
4869
+ files: [
4870
+ preparedFile(".hcm-delivery/bin/hcm", unixContent, 493),
4871
+ preparedFile(".hcm-delivery/bin/hcm.cmd", windowsContent, 420)
4872
+ ]
4873
+ };
4874
+ }
4875
+ async function resolveLaunch(candidate, nodeExecutable) {
4876
+ const requested = path3.resolve(candidate);
4877
+ const resolvedCandidate = await realFile(requested, "\u5019\u9009 HCM CLI");
4878
+ const header = (await readPrefix(resolvedCandidate, 256)).toString("utf8");
4879
+ const nodeEntry = /^(?:#![^\r\n]*\bnode\b)|\.(?:c|m)?js$/i.test(
4880
+ header.startsWith("#!") ? header.split(/\r?\n/, 1)[0] ?? "" : resolvedCandidate
4881
+ );
4882
+ if (nodeEntry) {
4883
+ return {
4884
+ kind: "node-entry",
4885
+ executable: await realFile(path3.resolve(nodeExecutable), "Node \u53EF\u6267\u884C\u6587\u4EF6"),
4886
+ entry: resolvedCandidate
4887
+ };
4888
+ }
4889
+ return { kind: "executable", executable: resolvedCandidate };
4890
+ }
4891
+ async function realFile(file, label) {
4892
+ try {
4893
+ const resolved = await fs5.realpath(file);
4894
+ const stat = await fs5.stat(resolved);
4895
+ if (!stat.isFile()) throw new Error("not a regular file");
4896
+ return resolved;
4897
+ } catch (cause) {
4898
+ throw new CliError15({
4899
+ code: CliErrorCode14.INVALID_ARGUMENT,
4900
+ message: `${label}\u4E0D\u53EF\u7528\uFF1A${file}`,
4901
+ cause
4902
+ });
4903
+ }
4904
+ }
4905
+ async function comparablePath(file) {
4906
+ const absolute = path3.resolve(file);
4907
+ return fs5.realpath(absolute).catch(() => absolute);
4908
+ }
4909
+ async function launchIdentity(launch) {
4910
+ const files = launch.kind === "node-entry" ? [launch.executable, launch.entry] : [launch.executable];
4911
+ const identities = await Promise.all(
4912
+ files.map(async (file) => {
4913
+ const stat = await fs5.stat(file, { bigint: true });
4914
+ return [file, stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].map(String).join("\0");
4915
+ })
4916
+ );
4917
+ return identities.join("\n");
4918
+ }
4919
+ async function readPrefix(file, bytes) {
4920
+ const handle = await fs5.open(file, "r");
4921
+ try {
4922
+ const buffer = Buffer.alloc(bytes);
4923
+ const result2 = await handle.read(buffer, 0, bytes, 0);
4924
+ return buffer.subarray(0, result2.bytesRead);
4925
+ } finally {
4926
+ await handle.close();
4927
+ }
4928
+ }
4929
+ async function runProbe(launch, args, timeoutMs = 5e3) {
4930
+ const command = launch.executable;
4931
+ const commandArgs = launch.kind === "node-entry" ? [launch.entry, ...args] : args;
4932
+ const env2 = {
4933
+ ...process.env,
4934
+ CI: "1",
4935
+ NO_COLOR: "1",
4936
+ HCM_RUNTIME_PROBE: "1"
4937
+ };
4938
+ const nodeOptions = normalizeWorkBuddyNodeOptions(env2.NODE_OPTIONS);
4939
+ if (nodeOptions === void 0) delete env2.NODE_OPTIONS;
4940
+ else env2.NODE_OPTIONS = nodeOptions;
4941
+ try {
4942
+ const { stdout, stderr } = await execFileAsync(command, commandArgs, {
4943
+ encoding: "utf8",
4944
+ timeout: timeoutMs,
4945
+ maxBuffer: 256 * 1024,
4946
+ windowsHide: true,
4947
+ env: env2
4948
+ });
4949
+ return `${stdout}
4950
+ ${stderr}`;
4951
+ } catch (cause) {
4952
+ throw new CliError15({
4953
+ code: CliErrorCode14.INVALID_ARGUMENT,
4954
+ message: `\u5019\u9009 HCM CLI \u80FD\u529B\u63A2\u9488\u5931\u8D25\uFF1Ahcm ${args.join(" ")}`,
4955
+ cause
4956
+ });
4957
+ }
4958
+ }
4959
+ function unixLauncher(launch) {
4960
+ const command = shellQuote(launch.executable);
4961
+ const entry = launch.kind === "node-entry" ? ` ${shellQuote(launch.entry)}` : "";
4962
+ return `#!/bin/sh
4963
+ set -eu
4964
+
4965
+ # WorkBuddy on macOS may inject --use-system-ca. Node can then spend minutes
4966
+ # evaluating the Keychain when a short-lived CLI process exits. Preserve every
4967
+ # other host option (including safety --require hooks) and change only this CA mode.
4968
+ wb_node_options="\${NODE_OPTIONS-}"
4969
+ if [ "$(uname -s 2>/dev/null || printf '%s' unknown)" = 'Darwin' ] && printf '%s
4970
+ ' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; then
4971
+ while printf '%s
4972
+ ' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; do
4973
+ wb_next_options="$(printf '%s
4974
+ ' "$wb_node_options" | sed -E 's/(^|[[:space:]])--use-system-ca([[:space:]]|$)/\\1\\2/g')"
4975
+ [ "$wb_next_options" != "$wb_node_options" ] || break
4976
+ wb_node_options="$wb_next_options"
4977
+ done
4978
+ if ! printf '%s
4979
+ ' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-bundled-ca([[:space:]]|$)'; then
4980
+ wb_node_options="\${wb_node_options:+$wb_node_options }--use-bundled-ca"
4981
+ fi
4982
+ NODE_OPTIONS="$wb_node_options"
4983
+ export NODE_OPTIONS
4984
+ fi
4985
+
4986
+ exec ${command}${entry} "$@"
4987
+ `;
4988
+ }
4989
+ function normalizeWorkBuddyNodeOptions(value, platform = process.platform) {
4990
+ if (platform !== "darwin" || value === void 0) return value;
4991
+ const systemCa = /(^|[\t\n\r ])--use-system-ca(?=$|[\t\n\r ])/;
4992
+ if (!systemCa.test(value)) return value;
4993
+ let normalized = value;
4994
+ while (systemCa.test(normalized)) normalized = normalized.replace(systemCa, "$1");
4995
+ normalized = normalized.trim();
4996
+ const bundledCa = /(^|[\t\n\r ])--use-bundled-ca(?=$|[\t\n\r ])/;
4997
+ if (!bundledCa.test(normalized)) {
4998
+ normalized = `${normalized}${normalized ? " " : ""}--use-bundled-ca`;
4999
+ }
5000
+ return normalized;
5001
+ }
5002
+ function windowsLauncher(launch) {
5003
+ const command = cmdQuote(launch.executable);
5004
+ const entry = launch.kind === "node-entry" ? ` ${cmdQuote(launch.entry)}` : "";
5005
+ return `@echo off\r
5006
+ ${command}${entry} %*\r
5007
+ `;
5008
+ }
5009
+ function shellQuote(value) {
5010
+ if (/[\r\n\0]/.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
5011
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
5012
+ }
5013
+ function cmdQuote(value) {
5014
+ if (/[\r\n\0"]/g.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
5015
+ return `"${value.replace(/%/g, "%%")}"`;
5016
+ }
5017
+ function preparedFile(filePath, content, mode) {
5018
+ return { path: filePath, content, bytes: content.length, sha256: sha2562(content), mode };
5019
+ }
5020
+ async function sha256File(file) {
5021
+ const hash = createHash2("sha256");
5022
+ await new Promise((resolve3, reject) => {
5023
+ const stream = createReadStream(file);
5024
+ stream.on("data", (chunk) => hash.update(chunk));
5025
+ stream.on("error", reject);
5026
+ stream.on("end", resolve3);
5027
+ });
5028
+ return hash.digest("hex");
5029
+ }
5030
+ function sha2562(value) {
5031
+ return createHash2("sha256").update(value).digest("hex");
5032
+ }
5033
+ function invalid2(message) {
5034
+ return new CliError15({ code: CliErrorCode14.INVALID_ARGUMENT, message });
5035
+ }
5036
+
5037
+ // src/commands/skills.ts
4253
5038
  var SKILLS_STATE_FILE = ".hcm-skills.json";
4254
- function sha256(text) {
4255
- return createHash("sha256").update(text, "utf8").digest("hex");
5039
+ function sha2563(text) {
5040
+ return createHash3("sha256").update(text, "utf8").digest("hex");
4256
5041
  }
4257
5042
  async function resolveEndpoint(args) {
4258
5043
  if (args.endpoint) return args.endpoint.replace(/\/+$/, "");
@@ -4260,14 +5045,43 @@ async function resolveEndpoint(args) {
4260
5045
  const envName = resolveActiveEnv3({ envFlag: args.env }, process.env, g);
4261
5046
  const env2 = await loadEnv5(envName).catch(() => null);
4262
5047
  if (!env2?.endpoint) {
4263
- throw new CliError14({
4264
- code: CliErrorCode13.MISSING_FLAG,
5048
+ throw new CliError16({
5049
+ code: CliErrorCode15.MISSING_FLAG,
4265
5050
  message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`\u3002\uFF08\u6280\u80FD\u5E93\u514D\u8BA4\u8BC1\uFF0C\u4E0D\u9700\u8981\u5148 login\uFF09"
4266
5051
  });
4267
5052
  }
4268
5053
  return env2.endpoint.replace(/\/+$/, "");
4269
5054
  }
4270
- async function exists(target) {
5055
+ function normalizeEndpoint2(endpoint) {
5056
+ return endpoint.replace(/\/+$/, "");
5057
+ }
5058
+ function decideTrackedEndpoint(options) {
5059
+ const { explicit } = options;
5060
+ const current = options.current ? normalizeEndpoint2(options.current) : void 0;
5061
+ const recorded = options.recorded ? normalizeEndpoint2(options.recorded) : void 0;
5062
+ if (explicit) {
5063
+ if (!current) throw invalid3("\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u7528 --env <name> \u6307\u4E00\u4E2A\u914D\u597D\u7684\u73AF\u5883");
5064
+ return current;
5065
+ }
5066
+ if (!recorded) {
5067
+ if (!current) {
5068
+ throw new CliError16({
5069
+ code: CliErrorCode15.MISSING_FLAG,
5070
+ message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`\u3002\uFF08\u6280\u80FD\u5E93\u514D\u8BA4\u8BC1\uFF0C\u4E0D\u9700\u8981\u5148 login\uFF09"
5071
+ });
5072
+ }
5073
+ return current;
5074
+ }
5075
+ if (!current || current === recorded) return recorded;
5076
+ throw invalid3(
5077
+ `\u8FD9\u4E2A\u76EE\u5F55\u7684\u6280\u80FD\u88C5\u81EA ${recorded}\uFF0C\u5F53\u524D env \u6307\u5411 ${current} \u2014\u2014 \u4E0D\u662F\u540C\u4E00\u4E2A\u73AF\u5883\u3002
5078
+ \u6280\u80FD\u968F\u4EA7\u54C1\u7248\u672C\u8D70\uFF0C\u8DE8\u73AF\u5883\u5347\u7EA7\u4F1A\u62FF\u53E6\u4E00\u4E2A\u73AF\u5883\u7684\u7248\u672C\u76D6\u6389\u73B0\u5728\u8FD9\u4EFD\uFF0C\u4E14\u4E0D\u4F1A\u6709\u4EFB\u4F55\u63D0\u793A\u3002
5079
+ \u660E\u8BF4\u8981\u54EA\u4E2A\uFF1A
5080
+ \xB7 \u7EE7\u7EED\u8DDF ${recorded} \u2192 \u52A0 --endpoint ${recorded}
5081
+ \xB7 \u6539\u8DDF\u5F53\u524D\u8FD9\u4E2A \u2192 \u52A0 --endpoint ${current}\uFF08\u6765\u6E90\u4F1A\u6539\u6210\u5B83\uFF0C\u4E4B\u540E\u4E0D\u7528\u518D\u5E26\uFF09`
5082
+ );
5083
+ }
5084
+ async function exists2(target) {
4271
5085
  try {
4272
5086
  await fsp.stat(target);
4273
5087
  return true;
@@ -4276,25 +5090,25 @@ async function exists(target) {
4276
5090
  }
4277
5091
  }
4278
5092
  async function detectTarget(cwd) {
4279
- if (await exists(path2.join(cwd, ".claude"))) return "claude";
4280
- if (await exists(path2.join(cwd, "AGENTS.md"))) return "codex";
4281
- if (await exists(path2.join(cwd, ".codex"))) return "codex";
5093
+ if (await exists2(path4.join(cwd, ".claude"))) return "claude";
5094
+ if (await exists2(path4.join(cwd, "AGENTS.md"))) return "codex";
5095
+ if (await exists2(path4.join(cwd, ".codex"))) return "codex";
4282
5096
  return void 0;
4283
5097
  }
4284
5098
  async function resolveInstallLayout(args, cwd = process.cwd()) {
4285
5099
  const requested = args.target && args.target !== "auto" ? args.target : void 0;
4286
5100
  const target = requested ?? await detectTarget(cwd) ?? "claude";
4287
5101
  if (target === "codex") {
4288
- const root = args.global ? path2.join(os2.homedir(), ".codex") : cwd;
5102
+ const root = args.global ? path4.join(os2.homedir(), ".codex") : cwd;
4289
5103
  return {
4290
5104
  target,
4291
- baseDir: args.dir ? path2.resolve(args.dir) : path2.join(root, "hcm-skills"),
4292
- agentsFile: path2.join(root, "AGENTS.md")
5105
+ baseDir: args.dir ? path4.resolve(args.dir) : path4.join(root, "hcm-skills"),
5106
+ agentsFile: path4.join(root, "AGENTS.md")
4293
5107
  };
4294
5108
  }
4295
5109
  return {
4296
5110
  target,
4297
- baseDir: args.dir ? path2.resolve(args.dir) : args.global ? path2.join(os2.homedir(), ".claude", "skills") : path2.join(cwd, ".claude", "skills")
5111
+ baseDir: args.dir ? path4.resolve(args.dir) : args.global ? path4.join(os2.homedir(), ".claude", "skills") : path4.join(cwd, ".claude", "skills")
4298
5112
  };
4299
5113
  }
4300
5114
  async function chironClient(args) {
@@ -4397,7 +5211,7 @@ async function listInstalledSkills(keyword, args) {
4397
5211
  process.stdout.write(formatObject13({ baseDir: layout.baseDir, target: layout.target, all: false, skills: [] }, { format: fmt }) + "\n");
4398
5212
  process.exit(0);
4399
5213
  }
4400
- process.stdout.write(`${path2.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002
5214
+ process.stdout.write(`${path4.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002
4401
5215
  `);
4402
5216
  process.stdout.write(" \u88C5\u5168\u90E8\uFF1Ahcm skills install --all\n");
4403
5217
  process.exit(0);
@@ -4416,7 +5230,7 @@ async function listInstalledSkills(keyword, args) {
4416
5230
  direct: record.direct,
4417
5231
  installedAt: record.installedAt,
4418
5232
  // 三种状态互斥:文件没了 / 本地改过 / 和装的时候一致
4419
- status: markdown === void 0 ? "missing" : sha256(markdown) !== record.sha256 ? "locally-modified" : "installed"
5233
+ status: markdown === void 0 ? "missing" : sha2563(markdown) !== record.sha256 ? "locally-modified" : "installed"
4420
5234
  });
4421
5235
  }
4422
5236
  const shown = keyword ? rows.filter((row) => row.id.includes(keyword) || row.title.includes(keyword)) : rows;
@@ -4470,12 +5284,73 @@ async function skillsShowCommand(id, args) {
4470
5284
  fail3(e, args);
4471
5285
  }
4472
5286
  }
5287
+ async function skillsSyncCommand(id, args) {
5288
+ try {
5289
+ assertSafeSkillId2(id);
5290
+ if (args.target !== "workbuddy") {
5291
+ throw invalid3(`sync --target \u5F53\u524D\u53EA\u652F\u6301 workbuddy\uFF0C\u6536\u5230 "${args.target ?? ""}"`);
5292
+ }
5293
+ if (!args.project?.trim()) throw invalid3("sync \u5FC5\u987B\u4F20 --project <path>");
5294
+ const project = path4.resolve(args.project);
5295
+ const runtime = await prepareWorkBuddyRuntime({
5296
+ candidate: args.cli,
5297
+ currentEntryHasRequiredCapabilities: !args.cli?.trim(),
5298
+ forbiddenCandidatePaths: [
5299
+ path4.join(project, ".hcm-delivery", "bin", "hcm"),
5300
+ path4.join(project, ".hcm-delivery", "bin", "hcm.cmd")
5301
+ ]
5302
+ });
5303
+ const connectionArgs = { endpoint: args.endpoint, env: args.env };
5304
+ const { http, endpoint, base } = await chironClient(connectionArgs);
5305
+ const catalog = await fetchSkillCatalog(http, base);
5306
+ const documents = await prepareSkillDocuments(
5307
+ catalog,
5308
+ id,
5309
+ async (skillId) => fetchSkillMarkdown(http, skillId, base),
5310
+ async (skillId, relativePath) => fetchSkillFile(http, skillId, relativePath, base)
5311
+ );
5312
+ const result2 = await syncWorkBuddyProject({
5313
+ project,
5314
+ endpoint,
5315
+ catalog,
5316
+ requestedSkill: id,
5317
+ documents,
5318
+ runtime
5319
+ });
5320
+ const fmt = args.output ?? "table";
5321
+ if (fmt === "json" || fmt === "yaml") {
5322
+ process.stdout.write(`${formatObject13(result2, { format: fmt })}
5323
+ `);
5324
+ } else if (result2.changed) {
5325
+ process.stdout.write(`\u2705 ${id} \u2192 ${result2.skillsDir}
5326
+ `);
5327
+ process.stdout.write(` \u95ED\u5305 ${result2.resolvedSkills.join(" \u2192 ")}
5328
+ `);
5329
+ process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
5330
+ `);
5331
+ process.stdout.write(` CLI \u80FD\u529B ${result2.capabilitiesSha256.slice(0, 12)}
5332
+ `);
5333
+ process.stdout.write(` \u9501 ${result2.lockPath}
5334
+ `);
5335
+ } else {
5336
+ process.stdout.write(`\u2713 ${id} \u5DF2\u662F\u5F53\u524D\u76EE\u6807\u5B9E\u4F8B\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0
5337
+ `);
5338
+ process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
5339
+ `);
5340
+ process.stdout.write(` \u9501 ${result2.lockPath}
5341
+ `);
5342
+ }
5343
+ process.exit(0);
5344
+ } catch (e) {
5345
+ fail3(e, { endpoint: args.endpoint, env: args.env });
5346
+ }
5347
+ }
4473
5348
  async function skillsInstallCommand(id, args) {
4474
5349
  try {
4475
5350
  const all = Boolean(args.all);
4476
- if (all && id) throw invalid("`--all` \u662F\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u4E0D\u8981\u518D\u5E26\u6280\u80FD id\uFF1B\u53EA\u88C5\u4E00\u4E2A\u5C31\u53BB\u6389 --all");
4477
- if (!all && !id) throw invalid("\u8981\u88C5\u54EA\u4E2A\u6280\u80FD\uFF1F\u7ED9\u4E2A id\uFF08`hcm skills list` \u770B\u76EE\u5F55\uFF09\uFF0C\u6216\u7528 `--all` \u88C5\u5168\u90E8");
4478
- if (id) assertSafeSkillId(id);
5351
+ if (all && id) throw invalid3("`--all` \u662F\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u4E0D\u8981\u518D\u5E26\u6280\u80FD id\uFF1B\u53EA\u88C5\u4E00\u4E2A\u5C31\u53BB\u6389 --all");
5352
+ if (!all && !id) throw invalid3("\u8981\u88C5\u54EA\u4E2A\u6280\u80FD\uFF1F\u7ED9\u4E2A id\uFF08`hcm skills list` \u770B\u76EE\u5F55\uFF09\uFF0C\u6216\u7528 `--all` \u88C5\u5168\u90E8");
5353
+ if (id) assertSafeSkillId2(id);
4479
5354
  const layout = await resolveInstallLayout(args);
4480
5355
  let documents;
4481
5356
  let requestedIds;
@@ -4483,7 +5358,7 @@ async function skillsInstallCommand(id, args) {
4483
5358
  let base;
4484
5359
  let manifest2;
4485
5360
  if (args.from) {
4486
- const root = path2.resolve(args.from);
5361
+ const root = path4.resolve(args.from);
4487
5362
  requestedIds = all ? await listLocalSkillIds(root) : [id];
4488
5363
  const seen = /* @__PURE__ */ new Set();
4489
5364
  documents = [];
@@ -4495,12 +5370,17 @@ async function skillsInstallCommand(id, args) {
4495
5370
  }
4496
5371
  }
4497
5372
  } else {
4498
- const client3 = await chironClient(args);
5373
+ const tracked = decideTrackedEndpoint({
5374
+ explicit: Boolean(args.endpoint || args.env),
5375
+ current: await resolveEndpoint(args).catch(() => void 0),
5376
+ recorded: (await readSkillsState(layout.baseDir))?.endpoint
5377
+ });
5378
+ const client3 = await chironClient({ ...args, endpoint: tracked });
4499
5379
  endpoint = client3.endpoint;
4500
5380
  base = client3.base;
4501
5381
  manifest2 = await fetchSkillCatalog(client3.http, base);
4502
- const order = all ? resolveAllSkillsInstallOrder(manifest2) : resolveSkillInstallOrder(manifest2, id);
4503
- if (all && order.length === 0) throw invalid(`${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u662F\u7A7A\u7684\uFF0C\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
5382
+ const order = all ? resolveAllSkillsInstallOrder(manifest2) : resolveSkillInstallOrder2(manifest2, id);
5383
+ if (all && order.length === 0) throw invalid3(`${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u662F\u7A7A\u7684\uFF0C\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
4504
5384
  requestedIds = all ? order.map((skill) => skill.id) : [id];
4505
5385
  documents = await mapWithConcurrency(
4506
5386
  order,
@@ -4508,7 +5388,7 @@ async function skillsInstallCommand(id, args) {
4508
5388
  (skill) => fetchSkillDocument(client3.http, skill, endpoint, base)
4509
5389
  );
4510
5390
  }
4511
- const result = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
5391
+ const result2 = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
4512
5392
  endpoint,
4513
5393
  base,
4514
5394
  target: layout.target,
@@ -4517,19 +5397,19 @@ async function skillsInstallCommand(id, args) {
4517
5397
  const agents = layout.agentsFile ? await syncAgentsIndex(layout, manifest2) : void 0;
4518
5398
  const fmt = args.output ?? "table";
4519
5399
  if (fmt === "json" || fmt === "yaml") {
4520
- process.stdout.write(formatObject13({ ...result, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents }, { format: fmt }) + "\n");
5400
+ process.stdout.write(formatObject13({ ...result2, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents }, { format: fmt }) + "\n");
4521
5401
  } else {
4522
- const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result.baseDir}` : `\u2705 ${id} \u2192 ${path2.join(result.baseDir, id, "SKILL.md")}`;
5402
+ const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result2.baseDir}` : `\u2705 ${id} \u2192 ${path4.join(result2.baseDir, id, "SKILL.md")}`;
4523
5403
  process.stdout.write(`${headline}
4524
5404
  `);
4525
- if (result.installed.length > 0) {
4526
- process.stdout.write(all ? ` \u65B0\u5199\u5165 ${result.installed.length} \u4E2A
4527
- ` : ` \u5DF2\u5B89\u88C5 ${result.installed.join(" \u2192 ")}
5405
+ if (result2.installed.length > 0) {
5406
+ process.stdout.write(all ? ` \u65B0\u5199\u5165 ${result2.installed.length} \u4E2A
5407
+ ` : ` \u5DF2\u5B89\u88C5 ${result2.installed.join(" \u2192 ")}
4528
5408
  `);
4529
5409
  }
4530
- if (result.unchanged.length > 0) {
4531
- process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${result.unchanged.length} \u4E2A
4532
- ` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${result.unchanged.join(", ")}
5410
+ if (result2.unchanged.length > 0) {
5411
+ process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${result2.unchanged.length} \u4E2A
5412
+ ` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${result2.unchanged.join(", ")}
4533
5413
  `);
4534
5414
  }
4535
5415
  const referenceCount = documents.reduce((n, d) => n + (d.references?.length ?? 0), 0);
@@ -4541,7 +5421,7 @@ async function skillsInstallCommand(id, args) {
4541
5421
  process.stdout.write(` \u5DF2${agents === "created" ? "\u521B\u5EFA" : "\u66F4\u65B0"} ${layout.agentsFile} \u91CC\u7684\u6280\u80FD\u7D22\u5F15\uFF08Codex \u8BFB\u8FD9\u91CC\uFF09
4542
5422
  `);
4543
5423
  }
4544
- process.stdout.write(` \u6765\u6E90 ${endpoint ?? path2.resolve(args.from)}
5424
+ process.stdout.write(` \u6765\u6E90 ${endpoint ?? path4.resolve(args.from)}
4545
5425
  `);
4546
5426
  process.stdout.write(all ? " \u5DF2\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D\uFF1A\u4EE5\u540E\u8DD1 hcm skills update\uFF0C\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4F1A\u81EA\u52A8\u8865\u4E0A\n" : " \u4EA7\u54C1\u66F4\u65B0\u540E\u8DD1 hcm skills update \u5347\u7EA7\n");
4547
5427
  }
@@ -4569,14 +5449,14 @@ async function listLocalSkillIds(root) {
4569
5449
  try {
4570
5450
  entries = await fsp.readdir(root, { withFileTypes: true });
4571
5451
  } catch {
4572
- throw invalid(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
5452
+ throw invalid3(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
4573
5453
  }
4574
5454
  const ids = [];
4575
5455
  for (const entry of entries) {
4576
5456
  if (!entry.isDirectory()) continue;
4577
- if (await exists(path2.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
5457
+ if (await exists2(path4.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
4578
5458
  }
4579
- if (ids.length === 0) throw invalid(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
5459
+ if (ids.length === 0) throw invalid3(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
4580
5460
  return ids.sort();
4581
5461
  }
4582
5462
  async function fetchSkillDocument(http, skill, endpoint, base) {
@@ -4600,23 +5480,23 @@ async function syncAgentsIndex(layout, manifest2) {
4600
5480
  for (const category of manifest2?.categories ?? []) {
4601
5481
  for (const skill of category.skills ?? []) catalog.set(skill.id, skill);
4602
5482
  }
4603
- const agentsDir = path2.dirname(layout.agentsFile);
5483
+ const agentsDir = path4.dirname(layout.agentsFile);
4604
5484
  const entries = Object.keys(state.skills).sort().map((id) => {
4605
5485
  const known = catalog.get(id);
4606
- const absolute = path2.join(layout.baseDir, id, "SKILL.md");
4607
- let relative2 = path2.relative(agentsDir, absolute);
5486
+ const absolute = path4.join(layout.baseDir, id, "SKILL.md");
5487
+ let relative2 = path4.relative(agentsDir, absolute);
4608
5488
  if (!relative2.startsWith(".")) relative2 = `./${relative2}`;
4609
5489
  return {
4610
5490
  id,
4611
5491
  title: known?.title ?? id,
4612
5492
  description: known?.description ?? "",
4613
- relativePath: relative2.split(path2.sep).join("/")
5493
+ relativePath: relative2.split(path4.sep).join("/")
4614
5494
  };
4615
5495
  });
4616
5496
  return upsertAgentsBlock(layout.agentsFile, renderAgentsBlock(entries));
4617
5497
  }
4618
5498
  async function resolveLocalSkillDocuments(sourceRoot, targetId) {
4619
- assertSafeSkillId(targetId);
5499
+ assertSafeSkillId2(targetId);
4620
5500
  const state = /* @__PURE__ */ new Map();
4621
5501
  const pathStack = [];
4622
5502
  const order = [];
@@ -4625,27 +5505,27 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
4625
5505
  if (current === "done") return;
4626
5506
  if (current === "visiting") {
4627
5507
  const start = pathStack.indexOf(id);
4628
- throw invalid(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
5508
+ throw invalid3(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
4629
5509
  }
4630
- assertSafeSkillId(id);
4631
- const local = path2.join(sourceRoot, id, "SKILL.md");
5510
+ assertSafeSkillId2(id);
5511
+ const local = path4.join(sourceRoot, id, "SKILL.md");
4632
5512
  let markdown;
4633
5513
  try {
4634
5514
  markdown = await fsp.readFile(local, "utf8");
4635
5515
  } catch (cause) {
4636
5516
  if (cause?.code === "ENOENT") {
4637
- throw invalid(requiredBy ? `\u672C\u5730\u6280\u80FD ${requiredBy} \u4F9D\u8D56\u4E0D\u5B58\u5728: ${id}\uFF08${local}\uFF09` : `\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709 ${id}/SKILL.md\uFF1A${path2.join(sourceRoot, id)}`);
5517
+ throw invalid3(requiredBy ? `\u672C\u5730\u6280\u80FD ${requiredBy} \u4F9D\u8D56\u4E0D\u5B58\u5728: ${id}\uFF08${local}\uFF09` : `\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709 ${id}/SKILL.md\uFF1A${path4.join(sourceRoot, id)}`);
4638
5518
  }
4639
5519
  throw cause;
4640
5520
  }
4641
- if (!markdown.trim()) throw invalid(`\u6280\u80FD ${id} \u5185\u5BB9\u4E3A\u7A7A`);
5521
+ if (!markdown.trim()) throw invalid3(`\u6280\u80FD ${id} \u5185\u5BB9\u4E3A\u7A7A`);
4642
5522
  state.set(id, "visiting");
4643
5523
  pathStack.push(id);
4644
5524
  const document = {
4645
5525
  id,
4646
5526
  markdown,
4647
5527
  source: local,
4648
- references: await readLocalReferences(path2.join(sourceRoot, id))
5528
+ references: await readLocalReferences(path4.join(sourceRoot, id))
4649
5529
  };
4650
5530
  for (const required of parseSkillRequirements(markdown, id)) {
4651
5531
  await visit(required, id);
@@ -4659,7 +5539,7 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
4659
5539
  }
4660
5540
  async function readSkillsState(baseDir) {
4661
5541
  try {
4662
- const raw = await fsp.readFile(path2.join(baseDir, SKILLS_STATE_FILE), "utf8");
5542
+ const raw = await fsp.readFile(path4.join(baseDir, SKILLS_STATE_FILE), "utf8");
4663
5543
  const parsed = JSON.parse(raw);
4664
5544
  if (!parsed || typeof parsed !== "object" || !parsed.skills) return void 0;
4665
5545
  return parsed;
@@ -4669,7 +5549,7 @@ async function readSkillsState(baseDir) {
4669
5549
  }
4670
5550
  async function writeSkillsState(baseDir, state) {
4671
5551
  await fsp.mkdir(baseDir, { recursive: true });
4672
- const file = path2.join(baseDir, SKILLS_STATE_FILE);
5552
+ const file = path4.join(baseDir, SKILLS_STATE_FILE);
4673
5553
  const temp = `${file}.${process.pid}.tmp`;
4674
5554
  await fsp.writeFile(temp, JSON.stringify(state, null, 2) + "\n", "utf8");
4675
5555
  await fsp.rename(temp, file);
@@ -4698,7 +5578,7 @@ async function upsertAgentsBlock(agentsFile, block) {
4698
5578
  existing = await fsp.readFile(agentsFile, "utf8");
4699
5579
  } catch (cause) {
4700
5580
  if (cause?.code !== "ENOENT") throw cause;
4701
- await fsp.mkdir(path2.dirname(agentsFile), { recursive: true });
5581
+ await fsp.mkdir(path4.dirname(agentsFile), { recursive: true });
4702
5582
  await fsp.writeFile(agentsFile, block + "\n", "utf8");
4703
5583
  return "created";
4704
5584
  }
@@ -4714,22 +5594,22 @@ async function upsertAgentsBlock(agentsFile, block) {
4714
5594
  return "appended";
4715
5595
  }
4716
5596
  async function installSkillDocuments(documents, requestedIds, baseDir, force, meta2) {
4717
- if (requestedIds.length === 0) throw invalid("\u6CA1\u6709\u6307\u5B9A\u8981\u88C5\u7684\u6280\u80FD");
4718
- for (const id of requestedIds) assertSafeSkillId(id);
5597
+ if (requestedIds.length === 0) throw invalid3("\u6CA1\u6709\u6307\u5B9A\u8981\u88C5\u7684\u6280\u80FD");
5598
+ for (const id of requestedIds) assertSafeSkillId2(id);
4719
5599
  const seen = /* @__PURE__ */ new Set();
4720
5600
  const candidates = documents.map((document) => {
4721
- assertSafeSkillId(document.id);
4722
- if (seen.has(document.id)) throw invalid(`\u5B89\u88C5\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
5601
+ assertSafeSkillId2(document.id);
5602
+ if (seen.has(document.id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
4723
5603
  seen.add(document.id);
4724
- if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
5604
+ if (!document.markdown.trim()) throw invalid3(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
4725
5605
  return {
4726
5606
  ...document,
4727
- target: path2.join(baseDir, document.id, "SKILL.md"),
5607
+ target: path4.join(baseDir, document.id, "SKILL.md"),
4728
5608
  bytes: Buffer.byteLength(document.markdown)
4729
5609
  };
4730
5610
  });
4731
5611
  for (const id of requestedIds) {
4732
- if (!seen.has(id)) throw invalid(`\u5B89\u88C5\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${id}`);
5612
+ if (!seen.has(id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${id}`);
4733
5613
  }
4734
5614
  const unchanged = /* @__PURE__ */ new Set();
4735
5615
  for (const candidate of candidates) {
@@ -4740,7 +5620,7 @@ async function installSkillDocuments(documents, requestedIds, baseDir, force, me
4740
5620
  unchanged.add(candidate.id);
4741
5621
  continue;
4742
5622
  }
4743
- throw invalid(
5623
+ throw invalid3(
4744
5624
  `${candidate.target} \u5DF2\u5B58\u5728\uFF0C\u4E14\u548C\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\u4E0D\u4E00\u81F4\u3002\u7528 --force \u8986\u76D6\uFF08\u4F1A\u4E22\u6389\u672C\u5730\u6539\u52A8\uFF09\uFF0C\u6216 --dir <path> \u88C5\u5230\u522B\u5904\uFF1B\u53EA\u60F3\u8DDF\u4E0A\u4EA7\u54C1\u7248\u672C\u7528 hcm skills update`
4745
5625
  );
4746
5626
  }
@@ -4775,7 +5655,7 @@ async function sameOnDisk(baseDir, candidate) {
4775
5655
  return true;
4776
5656
  }
4777
5657
  async function readLocalReferences(skillDir) {
4778
- const root = path2.join(skillDir, "references");
5658
+ const root = path4.join(skillDir, "references");
4779
5659
  const out = [];
4780
5660
  const walk = async (dir, prefix) => {
4781
5661
  let items;
@@ -4788,12 +5668,12 @@ async function readLocalReferences(skillDir) {
4788
5668
  for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
4789
5669
  const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
4790
5670
  if (item.isDirectory()) {
4791
- await walk(path2.join(dir, item.name), relative2);
5671
+ await walk(path4.join(dir, item.name), relative2);
4792
5672
  continue;
4793
5673
  }
4794
5674
  if (!item.isFile()) continue;
4795
5675
  assertSafeReferencePath(relative2);
4796
- out.push({ path: relative2, content: await fsp.readFile(path2.join(dir, item.name), "utf8") });
5676
+ out.push({ path: relative2, content: await fsp.readFile(path4.join(dir, item.name), "utf8") });
4797
5677
  }
4798
5678
  };
4799
5679
  await walk(root, "");
@@ -4803,15 +5683,15 @@ async function writeSkillDocuments(documents, baseDir) {
4803
5683
  const staged = [];
4804
5684
  try {
4805
5685
  for (const document of documents) {
4806
- const target = path2.join(baseDir, document.id, "SKILL.md");
4807
- await fsp.mkdir(path2.dirname(target), { recursive: true });
4808
- const temp = path2.join(path2.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
5686
+ const target = path4.join(baseDir, document.id, "SKILL.md");
5687
+ await fsp.mkdir(path4.dirname(target), { recursive: true });
5688
+ const temp = path4.join(path4.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
4809
5689
  await fsp.writeFile(temp, document.markdown, "utf8");
4810
5690
  staged.push({ temp, target });
4811
5691
  for (const reference of document.references ?? []) {
4812
5692
  assertSafeReferencePath(reference.path);
4813
- const referenceTarget = path2.join(baseDir, document.id, "references", reference.path);
4814
- await fsp.mkdir(path2.dirname(referenceTarget), { recursive: true });
5693
+ const referenceTarget = path4.join(baseDir, document.id, "references", reference.path);
5694
+ await fsp.mkdir(path4.dirname(referenceTarget), { recursive: true });
4815
5695
  const referenceTemp = `${referenceTarget}.${process.pid}.${Date.now()}.tmp`;
4816
5696
  await fsp.writeFile(referenceTemp, reference.content, "utf8");
4817
5697
  staged.push({ temp: referenceTemp, target: referenceTarget });
@@ -4840,24 +5720,24 @@ async function recordInstalled(baseDir, documents, meta2) {
4840
5720
  const previous = state.skills[document.id];
4841
5721
  state.skills[document.id] = {
4842
5722
  source: document.source,
4843
- sha256: sha256(document.markdown),
5723
+ sha256: sha2563(document.markdown),
4844
5724
  installedAt: meta2.keepInstalledAt?.has(document.id) && previous ? previous.installedAt : state.updatedAt,
4845
5725
  // 已经是直接安装的,不因为这次作为依赖被带进来就降级
4846
5726
  direct: meta2.directIds.has(document.id) || Boolean(previous?.direct),
4847
5727
  references: Object.fromEntries(
4848
- (document.references ?? []).map((reference) => [reference.path, sha256(reference.content)])
5728
+ (document.references ?? []).map((reference) => [reference.path, sha2563(reference.content)])
4849
5729
  )
4850
5730
  };
4851
5731
  }
4852
5732
  await writeSkillsState(baseDir, state);
4853
5733
  return state;
4854
5734
  }
4855
- function invalid(message) {
4856
- return new CliError14({ code: CliErrorCode13.INVALID_ARGUMENT, message });
5735
+ function invalid3(message) {
5736
+ return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
4857
5737
  }
4858
5738
  async function readInstalled(baseDir, id) {
4859
5739
  try {
4860
- return await fsp.readFile(path2.join(baseDir, id, "SKILL.md"), "utf8");
5740
+ return await fsp.readFile(path4.join(baseDir, id, "SKILL.md"), "utf8");
4861
5741
  } catch (cause) {
4862
5742
  if (cause?.code === "ENOENT") return void 0;
4863
5743
  throw cause;
@@ -4865,7 +5745,7 @@ async function readInstalled(baseDir, id) {
4865
5745
  }
4866
5746
  async function readInstalledReference(baseDir, id, relative2) {
4867
5747
  try {
4868
- return await fsp.readFile(path2.join(baseDir, id, "references", relative2), "utf8");
5748
+ return await fsp.readFile(path4.join(baseDir, id, "references", relative2), "utf8");
4869
5749
  } catch (cause) {
4870
5750
  if (cause?.code === "ENOENT") return void 0;
4871
5751
  throw cause;
@@ -4881,7 +5761,7 @@ async function planSkillUpdate(options) {
4881
5761
  const closure = /* @__PURE__ */ new Map();
4882
5762
  for (const id of wanted.sort()) {
4883
5763
  try {
4884
- for (const skill of resolveSkillInstallOrder(manifest2, id)) {
5764
+ for (const skill of resolveSkillInstallOrder2(manifest2, id)) {
4885
5765
  if (!closure.has(skill.id)) closure.set(skill.id, skill);
4886
5766
  }
4887
5767
  } catch {
@@ -4908,12 +5788,12 @@ async function planSkillUpdate(options) {
4908
5788
  const remote = await fetchDocument(skill);
4909
5789
  const remoteReferences = remote.references ?? [];
4910
5790
  let differsFromRemote = remote.markdown !== onDisk;
4911
- let editedLocally = record !== void 0 && sha256(onDisk) !== record.sha256;
5791
+ let editedLocally = record !== void 0 && sha2563(onDisk) !== record.sha256;
4912
5792
  for (const reference of remoteReferences) {
4913
5793
  const installed = await readInstalledReference(baseDir, skill.id, reference.path);
4914
5794
  if (installed !== reference.content) differsFromRemote = true;
4915
5795
  const recordedSha = record?.references?.[reference.path];
4916
- if (installed !== void 0 && recordedSha && sha256(installed) !== recordedSha) editedLocally = true;
5796
+ if (installed !== void 0 && recordedSha && sha2563(installed) !== recordedSha) editedLocally = true;
4917
5797
  }
4918
5798
  if (!differsFromRemote) {
4919
5799
  entries.push({ id: skill.id, status: "unchanged" });
@@ -4949,14 +5829,15 @@ async function skillsUpdateCommand(args) {
4949
5829
  const layout = await resolveInstallLayout(args);
4950
5830
  const state = await readSkillsState(layout.baseDir);
4951
5831
  if (!state || Object.keys(state.skills).length === 0) {
4952
- throw new CliError14({
4953
- code: CliErrorCode13.INVALID_ARGUMENT,
4954
- message: `${path2.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u6709\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002\u5148 \`hcm skills install <id>\`\uFF1B\u5982\u679C\u6280\u80FD\u662F\u624B\u5DE5\u62F7\u8FDB\u6765\u7684\uFF0C\u91CD\u88C5\u4E00\u6B21\u5373\u53EF\u63A5\u7BA1\u5347\u7EA7\u3002`
5832
+ throw new CliError16({
5833
+ code: CliErrorCode15.INVALID_ARGUMENT,
5834
+ message: `${path4.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u6709\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002\u5148 \`hcm skills install <id>\`\uFF1B\u5982\u679C\u6280\u80FD\u662F\u624B\u5DE5\u62F7\u8FDB\u6765\u7684\uFF0C\u91CD\u88C5\u4E00\u6B21\u5373\u53EF\u63A5\u7BA1\u5347\u7EA7\u3002`
4955
5835
  });
4956
5836
  }
4957
- const endpoint = args.endpoint ? args.endpoint.replace(/\/+$/, "") : await resolveEndpoint(args).catch((e) => {
4958
- if (state.endpoint) return state.endpoint;
4959
- throw e;
5837
+ const endpoint = decideTrackedEndpoint({
5838
+ explicit: Boolean(args.endpoint || args.env),
5839
+ current: await resolveEndpoint(args).catch(() => void 0),
5840
+ recorded: state.endpoint
4960
5841
  });
4961
5842
  const { http, base } = await chironClient({ ...args, endpoint });
4962
5843
  const manifest2 = await fetchSkillCatalog(http, base);
@@ -5034,8 +5915,8 @@ import {
5034
5915
  loadEnv as loadEnv6,
5035
5916
  loadGlobalConfig as loadGlobalConfig7,
5036
5917
  resolveActiveEnv as resolveActiveEnv4,
5037
- CliError as CliError15,
5038
- CliErrorCode as CliErrorCode14
5918
+ CliError as CliError17,
5919
+ CliErrorCode as CliErrorCode16
5039
5920
  } from "@hcmai/sdk";
5040
5921
  init_exit();
5041
5922
  async function resolveBootstrapKey(args, stdinTaken) {
@@ -5043,8 +5924,8 @@ async function resolveBootstrapKey(args, stdinTaken) {
5043
5924
  if (fromEnv) return fromEnv;
5044
5925
  if (args.bootstrapKeyStdin) {
5045
5926
  if (stdinTaken) {
5046
- throw new CliError15({
5047
- code: CliErrorCode14.INVALID_ARGUMENT,
5927
+ throw new CliError17({
5928
+ code: CliErrorCode16.INVALID_ARGUMENT,
5048
5929
  message: "--bootstrap-key-stdin \u4E0E --admin-password-stdin \u4E0D\u80FD\u540C\u65F6\u7528\uFF08stdin \u53EA\u6709\u4E00\u8DEF\uFF09\uFF1B\u628A\u5176\u4E2D\u4E00\u4E2A\u6539\u8D70 HCM_BOOTSTRAP_KEY \u73AF\u5883\u53D8\u91CF\u6216\u4EA4\u4E92\u8F93\u5165"
5049
5930
  });
5050
5931
  }
@@ -5074,8 +5955,8 @@ async function resolveEndpoint2(args) {
5074
5955
  const envName = resolveActiveEnv4({ envFlag: args.env }, process.env, g);
5075
5956
  const env2 = await loadEnv6(envName).catch(() => null);
5076
5957
  if (!env2?.endpoint) {
5077
- throw new CliError15({
5078
- code: CliErrorCode14.MISSING_FLAG,
5958
+ throw new CliError17({
5959
+ code: CliErrorCode16.MISSING_FLAG,
5079
5960
  message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`"
5080
5961
  });
5081
5962
  }
@@ -5084,8 +5965,8 @@ async function resolveEndpoint2(args) {
5084
5965
  async function tenantBootstrapCommand(args) {
5085
5966
  try {
5086
5967
  if (!args.tenantId || !args.adminUsername) {
5087
- throw new CliError15({
5088
- code: CliErrorCode14.MISSING_FLAG,
5968
+ throw new CliError17({
5969
+ code: CliErrorCode16.MISSING_FLAG,
5089
5970
  message: "--tenant-id \u4E0E --admin-username \u5FC5\u586B"
5090
5971
  });
5091
5972
  }
@@ -5107,7 +5988,7 @@ async function tenantBootstrapCommand(args) {
5107
5988
  onRefresh: async () => {
5108
5989
  }
5109
5990
  });
5110
- const result = await bootstrapTenant(http, key, {
5991
+ const result2 = await bootstrapTenant(http, key, {
5111
5992
  tenantId: args.tenantId,
5112
5993
  adminUsername: args.adminUsername,
5113
5994
  adminPassword: pwd,
@@ -5116,12 +5997,12 @@ async function tenantBootstrapCommand(args) {
5116
5997
  const fmt = args.output ?? "json";
5117
5998
  if (fmt !== "json") {
5118
5999
  process.stdout.write(
5119
- `\u2705 \u79DF\u6237 ${result.tenantId} \u5DF2\u5F00\u901A\uFF08adminUserId ${result.adminUserId ?? "-"}\uFF09
6000
+ `\u2705 \u79DF\u6237 ${result2.tenantId} \u5DF2\u5F00\u901A\uFF08adminUserId ${result2.adminUserId ?? "-"}\uFF09
5120
6001
  \u4E0B\u4E00\u6B65\uFF1Ahcm env add <name> --endpoint ${endpoint} && hcm login --env <name>
5121
6002
  `
5122
6003
  );
5123
6004
  }
5124
- process.stdout.write(formatObject14(result, { format: fmt }) + "\n");
6005
+ process.stdout.write(formatObject14(result2, { format: fmt }) + "\n");
5125
6006
  process.exit(0);
5126
6007
  } catch (e) {
5127
6008
  const err = e;
@@ -5145,8 +6026,8 @@ import {
5145
6026
  getTenantMeta,
5146
6027
  saveTenantMeta,
5147
6028
  deleteTenantMeta,
5148
- CliError as CliError16,
5149
- CliErrorCode as CliErrorCode15
6029
+ CliError as CliError18,
6030
+ CliErrorCode as CliErrorCode17
5150
6031
  } from "@hcmai/sdk";
5151
6032
  init_exit();
5152
6033
  init_auth_guard();
@@ -5182,21 +6063,21 @@ async function metaListCommand(args) {
5182
6063
  fail4(e, args);
5183
6064
  }
5184
6065
  }
5185
- async function metaGetCommand(path4, args) {
6066
+ async function metaGetCommand(path7, args) {
5186
6067
  try {
5187
6068
  const c = await client2(args);
5188
- const content = await getTenantMeta(c.raw(), path4);
6069
+ const content = await getTenantMeta(c.raw(), path7);
5189
6070
  process.stdout.write(content.endsWith("\n") ? content : content + "\n");
5190
6071
  process.exit(0);
5191
6072
  } catch (e) {
5192
6073
  fail4(e, args);
5193
6074
  }
5194
6075
  }
5195
- async function metaSetCommand(path4, args) {
6076
+ async function metaSetCommand(path7, args) {
5196
6077
  try {
5197
6078
  if (!args.file) {
5198
- throw new CliError16({
5199
- code: CliErrorCode15.MISSING_FLAG,
6079
+ throw new CliError18({
6080
+ code: CliErrorCode17.MISSING_FLAG,
5200
6081
  message: "--file <path> \u5FC5\u586B\uFF08\u4ECE\u6587\u4EF6\u8BFB\u53D6 meta \u539F\u6587\uFF1B\u7528 - \u8868\u793A stdin\uFF09"
5201
6082
  });
5202
6083
  }
@@ -5209,7 +6090,7 @@ async function metaSetCommand(path4, args) {
5209
6090
  }) : await fsp2.readFile(args.file, "utf8");
5210
6091
  if (args.dryRun) {
5211
6092
  process.stdout.write(
5212
- `[dry-run] \u5C06\u5199\u5165 ${path4}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
6093
+ `[dry-run] \u5C06\u5199\u5165 ${path7}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
5213
6094
  ---
5214
6095
  ${content}
5215
6096
  `
@@ -5217,35 +6098,35 @@ ${content}
5217
6098
  process.exit(0);
5218
6099
  }
5219
6100
  const c = await client2(args);
5220
- const result = await saveTenantMeta(c.raw(), path4, content);
6101
+ const result2 = await saveTenantMeta(c.raw(), path7, content);
5221
6102
  const fmt = args.output ?? "json";
5222
6103
  if (fmt !== "json") {
5223
- process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path4}
6104
+ process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path7}
5224
6105
  \u8BB0\u5F97\u6E05\u7F13\u5B58\uFF1Ahcm cache clear-meta <Model>
5225
6106
  `);
5226
6107
  }
5227
- process.stdout.write(formatObject15(result, { format: fmt }) + "\n");
6108
+ process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
5228
6109
  process.exit(0);
5229
6110
  } catch (e) {
5230
6111
  if (e?.code === "ENOENT") exitWithError(new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${args.file}`));
5231
6112
  fail4(e, args);
5232
6113
  }
5233
6114
  }
5234
- async function metaDeleteCommand(path4, args) {
6115
+ async function metaDeleteCommand(path7, args) {
5235
6116
  try {
5236
6117
  if (!args.yes) {
5237
- const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path4}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
6118
+ const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path7}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
5238
6119
  if (!ok) {
5239
6120
  process.stderr.write("\u5DF2\u53D6\u6D88\n");
5240
6121
  process.exit(1);
5241
6122
  }
5242
6123
  }
5243
6124
  const c = await client2(args);
5244
- const result = await deleteTenantMeta(c.raw(), path4);
6125
+ const result2 = await deleteTenantMeta(c.raw(), path7);
5245
6126
  const fmt = args.output ?? "json";
5246
- if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path4}
6127
+ if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path7}
5247
6128
  `);
5248
- process.stdout.write(formatObject15(result, { format: fmt }) + "\n");
6129
+ process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
5249
6130
  process.exit(0);
5250
6131
  } catch (e) {
5251
6132
  fail4(e, args);
@@ -5256,8 +6137,8 @@ async function metaDeleteCommand(path4, args) {
5256
6137
  init_exit();
5257
6138
  init_auth_guard();
5258
6139
  import * as crypto from "crypto";
5259
- import * as fs4 from "fs/promises";
5260
- import * as path3 from "path";
6140
+ import * as fs6 from "fs/promises";
6141
+ import * as path5 from "path";
5261
6142
  import {
5262
6143
  buildMiniPreviewTargets,
5263
6144
  buildMiniVerifyAgentReport,
@@ -5326,7 +6207,7 @@ async function workspaceListCommand(args) {
5326
6207
  try {
5327
6208
  const client3 = HcmClient16.fromAuthContext(await getActiveAuthContextFromCli(args));
5328
6209
  const limit = parsePositiveInt3(args.limit, 50, "--limit");
5329
- const result = await client3.query("TenantExtensionWorkspace", {
6210
+ const result2 = await client3.query("TenantExtensionWorkspace", {
5330
6211
  keyword: args.keyword,
5331
6212
  fields: [
5332
6213
  "workspaceKey",
@@ -5341,7 +6222,7 @@ async function workspaceListCommand(args) {
5341
6222
  sort: [{ field: "devModeEnabled", desc: true }, { field: "updateTime", desc: true }],
5342
6223
  limit
5343
6224
  });
5344
- writeWorkspaceListResult(result.rows, args.output ?? "table", result.total);
6225
+ writeWorkspaceListResult(result2.rows, args.output ?? "table", result2.total);
5345
6226
  process.exit(0);
5346
6227
  } catch (e) {
5347
6228
  exitWorkspaceError(e, args.env ?? args.profile);
@@ -5355,12 +6236,12 @@ async function workspaceActiveCommand(args) {
5355
6236
  fallbackEnv: localContext?.profile
5356
6237
  });
5357
6238
  const client3 = HcmClient16.fromAuthContext(ctx);
5358
- const result = await client3.query("TenantExtensionWorkspace", {
6239
+ const result2 = await client3.query("TenantExtensionWorkspace", {
5359
6240
  filter: { devModeEnabled: true },
5360
6241
  fields: ["workspaceKey", "devModeEnabled"],
5361
6242
  limit: 1
5362
6243
  });
5363
- const activeWorkspaceKey = optionalText(result.rows[0]?.workspaceKey) ?? null;
6244
+ const activeWorkspaceKey = optionalText(result2.rows[0]?.workspaceKey) ?? null;
5364
6245
  const view = {
5365
6246
  activeWorkspaceKey,
5366
6247
  localWorkspaceKey: localContext?.workspaceKey ?? null,
@@ -5392,7 +6273,7 @@ async function workspaceBindCommand(workspaceKey, args) {
5392
6273
  tenantId: args.tenant ?? client3?.tenantId,
5393
6274
  endpoint: args.endpoint ?? client3?.endpoint
5394
6275
  });
5395
- await fs4.mkdir(rootDir, { recursive: true });
6276
+ await fs6.mkdir(rootDir, { recursive: true });
5396
6277
  await writeWorkspaceContext(rootDir, context);
5397
6278
  writeWorkspaceBindResult(context, rootDir, args.output ?? "table");
5398
6279
  process.exit(0);
@@ -5427,7 +6308,7 @@ async function workspaceCreateCommand(workspaceKey, args) {
5427
6308
  tenantId: client3.tenantId,
5428
6309
  endpoint: client3.endpoint
5429
6310
  });
5430
- await fs4.mkdir(rootDir, { recursive: true });
6311
+ await fs6.mkdir(rootDir, { recursive: true });
5431
6312
  await writeWorkspaceContext(rootDir, context);
5432
6313
  writeWorkspaceCreateResult(
5433
6314
  {
@@ -5464,23 +6345,23 @@ async function workspacePullCommand(args) {
5464
6345
  tenantId: client3.tenantId ?? existing?.tenantId,
5465
6346
  endpoint: client3.endpoint ?? existing?.endpoint
5466
6347
  });
5467
- await fs4.mkdir(rootDir, { recursive: true });
6348
+ await fs6.mkdir(rootDir, { recursive: true });
5468
6349
  await writeWorkspaceContext(rootDir, context);
5469
6350
  const files = await listWorkspaceFiles(client3.raw(), workspaceKey, { pageSize: 5e3 });
5470
6351
  const miniApps = detectRemoteMiniApps(files.data);
5471
6352
  const pulledMiniApps = [];
5472
6353
  for (const appCode of miniApps) {
5473
- const result = await pullMiniAppProject({
6354
+ const result2 = await pullMiniAppProject({
5474
6355
  http: client3.raw(),
5475
6356
  appCode,
5476
6357
  workspaceKey,
5477
- targetDir: path3.join(rootDir, MINI_APPS_DIR, appCode),
6358
+ targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
5478
6359
  tenantId: client3.tenantId,
5479
6360
  endpoint: client3.endpoint,
5480
6361
  profile: args.profile ?? client3.profile,
5481
6362
  force: !!args.force
5482
6363
  });
5483
- pulledMiniApps.push(result);
6364
+ pulledMiniApps.push(result2);
5484
6365
  }
5485
6366
  const rawFiles = files.data.filter(
5486
6367
  (file) => !isDirectoryMarker(file.relativePath) && !isMiniAppPath(file.relativePath)
@@ -5489,7 +6370,7 @@ async function workspacePullCommand(args) {
5489
6370
  const rawRemoteByLocalPath = /* @__PURE__ */ new Map();
5490
6371
  for (const file of rawFiles) {
5491
6372
  const content = await loadWorkspaceFileContent(client3.raw(), file.id);
5492
- const target = path3.join(rootDir, content.relativePath);
6373
+ const target = path5.join(rootDir, content.relativePath);
5493
6374
  await writeTextFile(target, content.content, !!args.force);
5494
6375
  pulledRawFiles.push(content.relativePath);
5495
6376
  rawRemoteByLocalPath.set(content.relativePath, file);
@@ -5536,10 +6417,10 @@ async function workspaceAddMiniCommand(appCode, args) {
5536
6417
  fields: args.fields,
5537
6418
  defaults: args.defaults
5538
6419
  });
5539
- const result = await initMiniAppProject({
6420
+ const result2 = await initMiniAppProject({
5540
6421
  appCode,
5541
6422
  workspaceKey: context.workspaceKey,
5542
- targetDir: path3.join(rootDir, MINI_APPS_DIR, appCode),
6423
+ targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
5543
6424
  name: args.name,
5544
6425
  defaultSurface: parseSurface(args.surface),
5545
6426
  templateKind,
@@ -5553,7 +6434,7 @@ async function workspaceAddMiniCommand(appCode, args) {
5553
6434
  profile: args.env ?? args.profile ?? client3?.profile ?? context.profile,
5554
6435
  force: !!args.force
5555
6436
  });
5556
- writeWorkspaceAddMiniResult(result, rootDir, args.output ?? "table");
6437
+ writeWorkspaceAddMiniResult(result2, rootDir, args.output ?? "table");
5557
6438
  process.exit(0);
5558
6439
  } catch (e) {
5559
6440
  exitWorkspaceError(e, args.env ?? args.profile);
@@ -5633,12 +6514,12 @@ async function workspacePushCommand(args) {
5633
6514
  });
5634
6515
  const results = [];
5635
6516
  for (const app of miniApps) {
5636
- const result = await pushMiniAppProject({
6517
+ const result2 = await pushMiniAppProject({
5637
6518
  http: client3.raw(),
5638
6519
  rootDir: app.dir,
5639
6520
  dryRun: !!args.dryRun
5640
6521
  });
5641
- results.push({ app, result });
6522
+ results.push({ app, result: result2 });
5642
6523
  }
5643
6524
  writeWorkspacePushResult(rawResult, results, args.output ?? "table");
5644
6525
  process.exit(
@@ -5780,7 +6661,7 @@ function removedMiniCommand() {
5780
6661
  );
5781
6662
  }
5782
6663
  function resolveRootDir(dir) {
5783
- return path3.resolve(dir ?? ".");
6664
+ return path5.resolve(dir ?? ".");
5784
6665
  }
5785
6666
  function buildWorkspaceContext(args) {
5786
6667
  return {
@@ -5796,7 +6677,7 @@ function buildWorkspaceContext(args) {
5796
6677
  async function readWorkspaceContext(rootDir) {
5797
6678
  try {
5798
6679
  const parsed = JSON.parse(
5799
- await fs4.readFile(path3.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
6680
+ await fs6.readFile(path5.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
5800
6681
  );
5801
6682
  if (parsed.kind !== WORKSPACE_CONTEXT_KIND || parsed.version !== WORKSPACE_CONTEXT_VERSION) {
5802
6683
  throw new Error(`${WORKSPACE_CONTEXT_FILE} is not an HCM workspace context`);
@@ -5825,25 +6706,25 @@ async function requireWorkspaceContext(rootDir) {
5825
6706
  return context;
5826
6707
  }
5827
6708
  async function writeWorkspaceContext(rootDir, context) {
5828
- await fs4.writeFile(
5829
- path3.join(rootDir, WORKSPACE_CONTEXT_FILE),
6709
+ await fs6.writeFile(
6710
+ path5.join(rootDir, WORKSPACE_CONTEXT_FILE),
5830
6711
  JSON.stringify(context, null, 2) + "\n"
5831
6712
  );
5832
6713
  }
5833
6714
  async function writeTextFile(file, content, force) {
5834
6715
  try {
5835
- await fs4.access(file);
6716
+ await fs6.access(file);
5836
6717
  if (!force) throw new Error(`${file} already exists; use --force to overwrite`);
5837
6718
  } catch (e) {
5838
6719
  if (e.code !== "ENOENT") throw e;
5839
6720
  }
5840
- await fs4.mkdir(path3.dirname(file), { recursive: true });
5841
- await fs4.writeFile(file, content);
6721
+ await fs6.mkdir(path5.dirname(file), { recursive: true });
6722
+ await fs6.writeFile(file, content);
5842
6723
  }
5843
6724
  async function readWorkspaceRawManifest(rootDir, context) {
5844
6725
  try {
5845
6726
  const parsed = JSON.parse(
5846
- await fs4.readFile(path3.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
6727
+ await fs6.readFile(path5.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
5847
6728
  );
5848
6729
  if (parsed.version !== WORKSPACE_CONTEXT_VERSION || parsed.workspaceKey !== context.workspaceKey) {
5849
6730
  throw new Error(`${WORKSPACE_PULL_MANIFEST_FILE} does not match workspace ${context.workspaceKey}`);
@@ -5861,8 +6742,8 @@ async function readWorkspaceRawManifest(rootDir, context) {
5861
6742
  }
5862
6743
  }
5863
6744
  async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath) {
5864
- const stateDir = path3.join(rootDir, WORKSPACE_STATE_DIR);
5865
- await fs4.mkdir(stateDir, { recursive: true });
6745
+ const stateDir = path5.join(rootDir, WORKSPACE_STATE_DIR);
6746
+ await fs6.mkdir(stateDir, { recursive: true });
5866
6747
  const snapshots = files.map((file) => ({
5867
6748
  localPath: file.localPath,
5868
6749
  remotePath: file.localPath,
@@ -5871,8 +6752,8 @@ async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath
5871
6752
  size: file.size,
5872
6753
  lastModified: remoteByLocalPath.get(file.localPath)?.lastModified
5873
6754
  }));
5874
- await fs4.writeFile(
5875
- path3.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
6755
+ await fs6.writeFile(
6756
+ path5.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
5876
6757
  JSON.stringify(
5877
6758
  {
5878
6759
  version: WORKSPACE_CONTEXT_VERSION,
@@ -5894,10 +6775,10 @@ async function readWorkspaceRawLocalFiles(rootDir) {
5894
6775
  return files;
5895
6776
  }
5896
6777
  async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
5897
- const absoluteDir = path3.join(rootDir, relativeDir);
6778
+ const absoluteDir = path5.join(rootDir, relativeDir);
5898
6779
  let entries;
5899
6780
  try {
5900
- entries = await fs4.readdir(absoluteDir, { withFileTypes: true });
6781
+ entries = await fs6.readdir(absoluteDir, { withFileTypes: true });
5901
6782
  } catch (e) {
5902
6783
  if (e.code === "ENOENT") return;
5903
6784
  throw e;
@@ -5905,19 +6786,19 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
5905
6786
  for (const entry of entries) {
5906
6787
  if (entry.isDirectory() && RAW_WORKSPACE_EXCLUDED_DIRS.has(entry.name)) continue;
5907
6788
  if (entry.isFile() && RAW_WORKSPACE_EXCLUDED_FILES.has(entry.name)) continue;
5908
- const localPath = normalizeRelativePath(path3.posix.join(toPosix(relativeDir), entry.name));
5909
- const absolutePath = path3.join(rootDir, localPath);
6789
+ const localPath = normalizeRelativePath(path5.posix.join(toPosix(relativeDir), entry.name));
6790
+ const absolutePath = path5.join(rootDir, localPath);
5910
6791
  if (entry.isDirectory()) {
5911
6792
  await walkWorkspaceRawFiles(rootDir, localPath, files);
5912
6793
  continue;
5913
6794
  }
5914
6795
  if (!entry.isFile()) continue;
5915
- const content = await fs4.readFile(absolutePath, "utf-8");
6796
+ const content = await fs6.readFile(absolutePath, "utf-8");
5916
6797
  files.push({
5917
6798
  localPath,
5918
6799
  absolutePath,
5919
6800
  content,
5920
- sha256: sha2562(content),
6801
+ sha256: sha2564(content),
5921
6802
  size: Buffer.byteLength(content, "utf-8")
5922
6803
  });
5923
6804
  }
@@ -5931,13 +6812,13 @@ async function pushWorkspaceRawFiles(args) {
5931
6812
  const localFiles = await readWorkspaceRawLocalFiles(args.rootDir);
5932
6813
  const remoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
5933
6814
  const plan = buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles);
5934
- const result = {
6815
+ const result2 = {
5935
6816
  workspaceKey: args.context.workspaceKey,
5936
6817
  dryRun: args.dryRun,
5937
6818
  operations: plan.operations,
5938
6819
  conflicts: plan.conflicts
5939
6820
  };
5940
- if (args.dryRun || plan.conflicts.length) return result;
6821
+ if (args.dryRun || plan.conflicts.length) return result2;
5941
6822
  const localByPath = new Map(localFiles.map((file) => [file.localPath, file]));
5942
6823
  for (const operation of plan.operations) {
5943
6824
  if (operation.operation === "skip") continue;
@@ -5963,17 +6844,17 @@ async function pushWorkspaceRawFiles(args) {
5963
6844
  const latestRemoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
5964
6845
  const latestRemoteByLocalPath = new Map(latestRemoteFiles.map((file) => [file.item.relativePath, file.item]));
5965
6846
  await writeWorkspaceRawState(args.rootDir, args.context, localFiles, latestRemoteByLocalPath);
5966
- return result;
6847
+ return result2;
5967
6848
  }
5968
6849
  async function loadWorkspaceRawRemoteFiles(http, workspaceKey) {
5969
6850
  const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5e3 });
5970
- const result = [];
6851
+ const result2 = [];
5971
6852
  for (const item of list.data) {
5972
6853
  if (isDirectoryMarker(item.relativePath) || isMiniAppPath(item.relativePath)) continue;
5973
6854
  const content = await loadWorkspaceFileContent(http, item.id);
5974
- result.push({ item, content: content.content });
6855
+ result2.push({ item, content: content.content });
5975
6856
  }
5976
- return result;
6857
+ return result2;
5977
6858
  }
5978
6859
  function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
5979
6860
  const status = computeWorkspaceRawStatus(manifest2, localFiles);
@@ -5984,7 +6865,7 @@ function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
5984
6865
  for (const entry of status) {
5985
6866
  const remote = remoteByLocal.get(entry.localPath);
5986
6867
  const base = baseByLocal.get(entry.localPath);
5987
- const remoteSha = remote ? sha2562(remote.content) : void 0;
6868
+ const remoteSha = remote ? sha2564(remote.content) : void 0;
5988
6869
  if (entry.status === "added") {
5989
6870
  if (remote) {
5990
6871
  conflicts.push(workspaceRawConflict(entry, "remote file already exists but was not in local snapshot"));
@@ -6086,15 +6967,15 @@ function workspaceRawConflict(entry, reason) {
6086
6967
  };
6087
6968
  }
6088
6969
  async function discoverMiniApps(rootDir) {
6089
- const appsRoot = path3.join(rootDir, MINI_APPS_DIR);
6970
+ const appsRoot = path5.join(rootDir, MINI_APPS_DIR);
6090
6971
  let entries;
6091
6972
  try {
6092
- entries = await fs4.readdir(appsRoot, { withFileTypes: true });
6973
+ entries = await fs6.readdir(appsRoot, { withFileTypes: true });
6093
6974
  } catch (e) {
6094
6975
  if (e.code === "ENOENT") return [];
6095
6976
  throw e;
6096
6977
  }
6097
- return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir: path3.join(appsRoot, entry.name) })).sort((a, b) => a.appCode.localeCompare(b.appCode));
6978
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir: path5.join(appsRoot, entry.name) })).sort((a, b) => a.appCode.localeCompare(b.appCode));
6098
6979
  }
6099
6980
  function detectRemoteMiniApps(files) {
6100
6981
  const appCodes = /* @__PURE__ */ new Set();
@@ -6183,17 +7064,17 @@ function parseModelKey(value) {
6183
7064
  }
6184
7065
  return model;
6185
7066
  }
6186
- function parseJsonObject(value, flagName) {
7067
+ function parseJsonObject(value, flagName2) {
6187
7068
  if (!value) return void 0;
6188
7069
  if (isAuto(value)) return void 0;
6189
7070
  let parsed;
6190
7071
  try {
6191
7072
  parsed = JSON.parse(value);
6192
7073
  } catch {
6193
- throw new Error(`${flagName} must be valid JSON object`);
7074
+ throw new Error(`${flagName2} must be valid JSON object`);
6194
7075
  }
6195
7076
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
6196
- throw new Error(`${flagName} must be valid JSON object`);
7077
+ throw new Error(`${flagName2} must be valid JSON object`);
6197
7078
  }
6198
7079
  return parsed;
6199
7080
  }
@@ -6247,9 +7128,9 @@ function normalizeRelativePath(value) {
6247
7128
  return normalized;
6248
7129
  }
6249
7130
  function toPosix(value) {
6250
- return value.split(path3.sep).join("/");
7131
+ return value.split(path5.sep).join("/");
6251
7132
  }
6252
- function sha2562(content) {
7133
+ function sha2564(content) {
6253
7134
  return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
6254
7135
  }
6255
7136
  function stringRequired(value, field) {
@@ -6316,52 +7197,52 @@ function writeWorkspaceBindResult(context, rootDir, format) {
6316
7197
  ].join("\n") + "\n"
6317
7198
  );
6318
7199
  }
6319
- function writeWorkspaceCreateResult(result, format) {
7200
+ function writeWorkspaceCreateResult(result2, format) {
6320
7201
  if (format !== "table") {
6321
- process.stdout.write(formatObject16(result, { format }) + "\n");
7202
+ process.stdout.write(formatObject16(result2, { format }) + "\n");
6322
7203
  return;
6323
7204
  }
6324
7205
  process.stdout.write(
6325
7206
  [
6326
- `workspace created: ${result.workspaceKey}`,
6327
- `id: ${result.id}`,
6328
- `directory: ${result.rootDir}`,
6329
- `profile: ${result.profile}`,
6330
- `tenant: ${result.tenantId}`,
6331
- `activated: ${result.activated ? "yes" : "no"}`
7207
+ `workspace created: ${result2.workspaceKey}`,
7208
+ `id: ${result2.id}`,
7209
+ `directory: ${result2.rootDir}`,
7210
+ `profile: ${result2.profile}`,
7211
+ `tenant: ${result2.tenantId}`,
7212
+ `activated: ${result2.activated ? "yes" : "no"}`
6332
7213
  ].join("\n") + "\n"
6333
7214
  );
6334
7215
  }
6335
- function writeWorkspacePullResult(result, format) {
7216
+ function writeWorkspacePullResult(result2, format) {
6336
7217
  if (format !== "table") {
6337
- process.stdout.write(formatObject16(result, { format }) + "\n");
7218
+ process.stdout.write(formatObject16(result2, { format }) + "\n");
6338
7219
  return;
6339
7220
  }
6340
7221
  const lines = [
6341
- `workspace pulled: ${result.workspaceKey}`,
6342
- `directory: ${result.rootDir}`,
6343
- `remote files: ${result.totalRemoteFiles}`,
6344
- `mini apps: ${result.miniApps.length}`,
6345
- `raw files: ${result.rawFiles.length}`
7222
+ `workspace pulled: ${result2.workspaceKey}`,
7223
+ `directory: ${result2.rootDir}`,
7224
+ `remote files: ${result2.totalRemoteFiles}`,
7225
+ `mini apps: ${result2.miniApps.length}`,
7226
+ `raw files: ${result2.rawFiles.length}`
6346
7227
  ];
6347
- if (result.miniApps.length) {
7228
+ if (result2.miniApps.length) {
6348
7229
  lines.push("mini artifacts:");
6349
- for (const app of result.miniApps) lines.push(` - ${app.appCode}: ${app.files} files`);
7230
+ for (const app of result2.miniApps) lines.push(` - ${app.appCode}: ${app.files} files`);
6350
7231
  }
6351
7232
  process.stdout.write(lines.join("\n") + "\n");
6352
7233
  }
6353
- function writeWorkspaceAddMiniResult(result, rootDir, format) {
7234
+ function writeWorkspaceAddMiniResult(result2, rootDir, format) {
6354
7235
  if (format !== "table") {
6355
- process.stdout.write(formatObject16({ ...result, workspaceRoot: rootDir }, { format }) + "\n");
7236
+ process.stdout.write(formatObject16({ ...result2, workspaceRoot: rootDir }, { format }) + "\n");
6356
7237
  return;
6357
7238
  }
6358
7239
  process.stdout.write(
6359
7240
  [
6360
- `workspace artifact added: mini:${result.appCode}`,
6361
- `workspace: ${result.workspaceKey}`,
7241
+ `workspace artifact added: mini:${result2.appCode}`,
7242
+ `workspace: ${result2.workspaceKey}`,
6362
7243
  `workspace root: ${rootDir}`,
6363
- `directory: ${result.targetDir}`,
6364
- `files: ${result.files.length}`
7244
+ `directory: ${result2.targetDir}`,
7245
+ `files: ${result2.files.length}`
6365
7246
  ].join("\n") + "\n"
6366
7247
  );
6367
7248
  }
@@ -6546,8 +7427,8 @@ import {
6546
7427
  deleteIdentity as deleteIdentity2,
6547
7428
  loadGlobalConfig as loadGlobalConfig8,
6548
7429
  TokenStore as TokenStore3,
6549
- CliError as CliError17,
6550
- CliErrorCode as CliErrorCode16
7430
+ CliError as CliError19,
7431
+ CliErrorCode as CliErrorCode18
6551
7432
  } from "@hcmai/sdk";
6552
7433
  async function identityList(args) {
6553
7434
  try {
@@ -6575,8 +7456,8 @@ async function identityUse(name) {
6575
7456
  try {
6576
7457
  const meta2 = await loadIdentity4(name);
6577
7458
  const env2 = await loadEnv7(meta2.env).catch(() => {
6578
- throw new CliError17({
6579
- code: CliErrorCode16.IDENTITY_NOT_IN_ENV,
7459
+ throw new CliError19({
7460
+ code: CliErrorCode18.IDENTITY_NOT_IN_ENV,
6580
7461
  message: `identity '${name}' references env '${meta2.env}' which no longer exists. Re-create the env, or remove this orphan: hcm identity remove ${name}`
6581
7462
  });
6582
7463
  });
@@ -6610,6 +7491,768 @@ async function identityRemove(name) {
6610
7491
  }
6611
7492
  }
6612
7493
 
7494
+ // src/commands/delivery.ts
7495
+ import { createHash as createHash5 } from "crypto";
7496
+ import { formatObject as formatObject17 } from "@hcmai/sdk";
7497
+ var APPROVAL_BYPASS_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--force", "--auto-approve"]);
7498
+ var CREDENTIAL_FLAGS = /* @__PURE__ */ new Set([
7499
+ "--password",
7500
+ "--password-stdin",
7501
+ "--client-secret",
7502
+ "--pat",
7503
+ "--pat-stdin",
7504
+ "--new-password",
7505
+ "--new-password-stdin"
7506
+ ]);
7507
+ var PAYLOAD_FLAGS = ["--data", "--params"];
7508
+ var GUARDED_EXECUTOR_REASON_CODES = /* @__PURE__ */ new Set([
7509
+ "record-create",
7510
+ "record-update",
7511
+ "cache-mutation",
7512
+ "setting-write"
7513
+ ]);
7514
+ function assessDeliveryCommand(inputArgv) {
7515
+ const argv = inputArgv[0] === "--" ? inputArgv.slice(1) : [...inputArgv];
7516
+ const commandSha256 = sha2565(JSON.stringify(argv));
7517
+ const name = argv[0] ?? "";
7518
+ const subcommand = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
7519
+ const envAlias = flagValue(argv, "--env");
7520
+ const identityAlias = flagValue(argv, "--as");
7521
+ const sensitiveFlags = [...new Set(PAYLOAD_FLAGS.filter((flag) => hasFlag(argv, flag)))];
7522
+ const target = summarizeTarget(argv);
7523
+ let classification = classify(argv);
7524
+ const invalidPayload = payloadError(argv);
7525
+ if (invalidPayload) {
7526
+ classification = forbidden("invalid-input", "JSON payload \u65E0\u6548\uFF0C\u4E0D\u80FD\u8FDB\u5165\u4EFB\u4F55\u6267\u884C\u8DEF\u5F84\u3002");
7527
+ }
7528
+ const requirements = [];
7529
+ const guardedExecutionAvailable = classification.risk === "R2" && GUARDED_EXECUTOR_REASON_CODES.has(classification.reasonCode);
7530
+ if (classification.risk === "R2") {
7531
+ if (!envAlias) requirements.push("explicit-env");
7532
+ if (!identityAlias) requirements.push("explicit-identity");
7533
+ if (guardedExecutionAvailable) {
7534
+ requirements.push("change-plan", "rollback-plan", "human-approval", "single-use-plan");
7535
+ } else {
7536
+ requirements.push("guarded-executor");
7537
+ }
7538
+ } else if (classification.risk === "R3") {
7539
+ requirements.push("governance-escalation");
7540
+ }
7541
+ const allowed = classification.risk === "R0" || classification.risk === "R1";
7542
+ return {
7543
+ schemaVersion: 1,
7544
+ contractVersion: "2026-08-28",
7545
+ assessmentId: sha2565(`2026-08-28
7546
+ ${commandSha256}`).slice(0, 24),
7547
+ commandSha256,
7548
+ command: { name: name || "(empty)", ...subcommand ? { subcommand } : {} },
7549
+ ...classification,
7550
+ guardedExecutionAvailable,
7551
+ environment: {
7552
+ ...envAlias ? { envAlias } : {},
7553
+ ...identityAlias ? { identityAlias } : {}
7554
+ },
7555
+ target,
7556
+ requirements,
7557
+ evidenceRequirements: evidenceFor(classification.risk),
7558
+ sensitiveFlags,
7559
+ modePolicy: {
7560
+ planOnly: allowed ? "allow" : "deny",
7561
+ controlled: allowed || guardedExecutionAvailable ? "allow" : "deny"
7562
+ }
7563
+ };
7564
+ }
7565
+ function deliveryAssessCommand(argv, args) {
7566
+ const assessment = assessDeliveryCommand(argv);
7567
+ process.stdout.write(formatObject17(assessment, { format: args.output ?? "json" }) + "\n");
7568
+ }
7569
+ function classify(argv) {
7570
+ const name = argv[0] ?? "";
7571
+ const subcommand = argv[1] ?? "";
7572
+ if (argv.some((arg) => APPROVAL_BYPASS_FLAGS.has(flagName(arg)))) {
7573
+ return forbidden("approval-bypass", "\u547D\u4EE4\u5305\u542B\u8DF3\u8FC7\u786E\u8BA4\u7684\u65D7\u6807\uFF0C\u987E\u95EE\u4EA4\u4ED8\u9762\u4E0D\u6267\u884C\u3002");
7574
+ }
7575
+ if (argv.some((arg) => CREDENTIAL_FLAGS.has(flagName(arg)))) {
7576
+ return forbidden("credential-governance", "\u547D\u4EE4\u643A\u5E26\u8EAB\u4EFD\u51ED\u636E\u53C2\u6570\uFF0C\u5FC5\u987B\u8F6C\u4EA4\u8EAB\u4EFD\u6CBB\u7406\u6D41\u7A0B\u3002");
7577
+ }
7578
+ if (name === "version") return direct("R0", "product-discovery", "product-discovery");
7579
+ if (name === "delivery" && subcommand === "assess") {
7580
+ return direct("R0", "local-discovery", "risk-assessment");
7581
+ }
7582
+ if (name === "skills") {
7583
+ if (subcommand === "list" || subcommand === "show") {
7584
+ return direct("R0", "product-discovery", "skill-discovery");
7585
+ }
7586
+ if (subcommand === "sync" || subcommand === "install") {
7587
+ return direct("R1", "managed-local-write", "skill-managed-write");
7588
+ }
7589
+ return forbidden("unknown-command", "\u672A\u77E5 skills \u5B50\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
7590
+ }
7591
+ if (name === "env") {
7592
+ if (subcommand === "list" || subcommand === "current" || subcommand === "show") {
7593
+ return direct("R0", "local-discovery", "environment-read");
7594
+ }
7595
+ return forbidden("identity-governance", "\u73AF\u5883\u65B0\u589E\u3001\u5207\u6362\u3001\u7F16\u8F91\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
7596
+ }
7597
+ if (name === "identity") {
7598
+ if (subcommand === "list") return direct("R0", "local-discovery", "identity-read");
7599
+ return forbidden("identity-governance", "\u8EAB\u4EFD\u5207\u6362\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
7600
+ }
7601
+ if (name === "query" || name === "describe" || name === "trace") {
7602
+ return direct("R1", "remote-read", "business-read");
7603
+ }
7604
+ if (name === "cache") {
7605
+ if (subcommand === "stats") return direct("R1", "remote-read", "cache-read");
7606
+ if (subcommand === "clear" || subcommand === "clear-meta") {
7607
+ return guarded("operational-write", "cache-mutation");
7608
+ }
7609
+ }
7610
+ if (name === "meta") {
7611
+ if (subcommand === "list" || subcommand === "get") {
7612
+ return direct("R1", "remote-read", "metadata-read");
7613
+ }
7614
+ if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
7615
+ return direct("R1", "local-validation", "metadata-dry-run");
7616
+ }
7617
+ if (subcommand === "set") return guarded("tenant-config-write", "metadata-write");
7618
+ if (subcommand === "delete") return forbidden("destructive", "\u5143\u6570\u636E\u5220\u9664\u4E0D\u53EF\u7531\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u3002");
7619
+ }
7620
+ if (name === "setting") {
7621
+ if (subcommand === "get") return direct("R1", "remote-read", "setting-read");
7622
+ if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
7623
+ return direct("R1", "local-validation", "setting-dry-run");
7624
+ }
7625
+ if (subcommand === "set" || subcommand === "reset") {
7626
+ return guarded("tenant-config-write", "setting-write");
7627
+ }
7628
+ }
7629
+ if (name === "workspace") {
7630
+ if (subcommand === "push" && hasFlag(argv, "--dry-run")) {
7631
+ return direct("R1", "remote-read", "workspace-push-dry-run");
7632
+ }
7633
+ if (["list", "active", "status", "validate", "verify", "preview"].includes(subcommand)) {
7634
+ return direct("R1", "remote-read", "workspace-read");
7635
+ }
7636
+ if (subcommand === "pull") {
7637
+ return direct("R1", "managed-local-write", "workspace-pull");
7638
+ }
7639
+ if (subcommand === "bind" || subcommand === "add" && argv[2] === "mini") {
7640
+ return guarded("managed-local-write", "workspace-local-change");
7641
+ }
7642
+ if (subcommand === "create") return guarded("tenant-write", "workspace-create");
7643
+ if (subcommand === "push") return forbidden("publish", "\u5F00\u53D1\u7A7A\u95F4\u53D1\u5E03\u5C5E\u4E8E R3\u3002");
7644
+ }
7645
+ if (name === "import") {
7646
+ if (hasFlag(argv, "--dry-run")) {
7647
+ return direct("R1", "local-validation", "import-dry-run");
7648
+ }
7649
+ return guarded("bulk-tenant-write", "import-write");
7650
+ }
7651
+ if (name === "create" || name === "update") {
7652
+ return guarded("tenant-write", name === "create" ? "record-create" : "record-update");
7653
+ }
7654
+ if (name === "upload") return guarded("tenant-write", "document-upload");
7655
+ if (name === "download") return direct("R1", "managed-local-write", "document-download");
7656
+ if (name === "action") {
7657
+ return forbidden(
7658
+ "action-effect-unclassified",
7659
+ "Action \u5C1A\u65E0\u7EDF\u4E00 destructive/publish \u8BED\u4E49\uFF0C\u9996\u7248\u4E0D\u80FD\u6309\u540D\u79F0\u731C\u6D4B\u540E\u653E\u884C\u3002"
7660
+ );
7661
+ }
7662
+ if (name === "delete" || name === "meta" && subcommand === "delete") {
7663
+ return forbidden("destructive", "\u5220\u9664\u64CD\u4F5C\u4E0D\u8FDB\u5165\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u9762\u3002");
7664
+ }
7665
+ if (name === "tenant") {
7666
+ return forbidden("tenant-governance", "\u79DF\u6237\u5F00\u901A\u6216\u6CBB\u7406\u5FC5\u987B\u5347\u7EA7\u6CBB\u7406\u4EBA\u5458\u3002");
7667
+ }
7668
+ if (name === "login" || name === "logout") {
7669
+ return forbidden("identity-governance", "\u767B\u5F55\u4E0E\u767B\u51FA\u5C5E\u4E8E\u8EAB\u4EFD\u6CBB\u7406\u64CD\u4F5C\u3002");
7670
+ }
7671
+ if (name === "chat") {
7672
+ return forbidden("agent-autonomy", "\u987E\u95EE\u63D2\u4EF6\u4E0D\u901A\u8FC7\u5D4C\u5957\u6570\u5B57\u5458\u5DE5\u5BF9\u8BDD\u6269\u5927\u81EA\u6CBB\u6267\u884C\u9762\u3002");
7673
+ }
7674
+ return forbidden("unknown-command", "\u672A\u77E5\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
7675
+ }
7676
+ function summarizeTarget(argv) {
7677
+ const name = argv[0] ?? "";
7678
+ const subcommand = argv[1] ?? "";
7679
+ const target = {};
7680
+ if (["query", "describe", "create", "update", "delete"].includes(name) && subcommand) {
7681
+ target.model = subcommand;
7682
+ }
7683
+ if (name === "action" && subcommand) target.action = subcommand;
7684
+ if (name === "skills" && argv[2] && !argv[2].startsWith("-")) target.skill = argv[2];
7685
+ if (name === "meta" && argv[2] && !argv[2].startsWith("-")) target.path = argv[2];
7686
+ if (name === "cache" && argv[2] && !argv[2].startsWith("-")) {
7687
+ target.cacheOperation = subcommand;
7688
+ target.model = argv[2];
7689
+ }
7690
+ if (name === "setting" && argv[2] && !argv[2].startsWith("-")) {
7691
+ target.domain = argv[2];
7692
+ const settingKeys = valuesAfterFlag(argv, "--set").map((assignment) => assignment.split("=", 1)[0]).filter((key) => Boolean(key)).sort();
7693
+ if (settingKeys.length > 0) target.settingKeys = settingKeys;
7694
+ }
7695
+ const id = flagValue(argv, "--id");
7696
+ if (id) target.recordIdSha256 = sha2565(id);
7697
+ for (const flag of PAYLOAD_FLAGS) {
7698
+ const raw = flagValue(argv, flag);
7699
+ if (!raw) continue;
7700
+ target.payloadSha256 = sha2565(raw);
7701
+ try {
7702
+ const parsed = JSON.parse(raw);
7703
+ if (isPlainObject(parsed)) target.fieldNames = Object.keys(parsed).sort();
7704
+ } catch {
7705
+ }
7706
+ }
7707
+ return target;
7708
+ }
7709
+ function payloadError(argv) {
7710
+ for (const flag of PAYLOAD_FLAGS) {
7711
+ const raw = flagValue(argv, flag);
7712
+ if (raw === void 0) continue;
7713
+ try {
7714
+ const parsed = JSON.parse(raw);
7715
+ if (!isPlainObject(parsed)) return true;
7716
+ } catch {
7717
+ return true;
7718
+ }
7719
+ }
7720
+ return false;
7721
+ }
7722
+ function direct(risk, effect, reasonCode) {
7723
+ return {
7724
+ risk,
7725
+ execution: "direct",
7726
+ effect,
7727
+ reasonCode,
7728
+ reason: risk === "R0" ? "\u7EAF\u53D1\u73B0\u64CD\u4F5C\uFF0C\u53EF\u76F4\u63A5\u6267\u884C\u3002" : "\u53EA\u8BFB\u6216\u53D7\u7BA1\u672C\u5730\u64CD\u4F5C\uFF0C\u53EF\u76F4\u63A5\u6267\u884C\u5E76\u4FDD\u7559\u7ED3\u679C\u3002"
7729
+ };
7730
+ }
7731
+ function guarded(effect, reasonCode) {
7732
+ return {
7733
+ risk: "R2",
7734
+ execution: "guarded",
7735
+ effect,
7736
+ reasonCode,
7737
+ reason: "\u666E\u901A\u526F\u4F5C\u7528\u64CD\u4F5C\uFF1B\u53EA\u5141\u8BB8\u5728 controlled \u9879\u76EE\u4E2D\u901A\u8FC7 delivery prepare + delivery execute \u5355\u6B21\u53D7\u63A7\u6267\u884C\u3002"
7738
+ };
7739
+ }
7740
+ function forbidden(reasonCode, reason) {
7741
+ return { risk: "R3", execution: "forbidden", effect: reasonCode, reasonCode, reason };
7742
+ }
7743
+ function evidenceFor(risk) {
7744
+ if (risk === "R0") return ["command-digest", "discovery-result"];
7745
+ if (risk === "R1") return ["command-digest", "target-aliases", "result-summary"];
7746
+ if (risk === "R2") {
7747
+ return [
7748
+ "command-digest",
7749
+ "explicit-target-aliases",
7750
+ "pre-state-or-dry-run",
7751
+ "rollback-plan",
7752
+ "execution-receipt",
7753
+ "post-verification"
7754
+ ];
7755
+ }
7756
+ return ["governance-decision"];
7757
+ }
7758
+ function flagValue(argv, flag) {
7759
+ for (let index = 0; index < argv.length; index += 1) {
7760
+ const arg = argv[index];
7761
+ if (arg === void 0) continue;
7762
+ if (arg === flag) return argv[index + 1];
7763
+ if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1);
7764
+ }
7765
+ return void 0;
7766
+ }
7767
+ function hasFlag(argv, flag) {
7768
+ return argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
7769
+ }
7770
+ function flagName(arg) {
7771
+ const equals = arg.indexOf("=");
7772
+ return equals < 0 ? arg : arg.slice(0, equals);
7773
+ }
7774
+ function valuesAfterFlag(argv, flag) {
7775
+ const index = argv.indexOf(flag);
7776
+ if (index < 0) return [];
7777
+ const values = [];
7778
+ for (let cursor = index + 1; cursor < argv.length; cursor += 1) {
7779
+ const value = argv[cursor];
7780
+ if (value === void 0 || value.startsWith("-")) break;
7781
+ values.push(value);
7782
+ }
7783
+ return values;
7784
+ }
7785
+ function sha2565(value) {
7786
+ return createHash5("sha256").update(value).digest("hex");
7787
+ }
7788
+ function isPlainObject(value) {
7789
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7790
+ }
7791
+
7792
+ // src/commands/delivery-execution.ts
7793
+ import { createHash as createHash6, randomBytes as secureRandomBytes } from "crypto";
7794
+ import { spawn } from "child_process";
7795
+ import { createReadStream as createReadStream2, promises as fs7 } from "fs";
7796
+ import path6 from "path";
7797
+ import { CliError as CliError20, CliErrorCode as CliErrorCode19, formatObject as formatObject18 } from "@hcmai/sdk";
7798
+ init_exit();
7799
+ var EXECUTION_CONTRACT_VERSION = "2026-08-28.controlled-v1";
7800
+ var DEFAULT_TTL_SECONDS = 15 * 60;
7801
+ var MIN_TTL_SECONDS = 60;
7802
+ var MAX_TTL_SECONDS = 60 * 60;
7803
+ var PLAN_ID_PATTERN = /^[a-f0-9]{24}$/;
7804
+ async function prepareDeliveryExecution(input) {
7805
+ const project = await realProject(input.project);
7806
+ await readProjectMode(project);
7807
+ const assessment = assessDeliveryCommand(input.argv);
7808
+ assertGuardedAssessment(assessment, input.argv);
7809
+ const ttlSeconds = input.ttlSeconds ?? DEFAULT_TTL_SECONDS;
7810
+ if (!Number.isInteger(ttlSeconds) || ttlSeconds < MIN_TTL_SECONDS || ttlSeconds > MAX_TTL_SECONDS) {
7811
+ throw invalid4(`--ttl-seconds \u5FC5\u987B\u662F ${MIN_TTL_SECONDS} \u5230 ${MAX_TTL_SECONDS} \u7684\u6574\u6570`);
7812
+ }
7813
+ const change = await bindDocument(project, input.changeRef, "\u53D8\u66F4\u8BF4\u660E");
7814
+ const rollback = await bindDocument(project, input.rollbackRef, "\u56DE\u6EDA\u8BF4\u660E");
7815
+ const evidence = await bindEvidenceDirectory(project, input.evidenceDir);
7816
+ const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
7817
+ const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
7818
+ assertRuntimeMatches(runtime, lockedRuntime);
7819
+ const created = (input.now ?? (() => /* @__PURE__ */ new Date()))();
7820
+ if (Number.isNaN(created.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
7821
+ const core = {
7822
+ schemaVersion: 1,
7823
+ contractVersion: EXECUTION_CONTRACT_VERSION,
7824
+ nonceSha256: sha2566((input.randomBytes ?? secureRandomBytes)(12)),
7825
+ createdAt: created.toISOString(),
7826
+ expiresAt: new Date(created.getTime() + ttlSeconds * 1e3).toISOString(),
7827
+ chironLockSha256: sha2566(lockBytes),
7828
+ runtime,
7829
+ assessment,
7830
+ documents: { change, rollback },
7831
+ evidenceDir: evidence.relative
7832
+ };
7833
+ const planId = sha2566(canonicalJson(core)).slice(0, 24);
7834
+ const plan = { ...core, planId };
7835
+ const plansDir = path6.join(project, ".hcm-delivery", "execution-plans");
7836
+ await safeManagedDirectory(project, ".hcm-delivery/execution-plans", plansDir);
7837
+ const planFile = path6.join(plansDir, `${planId}.json`);
7838
+ try {
7839
+ await fs7.writeFile(planFile, `${JSON.stringify(plan, null, 2)}
7840
+ `, {
7841
+ encoding: "utf8",
7842
+ mode: 384,
7843
+ flag: "wx"
7844
+ });
7845
+ } catch (cause) {
7846
+ throw invalid4(`\u6267\u884C\u8BA1\u5212\u5199\u5165\u5931\u8D25\u6216 planId \u5DF2\u5B58\u5728\uFF1A${planId}`, cause);
7847
+ }
7848
+ return plan;
7849
+ }
7850
+ async function executeDeliveryPlan(input) {
7851
+ if (!PLAN_ID_PATTERN.test(input.planId)) throw invalid4("planId \u5FC5\u987B\u662F 24 \u4F4D\u5C0F\u5199\u5341\u516D\u8FDB\u5236");
7852
+ const project = await realProject(input.project);
7853
+ if (await readProjectMode(project) !== "controlled") {
7854
+ throw invalid4("\u9879\u76EE\u4E0D\u662F controlled\uFF0C\u4E0D\u80FD\u6267\u884C R2 \u8BA1\u5212");
7855
+ }
7856
+ const planFile = path6.join(project, ".hcm-delivery", "execution-plans", `${input.planId}.json`);
7857
+ const plan = await readPlan(project, planFile);
7858
+ if (plan.planId !== input.planId || computePlanId(plan) !== input.planId) {
7859
+ throw invalid4("\u6267\u884C\u8BA1\u5212\u5185\u5BB9\u4E0E planId \u6458\u8981\u4E0D\u4E00\u81F4");
7860
+ }
7861
+ const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
7862
+ if (Number.isNaN(now.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
7863
+ const expiresAt = Date.parse(plan.expiresAt);
7864
+ const createdAt = Date.parse(plan.createdAt);
7865
+ if (!Number.isFinite(createdAt) || !Number.isFinite(expiresAt) || expiresAt - createdAt < MIN_TTL_SECONDS * 1e3 || expiresAt - createdAt > MAX_TTL_SECONDS * 1e3) {
7866
+ throw invalid4("\u6267\u884C\u8BA1\u5212\u65F6\u95F4\u7A97\u53E3\u65E0\u6548");
7867
+ }
7868
+ if (now.getTime() < createdAt - 5e3) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E9\u4E8E\u8BA1\u5212\u521B\u5EFA\u65F6\u95F4");
7869
+ if (now.getTime() > expiresAt) throw invalid4("\u6267\u884C\u8BA1\u5212\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0 prepare");
7870
+ const assessment = assessDeliveryCommand(input.argv);
7871
+ assertGuardedAssessment(assessment, input.argv);
7872
+ if (canonicalJson(assessment) !== canonicalJson(plan.assessment)) {
7873
+ throw invalid4("\u5B9E\u9645 argv \u4E0E\u6267\u884C\u8BA1\u5212\u7684\u547D\u4EE4\u6458\u8981\u6216\u98CE\u9669\u4E8B\u5B9E\u4E0D\u4E00\u81F4");
7874
+ }
7875
+ const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
7876
+ if (sha2566(lockBytes) !== plan.chironLockSha256) {
7877
+ throw invalid4("Chiron \u9501\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0\u8BC4\u4F30\u5E76 prepare");
7878
+ }
7879
+ const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
7880
+ assertRuntimeMatches(runtime, lockedRuntime);
7881
+ if (canonicalJson(runtime) !== canonicalJson(plan.runtime)) {
7882
+ throw invalid4("CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E\u6267\u884C\u8BA1\u5212\u4E0D\u4E00\u81F4");
7883
+ }
7884
+ await assertDocumentUnchanged(project, plan.documents.change, "\u53D8\u66F4\u8BF4\u660E");
7885
+ await assertDocumentUnchanged(project, plan.documents.rollback, "\u56DE\u6EDA\u8BF4\u660E");
7886
+ const evidenceDir = await bindEvidenceDirectory(project, plan.evidenceDir);
7887
+ const receiptFile = path6.join(evidenceDir.absolute, `${plan.planId}.receipt.json`);
7888
+ if (await pathExists(receiptFile)) throw invalid4(`\u6267\u884C\u6536\u636E\u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u518D\u6B21\u6267\u884C\uFF1A${plan.planId}`);
7889
+ const claimsDir = path6.join(project, ".hcm-delivery", "execution-claims");
7890
+ await safeManagedDirectory(project, ".hcm-delivery/execution-claims", claimsDir);
7891
+ const claimFile = path6.join(claimsDir, `${plan.planId}.json`);
7892
+ try {
7893
+ await fs7.writeFile(
7894
+ claimFile,
7895
+ `${JSON.stringify({ schemaVersion: 1, planId: plan.planId, claimedAt: now.toISOString() }, null, 2)}
7896
+ `,
7897
+ { encoding: "utf8", mode: 384, flag: "wx" }
7898
+ );
7899
+ } catch (cause) {
7900
+ throw invalid4(`\u6267\u884C\u8BA1\u5212\u5DF2\u6D88\u8D39\uFF0C\u62D2\u7EDD\u91CD\u590D\u6267\u884C\uFF1A${plan.planId}`, cause);
7901
+ }
7902
+ const stdoutHash = createHash6("sha256");
7903
+ const stderrHash = createHash6("sha256");
7904
+ const run = input.runCommand ?? runCurrentCliCommand;
7905
+ let exitCode;
7906
+ try {
7907
+ ({ exitCode } = await run(input.argv, project, {
7908
+ stdout: (chunk) => stdoutHash.update(chunk),
7909
+ stderr: (chunk) => stderrHash.update(chunk)
7910
+ }));
7911
+ } catch (cause) {
7912
+ stderrHash.update(
7913
+ Buffer.from(`delivery executor failed: ${String(cause.message)}
7914
+ `)
7915
+ );
7916
+ exitCode = 1;
7917
+ }
7918
+ const completed = (input.now ?? (() => /* @__PURE__ */ new Date()))();
7919
+ if (Number.isNaN(completed.getTime())) throw invalid4("\u5B8C\u6210\u65F6\u95F4\u65E0\u6548");
7920
+ if (!Number.isInteger(exitCode) || exitCode < 0) exitCode = 1;
7921
+ const receipt = {
7922
+ schemaVersion: 1,
7923
+ contractVersion: EXECUTION_CONTRACT_VERSION,
7924
+ planId: plan.planId,
7925
+ assessmentId: plan.assessment.assessmentId,
7926
+ commandSha256: plan.assessment.commandSha256,
7927
+ startedAt: now.toISOString(),
7928
+ completedAt: completed.toISOString(),
7929
+ exitCode,
7930
+ stdoutSha256: stdoutHash.digest("hex"),
7931
+ stderrSha256: stderrHash.digest("hex")
7932
+ };
7933
+ await writeReceiptExclusive(receiptFile, receipt);
7934
+ return receipt;
7935
+ }
7936
+ async function deliveryPrepareCommand(argv, args) {
7937
+ try {
7938
+ if (!args.changeRef) throw invalid4("--change-ref <file> \u5FC5\u586B");
7939
+ if (!args.rollbackRef) throw invalid4("--rollback-ref <file> \u5FC5\u586B");
7940
+ if (!args.evidenceDir) throw invalid4("--evidence-dir <dir> \u5FC5\u586B");
7941
+ const ttlSeconds = parseTtl(args.ttlSeconds);
7942
+ const plan = await prepareDeliveryExecution({
7943
+ project: args.project ?? process.cwd(),
7944
+ argv,
7945
+ changeRef: args.changeRef,
7946
+ rollbackRef: args.rollbackRef,
7947
+ evidenceDir: args.evidenceDir,
7948
+ ...ttlSeconds === void 0 ? {} : { ttlSeconds }
7949
+ });
7950
+ process.stdout.write(formatObject18(plan, { format: args.output ?? "json" }) + "\n");
7951
+ } catch (cause) {
7952
+ exitWithError(cause);
7953
+ }
7954
+ }
7955
+ async function deliveryExecuteCommand(planId, argv, args) {
7956
+ try {
7957
+ const receipt = await executeDeliveryPlan({
7958
+ project: args.project ?? process.cwd(),
7959
+ planId,
7960
+ argv
7961
+ });
7962
+ process.stdout.write(formatObject18(receipt, { format: args.output ?? "json" }) + "\n");
7963
+ process.exitCode = receipt.exitCode;
7964
+ } catch (cause) {
7965
+ exitWithError(cause);
7966
+ }
7967
+ }
7968
+ async function readProjectMode(project) {
7969
+ const file = path6.join(project, ".hcm-delivery", "project.yml");
7970
+ const content = await readSafeFile(project, file, "\u9879\u76EE\u914D\u7F6E");
7971
+ const match = content.toString("utf8").match(
7972
+ /^deliveryMode:\s*(?:"(plan-only|controlled)"|'(plan-only|controlled)'|(plan-only|controlled))\s*$/m
7973
+ );
7974
+ const mode = match?.[1] ?? match?.[2] ?? match?.[3];
7975
+ if (mode !== "plan-only" && mode !== "controlled") {
7976
+ throw invalid4("project.yml \u7F3A\u5C11\u5408\u6CD5 deliveryMode");
7977
+ }
7978
+ return mode;
7979
+ }
7980
+ async function readChironLock(project) {
7981
+ const file = path6.join(project, ".hcm-delivery", "chiron.lock.json");
7982
+ const bytes = await readSafeFile(project, file, "Chiron \u9501");
7983
+ let parsed;
7984
+ try {
7985
+ parsed = JSON.parse(bytes.toString("utf8"));
7986
+ } catch (cause) {
7987
+ throw invalid4("Chiron \u9501\u4E0D\u662F\u5408\u6CD5 JSON", cause);
7988
+ }
7989
+ const launch = parsed?.runtime?.launch;
7990
+ if (!isRuntimeIdentity(launch)) throw invalid4("Chiron \u9501\u7F3A\u5C11\u5408\u6CD5 runtime.launch \u6458\u8981");
7991
+ return { bytes, runtime: launch };
7992
+ }
7993
+ async function bindDocument(project, reference, label) {
7994
+ const relative2 = safeRelative(reference, label);
7995
+ if (!(relative2.startsWith("work/") || relative2.startsWith("decisions/"))) {
7996
+ throw invalid4(`${label}\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE work/ \u6216 decisions/ \u4E0B`);
7997
+ }
7998
+ const file = path6.join(project, relative2);
7999
+ const bytes = await readSafeFile(project, file, label);
8000
+ return { path: relative2, sha256: sha2566(bytes) };
8001
+ }
8002
+ async function assertDocumentUnchanged(project, document, label) {
8003
+ const current = await bindDocument(project, document.path, label);
8004
+ if (current.sha256 !== document.sha256) throw invalid4(`${label}\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0 prepare`);
8005
+ }
8006
+ async function bindEvidenceDirectory(project, reference) {
8007
+ const relative2 = safeRelative(reference, "evidence \u76EE\u5F55");
8008
+ if (!(relative2 === "evidence" || relative2.startsWith("evidence/"))) {
8009
+ throw invalid4("evidence \u76EE\u5F55\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE evidence/ \u4E0B");
8010
+ }
8011
+ const absolute = path6.join(project, relative2);
8012
+ await assertNoSymlinkComponents(project, relative2);
8013
+ const stat = await fs7.stat(absolute).catch((cause) => {
8014
+ throw invalid4(`evidence \u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
8015
+ });
8016
+ if (!stat.isDirectory()) throw invalid4(`evidence \u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
8017
+ return { relative: relative2, absolute };
8018
+ }
8019
+ async function readPlan(project, file) {
8020
+ const bytes = await readSafeFile(project, file, "\u6267\u884C\u8BA1\u5212");
8021
+ let plan;
8022
+ try {
8023
+ plan = JSON.parse(bytes.toString("utf8"));
8024
+ } catch (cause) {
8025
+ throw invalid4("\u6267\u884C\u8BA1\u5212\u4E0D\u662F\u5408\u6CD5 JSON", cause);
8026
+ }
8027
+ if (!isExecutionPlan(plan)) throw invalid4("\u6267\u884C\u8BA1\u5212 schema \u65E0\u6548");
8028
+ return plan;
8029
+ }
8030
+ function computePlanId(plan) {
8031
+ const core = { ...plan };
8032
+ delete core.planId;
8033
+ return sha2566(canonicalJson(core)).slice(0, 24);
8034
+ }
8035
+ function assertGuardedAssessment(assessment, argv) {
8036
+ if (assessment.risk !== "R2") throw invalid4("\u53EA\u6709 R2 \u547D\u4EE4\u53EF\u4EE5\u8FDB\u5165\u53D7\u63A7\u6267\u884C\u8BA1\u5212");
8037
+ if (!assessment.guardedExecutionAvailable) {
8038
+ throw invalid4("\u8BE5 R2 \u547D\u4EE4\u4E0D\u5728\u9996\u6279\u53D7\u63A7\u6267\u884C\u5668\u652F\u6301\u9762");
8039
+ }
8040
+ if (!assessment.environment.envAlias) throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --env \u73AF\u5883\u522B\u540D");
8041
+ if (!assessment.environment.identityAlias) {
8042
+ throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --as \u8EAB\u4EFD\u522B\u540D");
8043
+ }
8044
+ validateSupportedCommandShape(assessment.reasonCode, argv);
8045
+ }
8046
+ function validateSupportedCommandShape(reasonCode, argv) {
8047
+ if (reasonCode === "record-create") {
8048
+ requireFlagValue(argv, "--data", "create \u5FC5\u987B\u5305\u542B --data <JSON>");
8049
+ return;
8050
+ }
8051
+ if (reasonCode === "record-update") {
8052
+ requireFlagValue(argv, "--id", "update \u5FC5\u987B\u5305\u542B --id <id>");
8053
+ requireFlagValue(argv, "--data", "update \u5FC5\u987B\u5305\u542B --data <JSON>");
8054
+ return;
8055
+ }
8056
+ if (reasonCode === "cache-mutation") {
8057
+ if (!argv[2] || argv[2].startsWith("-")) throw invalid4("cache clear \u5FC5\u987B\u5305\u542B Model");
8058
+ return;
8059
+ }
8060
+ if (reasonCode === "setting-write") {
8061
+ if (!argv[2] || argv[2].startsWith("-")) throw invalid4("setting \u5199\u547D\u4EE4\u5FC5\u987B\u5305\u542B domain");
8062
+ if (argv[1] === "set") {
8063
+ requireFlagValue(argv, "--set", "setting set \u5FC5\u987B\u5305\u542B --set <assignment>");
8064
+ return;
8065
+ }
8066
+ if (argv[1] === "reset" && (!argv[3] || !argv[4])) {
8067
+ throw invalid4("setting reset \u5FC5\u987B\u5305\u542B domain\u3001namespace \u4E0E key");
8068
+ }
8069
+ }
8070
+ }
8071
+ function requireFlagValue(argv, flag, message) {
8072
+ for (let index = 0; index < argv.length; index += 1) {
8073
+ const value = argv[index];
8074
+ if (value === flag) {
8075
+ const next = argv[index + 1];
8076
+ if (next && !next.startsWith("-")) return next;
8077
+ throw invalid4(message);
8078
+ }
8079
+ if (value?.startsWith(`${flag}=`) && value.length > flag.length + 1) {
8080
+ return value.slice(flag.length + 1);
8081
+ }
8082
+ }
8083
+ throw invalid4(message);
8084
+ }
8085
+ function assertRuntimeMatches(actual, expected) {
8086
+ if (canonicalJson(actual) !== canonicalJson(expected)) {
8087
+ throw invalid4("\u5F53\u524D CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E Chiron \u9501\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 skills sync");
8088
+ }
8089
+ }
8090
+ async function currentRuntimeIdentity() {
8091
+ const entry = process.argv[1];
8092
+ if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
8093
+ return {
8094
+ kind: "node-entry",
8095
+ executableSha256: await sha256File2(process.execPath),
8096
+ entrySha256: await sha256File2(entry)
8097
+ };
8098
+ }
8099
+ async function runCurrentCliCommand(argv, cwd, streams) {
8100
+ const entry = process.argv[1];
8101
+ if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
8102
+ return new Promise((resolve3, reject) => {
8103
+ const child = spawn(process.execPath, [entry, ...argv], {
8104
+ cwd,
8105
+ env: process.env,
8106
+ shell: false,
8107
+ stdio: ["inherit", "pipe", "pipe"],
8108
+ windowsHide: true
8109
+ });
8110
+ child.stdout.on("data", (chunk) => {
8111
+ streams.stdout(chunk);
8112
+ process.stdout.write(chunk);
8113
+ });
8114
+ child.stderr.on("data", (chunk) => {
8115
+ streams.stderr(chunk);
8116
+ process.stderr.write(chunk);
8117
+ });
8118
+ child.once("error", reject);
8119
+ child.once("close", (code, signal) => resolve3({ exitCode: code ?? (signal ? 1 : 0) }));
8120
+ });
8121
+ }
8122
+ async function writeReceiptExclusive(target, receipt) {
8123
+ const temp = `${target}.${process.pid}.${Date.now()}.tmp`;
8124
+ try {
8125
+ await fs7.writeFile(temp, `${JSON.stringify(receipt, null, 2)}
8126
+ `, {
8127
+ encoding: "utf8",
8128
+ mode: 384,
8129
+ flag: "wx"
8130
+ });
8131
+ await fs7.link(temp, target);
8132
+ } catch (cause) {
8133
+ throw invalid4(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${path6.basename(target)}`, cause);
8134
+ } finally {
8135
+ await fs7.unlink(temp).catch(() => void 0);
8136
+ }
8137
+ }
8138
+ async function safeManagedDirectory(project, relative2, absolute) {
8139
+ await assertNoSymlinkComponents(project, path6.dirname(relative2));
8140
+ const existing = await fs7.lstat(absolute).catch(() => void 0);
8141
+ if (existing?.isSymbolicLink()) throw invalid4(`\u53D7\u7BA1\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
8142
+ if (existing && !existing.isDirectory()) throw invalid4(`\u53D7\u7BA1\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
8143
+ if (!existing) await fs7.mkdir(absolute, { mode: 448 });
8144
+ await fs7.chmod(absolute, 448);
8145
+ }
8146
+ async function pathExists(file) {
8147
+ return fs7.lstat(file).then(
8148
+ () => true,
8149
+ (cause) => {
8150
+ if (cause.code === "ENOENT") return false;
8151
+ throw cause;
8152
+ }
8153
+ );
8154
+ }
8155
+ async function readSafeFile(project, file, label) {
8156
+ const relative2 = path6.relative(project, file).split(path6.sep).join("/");
8157
+ if (relative2.startsWith("../") || path6.isAbsolute(relative2))
8158
+ throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
8159
+ await assertNoSymlinkComponents(project, relative2);
8160
+ let stat;
8161
+ try {
8162
+ stat = await fs7.lstat(file);
8163
+ } catch (cause) {
8164
+ throw invalid4(`${label}\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
8165
+ }
8166
+ if (stat.isSymbolicLink()) throw invalid4(`${label}\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
8167
+ if (!stat.isFile()) throw invalid4(`${label}\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${relative2}`);
8168
+ return fs7.readFile(file);
8169
+ }
8170
+ async function assertNoSymlinkComponents(project, relative2) {
8171
+ const parts = relative2.split("/").filter(Boolean);
8172
+ let current = project;
8173
+ for (const part of parts) {
8174
+ current = path6.join(current, part);
8175
+ const stat = await fs7.lstat(current).catch(() => void 0);
8176
+ if (stat?.isSymbolicLink()) throw invalid4(`\u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
8177
+ if (!stat) break;
8178
+ }
8179
+ }
8180
+ async function realProject(requested) {
8181
+ const absolute = path6.resolve(requested);
8182
+ let project;
8183
+ try {
8184
+ project = await fs7.realpath(absolute);
8185
+ } catch (cause) {
8186
+ throw invalid4(`\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${absolute}`, cause);
8187
+ }
8188
+ const stat = await fs7.stat(project);
8189
+ if (!stat.isDirectory()) throw invalid4(`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${absolute}`);
8190
+ return project;
8191
+ }
8192
+ function safeRelative(reference, label) {
8193
+ if (!reference || path6.isAbsolute(reference) || /[\r\n\0]/.test(reference)) {
8194
+ throw invalid4(`${label}\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84`);
8195
+ }
8196
+ const normalized = path6.normalize(reference).split(path6.sep).join("/");
8197
+ if (normalized === ".." || normalized.startsWith("../") || normalized === ".") {
8198
+ throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
8199
+ }
8200
+ return normalized.replace(/^\.\//, "");
8201
+ }
8202
+ function isRuntimeIdentity(value) {
8203
+ if (!isRecord(value) || !isSha(value.executableSha256)) return false;
8204
+ if (value.kind === "executable") return true;
8205
+ return value.kind === "node-entry" && isSha(value.entrySha256);
8206
+ }
8207
+ function isExecutionPlan(value) {
8208
+ if (!isRecord(value)) return false;
8209
+ return value.schemaVersion === 1 && value.contractVersion === EXECUTION_CONTRACT_VERSION && typeof value.planId === "string" && PLAN_ID_PATTERN.test(value.planId) && typeof value.createdAt === "string" && typeof value.expiresAt === "string" && isSha(value.nonceSha256) && isSha(value.chironLockSha256) && isRuntimeIdentity(value.runtime) && isRecord(value.assessment) && isRecord(value.documents) && isBoundDocument(value.documents.change) && isBoundDocument(value.documents.rollback) && typeof value.evidenceDir === "string";
8210
+ }
8211
+ function isBoundDocument(value) {
8212
+ return isRecord(value) && typeof value.path === "string" && isSha(value.sha256);
8213
+ }
8214
+ function isRecord(value) {
8215
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8216
+ }
8217
+ function isSha(value) {
8218
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
8219
+ }
8220
+ function parseTtl(value) {
8221
+ if (value === void 0) return void 0;
8222
+ if (!/^\d+$/.test(value)) throw invalid4("--ttl-seconds \u5FC5\u987B\u662F\u6574\u6570");
8223
+ return Number(value);
8224
+ }
8225
+ function canonicalJson(value) {
8226
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
8227
+ if (isRecord(value)) {
8228
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
8229
+ }
8230
+ return JSON.stringify(value) ?? "null";
8231
+ }
8232
+ function sha2566(value) {
8233
+ return createHash6("sha256").update(value).digest("hex");
8234
+ }
8235
+ async function sha256File2(file) {
8236
+ const hash = createHash6("sha256");
8237
+ await new Promise((resolve3, reject) => {
8238
+ const stream = createReadStream2(file);
8239
+ stream.on("data", (chunk) => hash.update(chunk));
8240
+ stream.on("error", reject);
8241
+ stream.on("end", resolve3);
8242
+ });
8243
+ return hash.digest("hex");
8244
+ }
8245
+ function invalid4(message, cause) {
8246
+ return new CliError20({ code: CliErrorCode19.INVALID_ARGUMENT, message, cause });
8247
+ }
8248
+
8249
+ // src/startup.ts
8250
+ function needsLegacyProfileMigration(argv) {
8251
+ const args = argv.slice(2);
8252
+ if (args[0] === "delivery") return false;
8253
+ return !args.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg));
8254
+ }
8255
+
6613
8256
  // src/index.ts
6614
8257
  var program = new Command();
6615
8258
  var exitOnBrokenPipe = (err) => {
@@ -6640,6 +8283,10 @@ var identity = program.command("identity").description("\u7BA1\u7406 HCM \u8EAB\
6640
8283
  identity.command("list").description("\u5217\u51FA identity\uFF08\u9ED8\u8BA4\u5217\u5F53\u524D env\uFF0C--env all \u5217\u5168\u90E8\uFF09").option("--env <name>", '\u8FC7\u6EE4 env\uFF1B\u4F20 "all" \u5217\u5168\u90E8', void 0).action((opts) => identityList(opts));
6641
8284
  identity.command("use <name>").description("\u628A identity \u8BBE\u4E3A\u5176 env \u7684 default identity").action((name) => identityUse(name));
6642
8285
  identity.command("remove <name>").description("\u5220\u9664 identity\uFF08\u6E05 token + \u82E5\u662F env default \u5219\u6E05\u7A7A\uFF09").action((name) => identityRemove(name));
8286
+ var delivery = program.command("delivery").description("\u987E\u95EE\u4EA4\u4ED8\u547D\u4EE4\u7684\u98CE\u9669\u8BC4\u4F30\u4E0E\u4E00\u6B21\u6027\u53D7\u63A7\u6267\u884C\u5408\u540C");
8287
+ delivery.command("assess").description("\u7EAF\u672C\u5730\u8BC4\u4F30\u4E00\u6B21 HCM CLI argv\uFF1B\u7528 -- \u5206\u9694\u88AB\u8BC4\u4F30\u547D\u4EE4").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u88AB\u8BC4\u4F30\u7684 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570\uFF0C\u4E0D\u5305\u542B hcm \u672C\u8EAB").action((command, opts) => deliveryAssessCommand(command, opts));
8288
+ delivery.command("prepare").description("\u628A\u53D7\u652F\u6301\u7684 R2 argv \u4E0E\u9501\u3001\u8FD0\u884C\u65F6\u548C\u53D8\u66F4/\u56DE\u6EDA\u8BF4\u660E\u7ED1\u5B9A\u4E3A\u77ED\u65F6\u6267\u884C\u8BA1\u5212").requiredOption("--change-ref <file>", "\u9879\u76EE work/ \u6216 decisions/ \u4E0B\u7684\u53D8\u66F4\u8BF4\u660E").requiredOption("--rollback-ref <file>", "\u9879\u76EE work/ \u6216 decisions/ \u4E0B\u7684\u56DE\u6EDA\u8BF4\u660E").requiredOption("--evidence-dir <dir>", "\u9879\u76EE evidence/ \u4E0B\u7684\u6536\u636E\u76EE\u5F55").option("--project <dir>", "HCM \u987E\u95EE\u9879\u76EE\u6839\u76EE\u5F55", ".").option("--ttl-seconds <n>", "\u6709\u6548\u671F\u79D2\u6570\uFF0860-3600\uFF09", "900").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u88AB\u7ED1\u5B9A\u7684 R2 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570\uFF0C\u4E0D\u5305\u542B hcm \u672C\u8EAB").action((command, opts) => deliveryPrepareCommand(command, opts));
8289
+ delivery.command("execute <planId>").description("\u9010\u6B21\u6279\u51C6\u540E\u6D88\u8D39\u77ED\u65F6\u8BA1\u5212\uFF0C\u5E76\u6267\u884C\u4E00\u6B21\u4E0E\u8BA1\u5212\u5B8C\u5168\u76F8\u540C\u7684 R2 argv").option("--project <dir>", "HCM \u987E\u95EE\u9879\u76EE\u6839\u76EE\u5F55", ".").option("--output <fmt>", "\u6536\u636E\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u4E0E prepare \u5B8C\u5168\u76F8\u540C\u7684 R2 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570").action((planId, command, opts) => deliveryExecuteCommand(planId, command, opts));
6643
8290
  program.command("query <Model>").description("\u67E5\u8BE2 Model \u5217\u8868\u6570\u636E\uFF08\u901A\u8FC7 ModelQueryDsl JSON \u8868\u8FBE\u8FC7\u6EE4/\u6392\u5E8F/\u5206\u9875\uFF09").option(
6644
8291
  "--filter <JSON>",
6645
8292
  `\u8FC7\u6EE4 AST\uFF08ModelQueryDsl\uFF09\uFF1A\u53F6\u5B50 '{"field":"status","op":"eq","value":"ACTIVE"}'\uFF0C\u591A\u6761\u4EF6\u7528 '{"and":[\u53F6\u5B50,\u2026]}'\u3002\u26A0\uFE0F \u4E0D\u63A5\u53D7 {"\u5B57\u6BB5":"\u503C"} \u7B80\u5199\uFF0Cschema \u5C42\u4F1A\u62D2`
@@ -6703,6 +8350,7 @@ var skills = program.command("skills").description("\u777F\u620E Chiron \u4EA4\u
6703
8350
  skills.command("list [keyword]").description("\u5217\u51FA\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\u7684\u4EA4\u4ED8\u6280\u80FD\uFF1B--installed \u6539\u770B\u672C\u5730\u88C5\u4E86\u54EA\u4E9B\uFF08\u4E0D\u8FDE\u7F51\uFF09").option("--installed", "\u53EA\u770B\u672C\u5730\u5DF2\u88C5\u7684\u6280\u80FD\uFF08\u8BFB .hcm-skills.json\uFF0C\u4E0D\u8FDE\u7F51\uFF09").option("--target <agent>", "\u914D\u5408 --installed\uFF1Aclaude | codex | auto", "auto").option("--global", "\u914D\u5408 --installed\uFF1A\u770B\u7528\u6237\u7EA7\u76EE\u5F55").option("--dir <path>", "\u914D\u5408 --installed\uFF1A\u770B\u6307\u5B9A\u76EE\u5F55").option("--category <key>", "\u53EA\u770B\u67D0\u4E2A\u5206\u7C7B").option("--stage <key>", "\u53EA\u770B\u67D0\u4E2A\u4EA4\u4ED8\u9636\u6BB5\uFF1Ahandover | baseline | configure | migrate | integrate | golive | operate").option("--by-stage", "\u6309\u4EA4\u4ED8\u751F\u547D\u5468\u671F\u5206\u7EC4\u5C55\u793A\uFF08\u9ED8\u8BA4\u6309\u529F\u80FD\u5206\u7C7B\uFF09").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((kw, opts) => skillsListCommand(kw, opts));
6704
8351
  skills.command("show <id>").description("\u6253\u5370\u6280\u80FD\u539F\u6587\uFF08\u7BA1\u9053\u53CB\u597D\uFF09").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").action((id, opts) => skillsShowCommand(id, opts));
6705
8352
  skills.command("install [id]").description("\u8FDE\u540C requires \u524D\u7F6E\u6280\u80FD\u4E0E\u53C2\u8003\u6587\u4EF6\u88C5\u5230 agent \u6280\u80FD\u76EE\u5F55\uFF1B--all \u88C5\u6574\u4E2A\u6280\u80FD\u5E93").option("--all", "\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D\uFF08\u5F80\u540E update \u4F1A\u81EA\u52A8\u8865\u4E0A\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\uFF09").option("--target <agent>", "\u88C5\u7ED9\u54EA\u4E2A agent\uFF1Aclaude\uFF08.claude/skills/\uFF09| codex\uFF08hcm-skills/ + AGENTS.md\uFF09| auto", "auto").option("--global", "\u88C5\u5230\u7528\u6237\u7EA7\u76EE\u5F55\u800C\u975E\u5F53\u524D\u9879\u76EE").option("--dir <path>", "\u88C5\u5230\u6307\u5B9A\u76EE\u5F55").option("--from <dir>", "\u4ECE\u672C\u5730\u76EE\u5F55\u88C5\uFF08\u73B0\u573A\u6539\u8FC7\u7684\u7248\u672C\uFF09\uFF0C\u4E0D\u8D70\u7F51\u7EDC").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u540C\u540D\u6280\u80FD").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((id, opts) => skillsInstallCommand(id, opts));
8353
+ skills.command("sync <id>").description("\u4ECE\u76EE\u6807\u5B9E\u4F8B\u540C\u6B65 Skill \u95ED\u5305\u5230 WorkBuddy \u9879\u76EE\u5E76\u5199 chiron.lock").requiredOption("--target <agent>", "\u76EE\u6807 Agent \u5BBF\u4E3B\uFF1B\u5F53\u524D\u652F\u6301 workbuddy").requiredOption("--project <path>", "WorkBuddy \u9879\u76EE\u6839\u76EE\u5F55").option("--cli <path>", "\u663E\u5F0F\u6307\u5B9A\u8981\u9501\u5B9A\u5230\u9879\u76EE\u7684 HCM CLI\uFF1B\u9ED8\u8BA4\u4F7F\u7528\u5F53\u524D\u8FD9\u4EFD CLI").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((id, opts) => skillsSyncCommand(id, opts));
6706
8354
  skills.command("update").description("\u628A\u88C5\u8FC7\u7684\u6280\u80FD\u5347\u5230\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\uFF08\u4EA7\u54C1\u5347\u7EA7\u540E\u8DD1\u5B83\uFF09").option("--all", "\u8FDE\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4E00\u8D77\u88C5\u4E0A\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D").option("--check", "\u53EA\u770B\u4F1A\u53D8\u4EC0\u4E48\uFF0C\u4E0D\u5199\u76D8").option("--target <agent>", "claude | codex | auto\uFF08\u9ED8\u8BA4\u6309\u5DF2\u5B89\u88C5\u76EE\u5F55\u63A2\uFF09", "auto").option("--global", "\u5347\u7EA7\u7528\u6237\u7EA7\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--dir <path>", "\u5347\u7EA7\u6307\u5B9A\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--force", "\u8FDE\u672C\u5730\u6539\u8FC7\u7684\u6280\u80FD\u4E00\u8D77\u8986\u76D6\u6210\u4EA7\u54C1\u8FD9\u4E00\u7248").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6\u5B89\u88C5\u65F6\u8BB0\u5F55\u7684\u5730\u5740\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((opts) => skillsUpdateCommand(opts));
6707
8355
  program.command("update <Model>").description("\u66F4\u65B0\u4E00\u6761 Model \u8BB0\u5F55\uFF08\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF09").requiredOption("--id <id>", "\u8BB0\u5F55 ID").requiredOption("--data <JSON>", `\u8981\u66F4\u65B0\u7684\u5B57\u6BB5 JSON\uFF0C\u4F8B\u5982 '{"name":"\u65B0\u540D\u79F0"}'`).option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((model, opts) => updateCommand(model, opts));
6708
8356
  program.command("version").description("\u67E5\u540E\u7AEF\u7248\u672C\uFF08commit / tag / \u6784\u5EFA\u65F6\u95F4\uFF09\u2014\u2014\u514D\u8BA4\u8BC1\uFF0C\u65E0\u9700\u5148 login").option("--endpoint <url>", "\u540E\u7AEF\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => versionCommand(opts));
@@ -6711,13 +8359,15 @@ setting.command("get <domain>").description("\u8BFB\u67D0\u57DF\u53C2\u6570\uFF0
6711
8359
  setting.command("set <domain>").description("\u6539\u53C2\u6570\uFF0C\u53EF\u91CD\u590D --set\uFF1B\u8D70\u53D7\u63A7\u6821\u9A8C\u8DEF\u5F84\u800C\u975E\u6807\u51C6 CRUD").option("--set <assignment...>", "namespace.key=value\uFF0C\u4F8B\u5982 security.password.passwordPolicy=complex").option("--dry-run", "\u53EA\u663E\u793A\u5C06\u5199\u5165\u4EC0\u4E48\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((d, opts) => settingSetCommand(d, opts));
6712
8360
  setting.command("reset <domain> <namespace> <key>").description("\u6E05\u9664\u79DF\u6237\u8986\u76D6\uFF0C\u56DE\u843D\u5230\u9ED8\u8BA4\u503C").option("--revision <n>", "\u4E50\u89C2\u9501\u7248\u672C\u53F7").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((d, ns, k, opts) => settingResetCommand(d, ns, k, opts));
6713
8361
  async function main() {
6714
- try {
6715
- await migrateLegacyProfiles();
6716
- } catch (e) {
6717
- process.stderr.write(
6718
- `[hcm] event=migration_error reason=${JSON.stringify(String(e.message))}
8362
+ if (needsLegacyProfileMigration(process.argv)) {
8363
+ try {
8364
+ await migrateLegacyProfiles();
8365
+ } catch (e) {
8366
+ process.stderr.write(
8367
+ `[hcm] event=migration_error reason=${JSON.stringify(String(e.message))}
6719
8368
  `
6720
- );
8369
+ );
8370
+ }
6721
8371
  }
6722
8372
  await program.parseAsync(process.argv);
6723
8373
  }