zapmyco 0.14.0 → 0.15.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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { A as configureLogger, B as buildSkillSnapshot, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-BD5Odmr5.mjs";
2
+ import { A as configureLogger, D as eventBus, G as SESSION_DIR_NAME, K as VERSION, L as WebError, O as DEFAULT_COMPACTION_CONFIG, V as loadSkills, W as APP_NAME, d as SubAgentManager, f as SecurityBlockedError, g as runWithToolGuardContext, h as getToolGuardContext, i as AgentLlmFacade, j as logger, m as createToolInfoResolver, n as loadConfig, p as ToolGuard, q as __require, t as HOME_CONFIG_PATH, v as createLlmBasedAgent, z as ZapmycoErrorCode } from "../loader-DEuRwvSw.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
@@ -18,12 +18,13 @@ import i18next from "i18next";
18
18
  import TurndownService from "turndown";
19
19
  import { lookup } from "node:dns/promises";
20
20
  import { promisify } from "node:util";
21
+ import chokidar from "chokidar";
21
22
  import { marked } from "marked";
22
23
  import { Client } from "@modelcontextprotocol/sdk/client";
23
24
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
24
25
 
25
26
  //#region src/cli/repl/command-registry.ts
26
- const log$20 = logger.child("repl:command-registry");
27
+ const log$21 = logger.child("repl:command-registry");
27
28
  /**
28
29
  * 命令注册表
29
30
  */
@@ -39,7 +40,7 @@ var CommandRegistry = class {
39
40
  */
40
41
  register(cmd) {
41
42
  const canonicalName = cmd.name.toLowerCase();
42
- if (this.commands.has(canonicalName)) log$20.warn(`命令 "${canonicalName}" 已存在,将被覆盖`);
43
+ if (this.commands.has(canonicalName)) log$21.warn(`命令 "${canonicalName}" 已存在,将被覆盖`);
43
44
  this.commands.set(canonicalName, cmd);
44
45
  for (const alias of cmd.aliases) {
45
46
  const lowerAlias = alias.toLowerCase();
@@ -63,11 +64,34 @@ var CommandRegistry = class {
63
64
  return Array.from(this.commands.values());
64
65
  }
65
66
  /**
67
+ * 注销单个命令
68
+ */
69
+ unregister(name) {
70
+ const lowerName = name.toLowerCase();
71
+ const cmd = this.commands.get(lowerName);
72
+ if (!cmd) return false;
73
+ for (const alias of cmd.aliases) this.aliasMap.delete(alias.toLowerCase());
74
+ return this.commands.delete(lowerName);
75
+ }
76
+ /**
77
+ * 注销所有指定来源的命令
78
+ *
79
+ * @returns 被注销的命令名称列表
80
+ */
81
+ unregisterBySource(source) {
82
+ const removed = [];
83
+ for (const [name, cmd] of this.commands) if (cmd.source === source) {
84
+ this.unregister(name);
85
+ removed.push(name);
86
+ }
87
+ return removed;
88
+ }
89
+ /**
66
90
  * 分发并执行命令
67
91
  */
68
92
  async dispatch(parsed) {
69
93
  if (parsed.kind !== "command") {
70
- log$20.warn("dispatch 收到了非 command 类型的输入");
94
+ log$21.warn("dispatch 收到了非 command 类型的输入");
71
95
  return;
72
96
  }
73
97
  const cmd = this.getCommand(parsed.name);
@@ -79,7 +103,7 @@ var CommandRegistry = class {
79
103
  await cmd.handler(parsed.args, this.session);
80
104
  } catch (error) {
81
105
  const message = error instanceof Error ? error.message : String(error);
82
- log$20.error(`命令 /${cmd.name} 执行出错`, {}, error);
106
+ log$21.error(`命令 /${cmd.name} 执行出错`, {}, error);
83
107
  console.log(`\n 命令执行出错: ${message}\n`);
84
108
  }
85
109
  }
@@ -456,7 +480,7 @@ const noColor = {
456
480
  *
457
481
  * @module core/agent-team
458
482
  */
459
- const log$19 = logger.child("agent-instance-manager");
483
+ const log$20 = logger.child("agent-instance-manager");
460
484
  /**
461
485
  * Agent 实例状态转换表
462
486
  *
@@ -521,7 +545,7 @@ var AgentInstanceManager = class extends EventEmitter {
521
545
  const parent = this.instances.get(parentInstanceId);
522
546
  if (parent) parent.childInstanceIds.push(instance.instanceId);
523
547
  }
524
- log$19.debug("注册 Agent 实例", {
548
+ log$20.debug("注册 Agent 实例", {
525
549
  instanceId: instance.instanceId,
526
550
  typeId: definition.typeId,
527
551
  depth,
@@ -544,12 +568,12 @@ var AgentInstanceManager = class extends EventEmitter {
544
568
  transition(instanceId, newStatus) {
545
569
  const instance = this.instances.get(instanceId);
546
570
  if (!instance) {
547
- log$19.warn("状态转换失败:实例不存在", { instanceId });
571
+ log$20.warn("状态转换失败:实例不存在", { instanceId });
548
572
  return false;
549
573
  }
550
574
  const allowed = VALID_TRANSITIONS[instance.status];
551
575
  if (!allowed.includes(newStatus)) {
552
- log$19.warn("状态转换拒绝", {
576
+ log$20.warn("状态转换拒绝", {
553
577
  instanceId,
554
578
  from: instance.status,
555
579
  to: newStatus,
@@ -559,7 +583,7 @@ var AgentInstanceManager = class extends EventEmitter {
559
583
  }
560
584
  const oldStatus = instance.status;
561
585
  instance.status = newStatus;
562
- log$19.debug("Agent 实例状态转换", {
586
+ log$20.debug("Agent 实例状态转换", {
563
587
  instanceId,
564
588
  typeId: instance.typeId,
565
589
  from: oldStatus,
@@ -690,7 +714,7 @@ var AgentInstanceManager = class extends EventEmitter {
690
714
  instance.agent.removeAllListeners();
691
715
  instance.agent.systemPromptOverride = null;
692
716
  this.instances.delete(instanceId);
693
- log$19.debug("Agent 实例已清理", { instanceId });
717
+ log$20.debug("Agent 实例已清理", { instanceId });
694
718
  }
695
719
  /**
696
720
  * 清理所有终态的实例
@@ -1141,7 +1165,7 @@ const BUILTIN_AGENT_TYPES = [
1141
1165
  *
1142
1166
  * @module core/agent-team
1143
1167
  */
1144
- const log$18 = logger.child("agent-type-registry");
1168
+ const log$19 = logger.child("agent-type-registry");
1145
1169
  /**
1146
1170
  * Agent 类型注册中心
1147
1171
  *
@@ -1165,14 +1189,14 @@ var AgentTypeRegistry = class {
1165
1189
  */
1166
1190
  register(definition) {
1167
1191
  const existing = this.types.get(definition.typeId);
1168
- if (existing) log$18.info("覆盖已注册的 Agent 类型", {
1192
+ if (existing) log$19.info("覆盖已注册的 Agent 类型", {
1169
1193
  typeId: definition.typeId,
1170
1194
  oldSource: existing.source,
1171
1195
  newSource: definition.source
1172
1196
  });
1173
1197
  this.types.set(definition.typeId, definition);
1174
1198
  this.refreshCache();
1175
- log$18.debug("注册 Agent 类型", {
1199
+ log$19.debug("注册 Agent 类型", {
1176
1200
  typeId: definition.typeId,
1177
1201
  source: definition.source
1178
1202
  });
@@ -1185,7 +1209,7 @@ var AgentTypeRegistry = class {
1185
1209
  registerAll(definitions) {
1186
1210
  for (const def of definitions) this.types.set(def.typeId, def);
1187
1211
  this.refreshCache();
1188
- log$18.info("批量注册 Agent 类型", { count: definitions.length });
1212
+ log$19.info("批量注册 Agent 类型", { count: definitions.length });
1189
1213
  }
1190
1214
  /**
1191
1215
  * 注销 Agent 类型
@@ -1197,7 +1221,7 @@ var AgentTypeRegistry = class {
1197
1221
  const result = this.types.delete(typeId);
1198
1222
  if (result) {
1199
1223
  this.refreshCache();
1200
- log$18.debug("注销 Agent 类型", { typeId });
1224
+ log$19.debug("注销 Agent 类型", { typeId });
1201
1225
  }
1202
1226
  return result;
1203
1227
  }
@@ -1284,7 +1308,7 @@ var AgentTypeRegistry = class {
1284
1308
  loadBuiltinTypes() {
1285
1309
  for (const def of BUILTIN_AGENT_TYPES) this.types.set(def.typeId, def);
1286
1310
  this.refreshCache();
1287
- log$18.info("加载内置 Agent 类型", { count: BUILTIN_AGENT_TYPES.length });
1311
+ log$19.info("加载内置 Agent 类型", { count: BUILTIN_AGENT_TYPES.length });
1288
1312
  }
1289
1313
  /**
1290
1314
  * 刷新可见类型缓存
@@ -1445,11 +1469,11 @@ function formatTokens(tokens) {
1445
1469
  function createClearCommand() {
1446
1470
  return {
1447
1471
  name: "clear",
1448
- aliases: [],
1449
- description: "清除屏幕",
1472
+ aliases: ["reset", "new"],
1473
+ description: "清空会话上下文,重置 Agent 状态",
1450
1474
  usage: "/clear",
1451
1475
  handler(_args, session) {
1452
- session.clearOutput();
1476
+ session.clearAgentContext();
1453
1477
  session.getInputParser().reset();
1454
1478
  }
1455
1479
  };
@@ -3120,6 +3144,16 @@ var AgentStatusBar = class extends Container {
3120
3144
  #loadingTimer;
3121
3145
  /** 上次活跃实例快照(用于检测变化) */
3122
3146
  #lastActiveCount = 0;
3147
+ /** 当前模型名称 */
3148
+ #modelName = null;
3149
+ /** 累积非缓存 input tokens(usage.input = totalInput - cacheRead) */
3150
+ #inputTokens = 0;
3151
+ /** 累积 cache read tokens(缓存命中) */
3152
+ #cacheReadTokens = 0;
3153
+ /** 累积 output tokens */
3154
+ #outputTokens = 0;
3155
+ /** 任务已耗时(ms) */
3156
+ #durationMs = 0;
3123
3157
  /**
3124
3158
  * 切换展开/折叠状态
3125
3159
  */
@@ -3131,6 +3165,42 @@ var AgentStatusBar = class extends Container {
3131
3165
  get isExpanded() {
3132
3166
  return this.#expanded;
3133
3167
  }
3168
+ /**
3169
+ * 设置当前模型名称
3170
+ */
3171
+ setModelName(name) {
3172
+ this.#modelName = name;
3173
+ this.invalidate();
3174
+ }
3175
+ /**
3176
+ * 更新 Token 统计数据
3177
+ *
3178
+ * @param inputTokens - 非缓存 input tokens(usage.input)
3179
+ * @param cacheRead - 缓存读取 tokens(usage.cacheRead)
3180
+ * @param outputTokens - 输出 tokens
3181
+ * @param durationMs - 任务已耗时(毫秒)
3182
+ */
3183
+ updateTokenStats(inputTokens, cacheRead, outputTokens, durationMs) {
3184
+ this.#inputTokens = inputTokens;
3185
+ this.#cacheReadTokens = cacheRead;
3186
+ this.#outputTokens = outputTokens;
3187
+ if (durationMs !== void 0) this.#durationMs = durationMs;
3188
+ this.invalidate();
3189
+ }
3190
+ /**
3191
+ * 清除 Token 数据(执行结束后调用)
3192
+ */
3193
+ clearTokenStats() {
3194
+ this.#modelName = null;
3195
+ this.#inputTokens = 0;
3196
+ this.#cacheReadTokens = 0;
3197
+ this.#outputTokens = 0;
3198
+ this.invalidate();
3199
+ }
3200
+ /** 是否有 Token 信息要显示 */
3201
+ get hasTokenInfo() {
3202
+ return this.#modelName !== null;
3203
+ }
3134
3204
  invalidate() {
3135
3205
  super.invalidate();
3136
3206
  }
@@ -3139,6 +3209,7 @@ var AgentStatusBar = class extends Container {
3139
3209
  if (activeInstances.length === 0) {
3140
3210
  this.#stopLoading();
3141
3211
  this.#lastActiveCount = 0;
3212
+ if (this.hasTokenInfo) return [truncateToWidth(this.#renderTokenInfoLine(), width)];
3142
3213
  return [];
3143
3214
  }
3144
3215
  if (this.#lastActiveCount === 0) this.#startLoading();
@@ -3148,8 +3219,15 @@ var AgentStatusBar = class extends Container {
3148
3219
  }, 0);
3149
3220
  const totalDuration = this.#formatDuration(Math.max(...activeInstances.map((i) => Date.now() - i.createdAt)));
3150
3221
  const frame = LOADING_FRAMES$2[this.#loadingFrame % LOADING_FRAMES$2.length] ?? "";
3151
- if (!this.#expanded) return [this.#renderCollapsed(frame, activeInstances.length, totalToolUses, totalDuration).slice(0, width)];
3152
- return this.#renderExpanded(frame, activeInstances, width);
3222
+ if (!this.#expanded) {
3223
+ const lines = [];
3224
+ lines.push(truncateToWidth(this.#renderCollapsed(frame, activeInstances.length, totalToolUses, totalDuration), width));
3225
+ if (this.hasTokenInfo) lines.push(truncateToWidth(this.#renderTokenInfoLine(), width));
3226
+ return lines;
3227
+ }
3228
+ const lines = this.#renderExpanded(frame, activeInstances, width);
3229
+ if (this.hasTokenInfo) lines.push(truncateToWidth(this.#renderTokenInfoLine(), width));
3230
+ return lines;
3153
3231
  }
3154
3232
  /** 渲染折叠模式单行 */
3155
3233
  #renderCollapsed(frame, count, toolUses, duration) {
@@ -3161,7 +3239,7 @@ var AgentStatusBar = class extends Container {
3161
3239
  const count = instances.length;
3162
3240
  const header = chalk.cyan(` ${frame} Running ${count} agent${count > 1 ? "s" : ""}...`);
3163
3241
  const hint = chalk.dim("(ctrl+o to collapse)");
3164
- lines.push(`${header} ${hint}`.slice(0, width));
3242
+ lines.push(truncateToWidth(`${header} ${hint}`, width));
3165
3243
  for (let i = 0; i < instances.length; i++) {
3166
3244
  const inst = instances[i];
3167
3245
  const isLast = i === instances.length - 1;
@@ -3175,11 +3253,11 @@ var AgentStatusBar = class extends Container {
3175
3253
  const toolUsesStr = act ? chalk.gray(`\u00B7 ${act.toolUses} tool uses`) : "";
3176
3254
  const durationStr = chalk.gray(`\u00B7 ${this.#formatDuration(Date.now() - inst.createdAt)}`);
3177
3255
  const line1 = ` ${chalk.dim(connector)}${statusColor(icon)}${chalk.reset} ${typeLabel} ${chalk.dim(taskPreview)} ${toolUsesStr} ${durationStr}`;
3178
- lines.push(line1.slice(0, width));
3256
+ lines.push(truncateToWidth(line1, width));
3179
3257
  if (act) {
3180
3258
  const toolDisplay = act.args ? `${act.toolName}: ${act.args.slice(0, 60)}` : act.toolName;
3181
3259
  const line2 = ` ${chalk.dim(childPrefix + TREE_CONT)}${chalk.cyan(toolDisplay)}`;
3182
- lines.push(line2.slice(0, width));
3260
+ lines.push(truncateToWidth(line2, width));
3183
3261
  }
3184
3262
  }
3185
3263
  return lines;
@@ -3205,6 +3283,33 @@ var AgentStatusBar = class extends Container {
3205
3283
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
3206
3284
  return `${Math.floor(ms / 6e4)}m${Math.floor(ms % 6e4 / 1e3)}s`;
3207
3285
  }
3286
+ /** 格式化 Token 数字
3287
+ *
3288
+ * - < 1,000: 原始数字(456)
3289
+ * - ≥ 1,000: 千分位分隔(1,234)
3290
+ * - ≥ 10,000: K 单位(15.3K)
3291
+ * - ≥ 1,000,000: M 单位(1.2M)
3292
+ */
3293
+ #formatTokenCount(n) {
3294
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
3295
+ if (n >= 1e4) return `${(n / 1e3).toFixed(1)}K`;
3296
+ return n.toLocaleString();
3297
+ }
3298
+ /** 渲染 Token 信息行 */
3299
+ #renderTokenInfoLine() {
3300
+ const modelStr = chalk.cyan(this.#modelName ?? "");
3301
+ const separator = chalk.gray(" · ");
3302
+ const totalInput = this.#inputTokens + this.#cacheReadTokens;
3303
+ const missTokens = this.#inputTokens;
3304
+ const cacheRate = totalInput > 0 ? Math.round(this.#cacheReadTokens / totalInput * 100) : 0;
3305
+ const inStr = chalk.white(this.#formatTokenCount(totalInput));
3306
+ const hitStr = chalk.green(this.#formatTokenCount(this.#cacheReadTokens));
3307
+ const rateStr = cacheRate >= 80 ? chalk.green(`${cacheRate}%`) : chalk.yellow(`${cacheRate}%`);
3308
+ const missStr = chalk.yellow(this.#formatTokenCount(missTokens));
3309
+ const outStr = chalk.cyan(this.#formatTokenCount(this.#outputTokens));
3310
+ const durationStr = chalk.magenta(this.#formatDuration(this.#durationMs));
3311
+ return ` ${modelStr}${separator}${chalk.gray("IN")} ${inStr}${separator}${chalk.gray("HIT")} ${hitStr} ${chalk.dim(`(${rateStr})`)}${separator}${chalk.gray("MISS")} ${missStr}${separator}${chalk.gray("OUT")} ${outStr}${separator}${durationStr}`;
3312
+ }
3208
3313
  };
3209
3314
 
3210
3315
  //#endregion
@@ -3975,7 +4080,7 @@ const CRON_CONSTANTS = {
3975
4080
  *
3976
4081
  * @module cli/repl/cron/cron-scheduler
3977
4082
  */
3978
- const log$17 = logger.child("cron:scheduler");
4083
+ const log$18 = logger.child("cron:scheduler");
3979
4084
  var CronScheduler = class extends EventEmitter {
3980
4085
  store;
3981
4086
  jobs = [];
@@ -3996,7 +4101,7 @@ var CronScheduler = class extends EventEmitter {
3996
4101
  if (this.running) return;
3997
4102
  const loadedJobs = await this.store.load();
3998
4103
  this.jobs = loadedJobs;
3999
- log$17.info(`调度器启动,加载 ${loadedJobs.length} 个 durable 任务`);
4104
+ log$18.info(`调度器启动,加载 ${loadedJobs.length} 个 durable 任务`);
4000
4105
  await this.handleMissedJobs();
4001
4106
  this.checkAutoExpiry();
4002
4107
  this.running = true;
@@ -4012,7 +4117,7 @@ var CronScheduler = class extends EventEmitter {
4012
4117
  clearInterval(this.timer);
4013
4118
  this.timer = null;
4014
4119
  }
4015
- log$17.info("调度器已停止");
4120
+ log$18.info("调度器已停止");
4016
4121
  }
4017
4122
  /** 添加任务 */
4018
4123
  async addJob(job) {
@@ -4024,7 +4129,7 @@ var CronScheduler = class extends EventEmitter {
4024
4129
  this.jobs.push(job);
4025
4130
  await this.store.persist(this.jobs);
4026
4131
  } else this.sessionJobs.push(job);
4027
- log$17.info("任务已添加", {
4132
+ log$18.info("任务已添加", {
4028
4133
  id: job.id,
4029
4134
  cron: job.cron,
4030
4135
  durable: job.durable
@@ -4154,7 +4259,7 @@ var CronScheduler = class extends EventEmitter {
4154
4259
  }, delay);
4155
4260
  }
4156
4261
  if (toDelete.length > 0) {
4157
- log$17.info(`跳过 ${toDelete.length} 个错过的一次性任务(超出补发上限)`);
4262
+ log$18.info(`跳过 ${toDelete.length} 个错过的一次性任务(超出补发上限)`);
4158
4263
  this.emit("missed-overflow", {
4159
4264
  count: toDelete.length,
4160
4265
  jobIds: toDelete.map((m) => m.id)
@@ -4172,7 +4277,7 @@ var CronScheduler = class extends EventEmitter {
4172
4277
  job.lastFiredAt = now;
4173
4278
  job.fireCount++;
4174
4279
  this.removeJob(job.id);
4175
- log$17.info("任务已过期并触发最后一次", { id: job.id });
4280
+ log$18.info("任务已过期并触发最后一次", { id: job.id });
4176
4281
  }
4177
4282
  }
4178
4283
  }
@@ -4233,7 +4338,7 @@ function applyOneShotJitter(jobId, rawNext) {
4233
4338
  *
4234
4339
  * @module cli/repl/cron/cron-store
4235
4340
  */
4236
- const log$16 = logger.child("cron:store");
4341
+ const log$17 = logger.child("cron:store");
4237
4342
  const STORE_FILE = join(join(homedir(), ".zapmyco", "cron"), "scheduled_tasks.json");
4238
4343
  var CronStore = class {
4239
4344
  filePath;
@@ -4257,13 +4362,13 @@ var CronStore = class {
4257
4362
  const raw = await readFile(this.filePath, "utf-8");
4258
4363
  const data = JSON.parse(raw);
4259
4364
  if (!Array.isArray(data)) {
4260
- log$16.warn("存储文件格式无效(非数组),将使用空列表");
4365
+ log$17.warn("存储文件格式无效(非数组),将使用空列表");
4261
4366
  return [];
4262
4367
  }
4263
4368
  return this.validateJobs(data);
4264
4369
  } catch (err) {
4265
4370
  if (err.code === "ENOENT") return [];
4266
- log$16.warn("加载定时任务文件失败,将使用空列表", { error: err instanceof Error ? err.message : String(err) });
4371
+ log$17.warn("加载定时任务文件失败,将使用空列表", { error: err instanceof Error ? err.message : String(err) });
4267
4372
  return [];
4268
4373
  }
4269
4374
  }
@@ -4310,7 +4415,7 @@ var CronStore = class {
4310
4415
  if (typeof obj.maxFires === "number") job.maxFires = obj.maxFires;
4311
4416
  valid.push(job);
4312
4417
  }
4313
- if (valid.length < raw.length) log$16.warn(`跳过 ${raw.length - valid.length} 个无效任务条目`);
4418
+ if (valid.length < raw.length) log$17.warn(`跳过 ${raw.length - valid.length} 个无效任务条目`);
4314
4419
  return valid;
4315
4420
  }
4316
4421
  };
@@ -4328,7 +4433,7 @@ function getCronStore() {
4328
4433
  * 基于内存的环形缓冲区,记录 REPL 会话中的用户输入和执行结果。
4329
4434
  * 支持文件持久化到 ~/.zapmyco/history.json,跨会话恢复。
4330
4435
  */
4331
- const log$15 = logger.child("history:store");
4436
+ const log$16 = logger.child("history:store");
4332
4437
  /** 默认最大历史条数 */
4333
4438
  const DEFAULT_MAX_SIZE = 100;
4334
4439
  /** 历史文件存储路径 */
@@ -4387,13 +4492,13 @@ var HistoryStore = class {
4387
4492
  if (Array.isArray(data.entries)) {
4388
4493
  this.entries = data.entries.slice(-this.maxSize);
4389
4494
  this.nextId = typeof data.nextId === "number" ? data.nextId : 1;
4390
- log$15.debug("历史记录已加载", {
4495
+ log$16.debug("历史记录已加载", {
4391
4496
  count: this.entries.length,
4392
4497
  nextId: this.nextId
4393
4498
  });
4394
4499
  }
4395
4500
  } catch {
4396
- log$15.debug("无历史文件或加载失败,使用空历史");
4501
+ log$16.debug("无历史文件或加载失败,使用空历史");
4397
4502
  }
4398
4503
  }
4399
4504
  /** 持久化历史记录到文件 */
@@ -4406,7 +4511,7 @@ var HistoryStore = class {
4406
4511
  }, null, 2);
4407
4512
  writeFileSync(this.filePath, data, "utf-8");
4408
4513
  } catch (err) {
4409
- log$15.warn("历史记录保存失败", { error: err instanceof Error ? err.message : String(err) });
4514
+ log$16.warn("历史记录保存失败", { error: err instanceof Error ? err.message : String(err) });
4410
4515
  }
4411
4516
  }
4412
4517
  };
@@ -5288,7 +5393,7 @@ var Renderer = class {
5288
5393
  *
5289
5394
  * @module core/agent-team
5290
5395
  */
5291
- const log$14 = logger.child("background-store");
5396
+ const log$15 = logger.child("background-store");
5292
5397
  /**
5293
5398
  * 后台任务持久化存储
5294
5399
  *
@@ -5318,14 +5423,14 @@ var BackgroundTaskStore = class {
5318
5423
  const entries = JSON.parse(raw);
5319
5424
  this.tasks.clear();
5320
5425
  for (const entry of entries) this.tasks.set(entry.taskId, entry);
5321
- log$14.debug("后台任务已加载", {
5426
+ log$15.debug("后台任务已加载", {
5322
5427
  count: entries.length,
5323
5428
  path: this.filePath
5324
5429
  });
5325
5430
  return entries;
5326
5431
  }
5327
5432
  } catch (err) {
5328
- log$14.warn("后台任务加载失败", {
5433
+ log$15.warn("后台任务加载失败", {
5329
5434
  error: String(err),
5330
5435
  path: this.filePath
5331
5436
  });
@@ -5384,7 +5489,7 @@ var BackgroundTaskStore = class {
5384
5489
  const entries = Array.from(this.tasks.values());
5385
5490
  writeFileSync(this.filePath, JSON.stringify(entries, null, 2), "utf-8");
5386
5491
  } catch (err) {
5387
- log$14.error("后台任务持久化失败", {
5492
+ log$15.error("后台任务持久化失败", {
5388
5493
  error: String(err),
5389
5494
  path: this.filePath
5390
5495
  });
@@ -5410,7 +5515,7 @@ var BackgroundTaskStore = class {
5410
5515
  }
5411
5516
  if (cleaned > 0) {
5412
5517
  this.persist();
5413
- log$14.info("清理过期后台任务", { cleaned });
5518
+ log$15.info("清理过期后台任务", { cleaned });
5414
5519
  }
5415
5520
  return cleaned;
5416
5521
  }
@@ -5427,7 +5532,7 @@ var BackgroundTaskStore = class {
5427
5532
  *
5428
5533
  * @module core/agent-team
5429
5534
  */
5430
- const log$13 = logger.child("agent-message-bus");
5535
+ const log$14 = logger.child("agent-message-bus");
5431
5536
  /**
5432
5537
  * Agent 消息总线(单例)
5433
5538
  *
@@ -5461,12 +5566,12 @@ var AgentMessageBus = class {
5461
5566
  };
5462
5567
  const targetInstance = getAgentInstanceManager().get(toAgentId);
5463
5568
  if (targetInstance) targetInstance.inbox.push(fullMessage);
5464
- else log$13.warn("目标 Agent 实例不存在,消息丢弃", {
5569
+ else log$14.warn("目标 Agent 实例不存在,消息丢弃", {
5465
5570
  toAgentId,
5466
5571
  messageId: fullMessage.messageId
5467
5572
  });
5468
5573
  this.emitter.emit(`msg:${toAgentId}`, fullMessage);
5469
- log$13.debug("消息已投递", {
5574
+ log$14.debug("消息已投递", {
5470
5575
  from: fromAgentId,
5471
5576
  to: toAgentId,
5472
5577
  type: fullMessage.type,
@@ -5549,7 +5654,7 @@ function getAgentMessageBus() {
5549
5654
  *
5550
5655
  * @module core/agent-team
5551
5656
  */
5552
- const log$12 = logger.child("background-agent-manager");
5657
+ const log$13 = logger.child("background-agent-manager");
5553
5658
  /**
5554
5659
  * 后台 Agent 管理器(单例)
5555
5660
  */
@@ -5606,7 +5711,7 @@ var BackgroundAgentManager = class {
5606
5711
  parentAgentId: params.parentInstanceId
5607
5712
  });
5608
5713
  this.runAsync(taskId, params, abortController, timeoutMs).catch((err) => {
5609
- log$12.error("后台 Agent 意外崩溃", {
5714
+ log$13.error("后台 Agent 意外崩溃", {
5610
5715
  taskId,
5611
5716
  error: String(err)
5612
5717
  });
@@ -5626,7 +5731,7 @@ var BackgroundAgentManager = class {
5626
5731
  const orchestrator = this.orchestrator;
5627
5732
  const messageBus = getAgentMessageBus();
5628
5733
  const timeoutHandle = setTimeout(() => {
5629
- log$12.warn("后台 Agent 超时,自动取消", {
5734
+ log$13.warn("后台 Agent 超时,自动取消", {
5630
5735
  taskId,
5631
5736
  timeoutMs
5632
5737
  });
@@ -5670,7 +5775,7 @@ var BackgroundAgentManager = class {
5670
5775
  taskId,
5671
5776
  requiresResponse: false
5672
5777
  });
5673
- log$12.info("后台 Agent 完成通知已发送", {
5778
+ log$13.info("后台 Agent 完成通知已发送", {
5674
5779
  taskId,
5675
5780
  instanceId: result.instanceId,
5676
5781
  parentId: params.parentInstanceId,
@@ -5697,7 +5802,7 @@ var BackgroundAgentManager = class {
5697
5802
  completedAt: Date.now(),
5698
5803
  error
5699
5804
  });
5700
- log$12.warn("后台 Agent 失败", {
5805
+ log$13.warn("后台 Agent 失败", {
5701
5806
  taskId,
5702
5807
  error
5703
5808
  });
@@ -5734,7 +5839,7 @@ var BackgroundAgentManager = class {
5734
5839
  if (runtime.instanceId) try {
5735
5840
  await getAgentInstanceManager().cancel(runtime.instanceId);
5736
5841
  } catch {}
5737
- log$12.info("后台 Agent 已取消", { taskId });
5842
+ log$13.info("后台 Agent 已取消", { taskId });
5738
5843
  return true;
5739
5844
  }
5740
5845
  /**
@@ -5743,13 +5848,13 @@ var BackgroundAgentManager = class {
5743
5848
  restore() {
5744
5849
  this.store.load();
5745
5850
  const stale = this.store.cleanStale();
5746
- if (stale > 0) log$12.info("跨会话恢复:清理了过期后台任务", { count: stale });
5851
+ if (stale > 0) log$13.info("跨会话恢复:清理了过期后台任务", { count: stale });
5747
5852
  const active = this.store.listActive();
5748
5853
  for (const entry of active) this.store.updateStatus(entry.taskId, "failed", {
5749
5854
  completedAt: Date.now(),
5750
5855
  error: "会话终止导致任务丢失"
5751
5856
  });
5752
- if (active.length > 0) log$12.info("跨会话恢复:标记活跃任务为 failed", { count: active.length });
5857
+ if (active.length > 0) log$13.info("跨会话恢复:标记活跃任务为 failed", { count: active.length });
5753
5858
  }
5754
5859
  };
5755
5860
  /** 全局单例 */
@@ -5968,7 +6073,7 @@ function createDeferred() {
5968
6073
  *
5969
6074
  * @module core/question/question-manager
5970
6075
  */
5971
- const log$11 = logger.child("question-manager");
6076
+ const log$12 = logger.child("question-manager");
5972
6077
  /** 问题超时时间(5 分钟) */
5973
6078
  const QUESTION_TIMEOUT_MS = 300 * 1e3;
5974
6079
  var QuestionManager = class {
@@ -6014,13 +6119,13 @@ var QuestionManager = class {
6014
6119
  requestId,
6015
6120
  questionCount: params.questions.length
6016
6121
  });
6017
- log$11.debug("提问已发出", {
6122
+ log$12.debug("提问已发出", {
6018
6123
  requestId,
6019
6124
  questionCount: params.questions.length
6020
6125
  });
6021
6126
  const timeout = setTimeout(() => {
6022
6127
  if (!deferred.isSettled) {
6023
- log$11.warn("提问超时,自动取消", { requestId });
6128
+ log$12.warn("提问超时,自动取消", { requestId });
6024
6129
  deferred.reject(/* @__PURE__ */ new Error("提问超时,用户未在 5 分钟内回答"));
6025
6130
  this.pending.delete(requestId);
6026
6131
  eventBus.emit("question:timeout", { requestId });
@@ -6034,7 +6139,7 @@ var QuestionManager = class {
6034
6139
  requestId,
6035
6140
  answerCount: Object.keys(result.answers).length
6036
6141
  });
6037
- log$11.debug("提问已回答", { requestId });
6142
+ log$12.debug("提问已回答", { requestId });
6038
6143
  return result;
6039
6144
  } catch (err) {
6040
6145
  clearTimeout(timeout);
@@ -6043,7 +6148,7 @@ var QuestionManager = class {
6043
6148
  requestId,
6044
6149
  reason: err instanceof Error ? err.message : String(err)
6045
6150
  });
6046
- log$11.debug("提问已取消", {
6151
+ log$12.debug("提问已取消", {
6047
6152
  requestId,
6048
6153
  error: err instanceof Error ? err.message : String(err)
6049
6154
  });
@@ -6065,7 +6170,7 @@ var QuestionManager = class {
6065
6170
  const count = this.pending.size;
6066
6171
  for (const [, entry] of this.pending) if (!entry.deferred.isSettled) entry.deferred.reject(error);
6067
6172
  this.pending.clear();
6068
- if (count > 0) log$11.debug("已清理所有待处理问题", { count });
6173
+ if (count > 0) log$12.debug("已清理所有待处理问题", { count });
6069
6174
  }
6070
6175
  };
6071
6176
  let globalQuestionManager = null;
@@ -9713,6 +9818,137 @@ function parseArgs(args) {
9713
9818
  if (current.length > 0) result.push(current);
9714
9819
  return result;
9715
9820
  }
9821
+ /** 代码块模式: ```! ... ``` */
9822
+ const SHELL_BLOCK_PATTERN = /```!\s*\n?([\s\S]*?)\n?```/g;
9823
+ /** 行内模式: !`command`(前面需有空白或行首) */
9824
+ const SHELL_INLINE_PATTERN = /(?<=^|\s)!`([^`]+)`/gm;
9825
+ /** Shell 命令默认超时(秒) */
9826
+ const SKILL_SHELL_TIMEOUT_SEC = 30;
9827
+ /** Shell 命令输出最大字符数 */
9828
+ const SKILL_SHELL_MAX_OUTPUT_CHARS = 1e4;
9829
+ /**
9830
+ * 执行 SKILL.md 中的 Shell 命令
9831
+ *
9832
+ * 支持两种语法:
9833
+ * 1. 代码块: ```! echo "hello" ```
9834
+ * 2. 行内: 前导 !`echo "hello"`
9835
+ */
9836
+ async function executeShellCommandsInSkill(content) {
9837
+ if (!content.includes("```!") && !content.includes("!`")) return content;
9838
+ let result = content;
9839
+ result = await replaceBlockCommands(result);
9840
+ result = await replaceInlineCommands(result);
9841
+ return result;
9842
+ }
9843
+ /** 处理 ```! ... ``` 代码块 */
9844
+ async function replaceBlockCommands(content) {
9845
+ const matches = [];
9846
+ const pattern = new RegExp(SHELL_BLOCK_PATTERN.source, "g");
9847
+ for (let match = pattern.exec(content); match !== null; match = pattern.exec(content)) matches.push({
9848
+ full: match[0],
9849
+ command: (match[1] ?? "").trim()
9850
+ });
9851
+ if (matches.length === 0) return content;
9852
+ const results = await Promise.all(matches.map(async (m) => ({
9853
+ target: m.full,
9854
+ replacement: await executeSingleCommand(m.command, false)
9855
+ })));
9856
+ let result = content;
9857
+ for (const { target, replacement } of results) result = result.replace(target, replacement);
9858
+ return result;
9859
+ }
9860
+ /** 处理 !`command` 行内命令 */
9861
+ async function replaceInlineCommands(content) {
9862
+ const matches = [];
9863
+ const pattern = new RegExp(SHELL_INLINE_PATTERN.source, "gm");
9864
+ for (let match = pattern.exec(content); match !== null; match = pattern.exec(content)) matches.push({
9865
+ full: match[0],
9866
+ command: (match[1] ?? "").trim()
9867
+ });
9868
+ if (matches.length === 0) return content;
9869
+ let result = content;
9870
+ for (const m of matches) {
9871
+ const output = await executeSingleCommand(m.command, true);
9872
+ result = result.replace(m.full, output);
9873
+ }
9874
+ return result;
9875
+ }
9876
+ /** 执行单个 Shell 命令并返回替换文本 */
9877
+ async function executeSingleCommand(command, inline) {
9878
+ if (!command) return "";
9879
+ const security = checkCommandSecurity(command);
9880
+ if (security.blocked) return `[Shell 命令被阻断] 命令: ${command} 原因: ${security.reason ?? "未知"}`;
9881
+ if (security.requiresApproval) return `[Shell 命令需审批] 命令: ${command} 原因: ${security.reason ?? "需要用户确认"}`;
9882
+ const shell = process.env.SHELL || "/bin/bash";
9883
+ try {
9884
+ const { stdout, stderr, exitCode } = await spawnCommand(shell, command);
9885
+ return formatShellOutput(stdout, stderr, exitCode, inline);
9886
+ } catch (err) {
9887
+ return `[Shell 命令执行失败] ${err instanceof Error ? err.message : String(err)}`;
9888
+ }
9889
+ }
9890
+ /** spawn 执行命令并返回结构化结果 */
9891
+ function spawnCommand(shell, command) {
9892
+ return new Promise((resolve) => {
9893
+ const child = spawn(shell, ["-c", command], {
9894
+ env: sanitizeEnv(),
9895
+ stdio: [
9896
+ "pipe",
9897
+ "pipe",
9898
+ "pipe"
9899
+ ]
9900
+ });
9901
+ const stdoutChunks = [];
9902
+ const stderrChunks = [];
9903
+ child.stdout?.on("data", (data) => {
9904
+ stdoutChunks.push(data.toString());
9905
+ });
9906
+ child.stderr?.on("data", (data) => {
9907
+ stderrChunks.push(data.toString());
9908
+ });
9909
+ let settled = false;
9910
+ const settle = (result) => {
9911
+ if (settled) return;
9912
+ settled = true;
9913
+ clearTimeout(timer);
9914
+ resolve(result);
9915
+ };
9916
+ child.on("exit", (code) => {
9917
+ settle({
9918
+ stdout: stdoutChunks.join(""),
9919
+ stderr: stderrChunks.join(""),
9920
+ exitCode: code
9921
+ });
9922
+ });
9923
+ child.on("error", () => {
9924
+ settle({
9925
+ stdout: "",
9926
+ stderr: "进程启动失败",
9927
+ exitCode: -1
9928
+ });
9929
+ });
9930
+ const timer = setTimeout(() => {
9931
+ try {
9932
+ child.kill("SIGTERM");
9933
+ } catch {}
9934
+ setTimeout(() => {
9935
+ try {
9936
+ child.kill("SIGKILL");
9937
+ } catch {}
9938
+ }, 2e3);
9939
+ }, SKILL_SHELL_TIMEOUT_SEC * 1e3);
9940
+ });
9941
+ }
9942
+ /** 格式化 shell 命令输出 */
9943
+ function formatShellOutput(stdout, stderr, exitCode, inline) {
9944
+ let output = stdout;
9945
+ if (stderr) output += (output ? "\n" : "") + (inline ? `[stderr: ${stderr.trim()}]` : `[stderr]\n${stderr.trim()}`);
9946
+ output = stripAnsi(output);
9947
+ output = redactSensitiveInfo(output);
9948
+ output = truncateOutput(output, SKILL_SHELL_MAX_OUTPUT_CHARS);
9949
+ if (exitCode !== null && exitCode !== 0) output += `\n(退出码: ${exitCode})`;
9950
+ return output || "(无输出)";
9951
+ }
9716
9952
  /**
9717
9953
  * 创建 Skill 工具
9718
9954
  *
@@ -9781,7 +10017,7 @@ function createSkillTool(_config) {
9781
10017
  const endIdx = content.indexOf("---", 3);
9782
10018
  if (endIdx !== -1) bodyContent = content.slice(endIdx + 3).trim();
9783
10019
  }
9784
- const substituted = substituteVariables(bodyContent, params.args, skill.baseDir, skill.name);
10020
+ const withShell = await executeShellCommandsInSkill(substituteVariables(bodyContent, params.args, skill.baseDir, skill.name));
9785
10021
  const hint = skill.frontmatter["argument-hint"] ? ` [${skill.frontmatter["argument-hint"]}]` : "";
9786
10022
  const instructionParts = [
9787
10023
  `# Skill: ${skill.name}${hint}`,
@@ -9790,12 +10026,12 @@ function createSkillTool(_config) {
9790
10026
  "",
9791
10027
  "---",
9792
10028
  "",
9793
- substituted,
10029
+ withShell,
9794
10030
  "",
9795
10031
  "---",
9796
10032
  `Base directory: ${skill.baseDir}`
9797
10033
  ];
9798
- if (!substituted.includes(params.args ?? "") && params.args) instructionParts.push("", `ARGUMENTS: ${params.args}`);
10034
+ if (!withShell.includes(params.args ?? "") && params.args) instructionParts.push("", `ARGUMENTS: ${params.args}`);
9799
10035
  return {
9800
10036
  content: [{
9801
10037
  type: "text",
@@ -9833,8 +10069,8 @@ function getSkillCommandSpecs(entries) {
9833
10069
  * @param args - 用户传递的参数(可选)
9834
10070
  * @returns 格式化后的完整指令文本
9835
10071
  */
9836
- function formatSkillContent(skill, args) {
9837
- const substituted = substituteVariables(skill.body, args, skill.baseDir, skill.name);
10072
+ async function formatSkillContent(skill, args) {
10073
+ const withShell = await executeShellCommandsInSkill(substituteVariables(skill.body, args, skill.baseDir, skill.name));
9838
10074
  const hint = skill.frontmatter["argument-hint"] ? ` [${skill.frontmatter["argument-hint"]}]` : "";
9839
10075
  const instructionParts = [
9840
10076
  `# Skill: ${skill.name}${hint}`,
@@ -9843,12 +10079,12 @@ function formatSkillContent(skill, args) {
9843
10079
  "",
9844
10080
  "---",
9845
10081
  "",
9846
- substituted,
10082
+ withShell,
9847
10083
  "",
9848
10084
  "---",
9849
10085
  `Base directory: ${skill.baseDir}`
9850
10086
  ];
9851
- if (!substituted.includes(args ?? "") && args) instructionParts.push("", `ARGUMENTS: ${args}`);
10087
+ if (!withShell.includes(args ?? "") && args) instructionParts.push("", `ARGUMENTS: ${args}`);
9852
10088
  return instructionParts.filter(Boolean).join("\n");
9853
10089
  }
9854
10090
  /**
@@ -11360,7 +11596,7 @@ var WorktreeStore = class {
11360
11596
  * @module core/worktree
11361
11597
  */
11362
11598
  const execFileAsync = promisify(execFile);
11363
- const log$10 = logger.child("worktree-manager");
11599
+ const log$11 = logger.child("worktree-manager");
11364
11600
  let globalWorktreeManager = null;
11365
11601
  /** 获取全局 WorktreeManager 实例 */
11366
11602
  function getWorktreeManager() {
@@ -11396,7 +11632,7 @@ var WorktreeManager = class {
11396
11632
  throw new WorktreeError("无法确定 git 仓库根目录,请确保在 git 仓库中运行", "NOT_GIT_REPO");
11397
11633
  }
11398
11634
  try {
11399
- log$10.info("创建 worktree", {
11635
+ log$11.info("创建 worktree", {
11400
11636
  branchName,
11401
11637
  worktreePath,
11402
11638
  gitRoot
@@ -11424,14 +11660,14 @@ var WorktreeManager = class {
11424
11660
  });
11425
11661
  } catch (err) {
11426
11662
  const msg = err instanceof Error ? err.message : String(err);
11427
- log$10.warn("创建分支失败,清理 worktree", {
11663
+ log$11.warn("创建分支失败,清理 worktree", {
11428
11664
  branchName,
11429
11665
  error: msg
11430
11666
  });
11431
11667
  try {
11432
11668
  await this.removeByPath(worktreePath, branchName, true);
11433
11669
  } catch (err) {
11434
- log$10.warn("Worktree 创建失败后清理出错", {
11670
+ log$11.warn("Worktree 创建失败后清理出错", {
11435
11671
  worktreePath,
11436
11672
  error: err instanceof Error ? err.message : String(err)
11437
11673
  });
@@ -11456,7 +11692,7 @@ var WorktreeManager = class {
11456
11692
  createdBy: info.createdBy,
11457
11693
  status: "active"
11458
11694
  });
11459
- log$10.info("Worktree 创建成功", {
11695
+ log$11.info("Worktree 创建成功", {
11460
11696
  id: info.id,
11461
11697
  path: worktreePath
11462
11698
  });
@@ -11468,13 +11704,13 @@ var WorktreeManager = class {
11468
11704
  async remove(id, discardChanges) {
11469
11705
  const info = this.activeWorktrees.get(id);
11470
11706
  if (!info) {
11471
- log$10.warn("尝试删除不存在的 worktree", { id });
11707
+ log$11.warn("尝试删除不存在的 worktree", { id });
11472
11708
  return;
11473
11709
  }
11474
11710
  await this.removeByPath(info.worktreePath, info.branchName, discardChanges);
11475
11711
  this.activeWorktrees.delete(id);
11476
11712
  this.store.delete(id);
11477
- log$10.info("Worktree 已删除", { id });
11713
+ log$11.info("Worktree 已删除", { id });
11478
11714
  }
11479
11715
  /**
11480
11716
  * 通过路径删除 worktree(内部方法)
@@ -11484,7 +11720,7 @@ var WorktreeManager = class {
11484
11720
  try {
11485
11721
  await execFileAsync("git", ["worktree", "prune"], { timeout: 1e4 });
11486
11722
  } catch (err) {
11487
- log$10.warn("Worktree prune 失败(路径不存在)", {
11723
+ log$11.warn("Worktree prune 失败(路径不存在)", {
11488
11724
  worktreePath,
11489
11725
  error: err instanceof Error ? err.message : String(err)
11490
11726
  });
@@ -11498,7 +11734,7 @@ var WorktreeManager = class {
11498
11734
  await execFileAsync("git", args, { timeout: 15e3 });
11499
11735
  } catch (err) {
11500
11736
  const msg = err instanceof Error ? err.message : String(err);
11501
- log$10.warn("git worktree remove 失败", {
11737
+ log$11.warn("git worktree remove 失败", {
11502
11738
  worktreePath,
11503
11739
  error: msg
11504
11740
  });
@@ -11510,7 +11746,7 @@ var WorktreeManager = class {
11510
11746
  branchName
11511
11747
  ], { timeout: 1e4 });
11512
11748
  } catch (err) {
11513
- log$10.warn("删除 worktree 分支失败", {
11749
+ log$11.warn("删除 worktree 分支失败", {
11514
11750
  branchName,
11515
11751
  error: err instanceof Error ? err.message : String(err)
11516
11752
  });
@@ -11518,7 +11754,7 @@ var WorktreeManager = class {
11518
11754
  try {
11519
11755
  await execFileAsync("git", ["worktree", "prune"], { timeout: 1e4 });
11520
11756
  } catch (err) {
11521
- log$10.warn("Worktree prune 失败", { error: err instanceof Error ? err.message : String(err) });
11757
+ log$11.warn("Worktree prune 失败", { error: err instanceof Error ? err.message : String(err) });
11522
11758
  }
11523
11759
  }
11524
11760
  /**
@@ -11540,7 +11776,7 @@ var WorktreeManager = class {
11540
11776
  await this.remove(id, true);
11541
11777
  return { cleaned: true };
11542
11778
  }
11543
- log$10.info("Worktree 有变更,保留", {
11779
+ log$11.info("Worktree 有变更,保留", {
11544
11780
  id,
11545
11781
  path: info.worktreePath
11546
11782
  });
@@ -11560,7 +11796,7 @@ var WorktreeManager = class {
11560
11796
  });
11561
11797
  return stdout.trim().length > 0;
11562
11798
  } catch (err) {
11563
- log$10.warn("检查 worktree 变更状态失败", {
11799
+ log$11.warn("检查 worktree 变更状态失败", {
11564
11800
  worktreePath,
11565
11801
  error: err instanceof Error ? err.message : String(err)
11566
11802
  });
@@ -11580,7 +11816,7 @@ var WorktreeManager = class {
11580
11816
  await this.removeByPath(record.worktreePath, record.branchName, true);
11581
11817
  cleaned++;
11582
11818
  } catch (err) {
11583
- log$10.warn("过期 worktree 清理失败", {
11819
+ log$11.warn("过期 worktree 清理失败", {
11584
11820
  id: record.id,
11585
11821
  path: record.worktreePath,
11586
11822
  error: err instanceof Error ? err.message : String(err)
@@ -11589,7 +11825,7 @@ var WorktreeManager = class {
11589
11825
  this.activeWorktrees.delete(record.id);
11590
11826
  this.store.delete(record.id);
11591
11827
  }
11592
- if (cleaned > 0) log$10.info("过期 worktree 清理完成", { cleaned });
11828
+ if (cleaned > 0) log$11.info("过期 worktree 清理完成", { cleaned });
11593
11829
  return cleaned;
11594
11830
  }
11595
11831
  listActive() {
@@ -11640,7 +11876,7 @@ const AGENT_STANDARD_TOOLS = [
11640
11876
 
11641
11877
  //#endregion
11642
11878
  //#region src/core/agent-team/agent-factory.ts
11643
- const log$9 = logger.child("agent-factory");
11879
+ const log$10 = logger.child("agent-factory");
11644
11880
  /**
11645
11881
  * 创建 Agent 实例
11646
11882
  *
@@ -11658,6 +11894,7 @@ const log$9 = logger.child("agent-factory");
11658
11894
  * @returns 完全初始化的 LlmBasedAgent
11659
11895
  */
11660
11896
  function createAgentFromType(definition, instance, parentAgent, availableTools, config) {
11897
+ const thinkingLevel = resolveAgentThinkingLevel(definition.thinkingLevel, config.thinkingLevel, parentAgent);
11661
11898
  const agent = createLlmBasedAgent({
11662
11899
  agentId: instance.instanceId,
11663
11900
  displayName: definition.displayName,
@@ -11665,14 +11902,15 @@ function createAgentFromType(definition, instance, parentAgent, availableTools,
11665
11902
  runtimeConfig: {
11666
11903
  enabled: true,
11667
11904
  toolExecution: "sequential",
11668
- maxTurns: definition.maxTurns
11905
+ maxTurns: definition.maxTurns,
11906
+ thinkingLevel
11669
11907
  }
11670
11908
  });
11671
11909
  shareParentResources(agent, parentAgent, definition);
11672
11910
  const tools = resolveTools(definition, availableTools, instance.depth, config);
11673
11911
  agent.registerTools(tools);
11674
11912
  agent.systemPromptOverride = buildSystemPrompt(definition, instance.task.description, config);
11675
- log$9.debug("创建 Agent 实例", {
11913
+ log$10.debug("创建 Agent 实例", {
11676
11914
  typeId: definition.typeId,
11677
11915
  instanceId: instance.instanceId,
11678
11916
  depth: instance.depth,
@@ -11736,6 +11974,17 @@ function buildSystemPrompt(definition, taskDescription, _config) {
11736
11974
  return definition.getSystemPrompt(ctx);
11737
11975
  }
11738
11976
  /**
11977
+ * 解析 Agent 的 thinkingLevel
11978
+ *
11979
+ * 优先级:definition.thinkingLevel > config.thinkingLevel > 'off'
11980
+ * 'inherit' 值:从父 Agent 读取当前 thinkingLevel
11981
+ */
11982
+ function resolveAgentThinkingLevel(definitionLevel, configLevel, parentAgent) {
11983
+ const level = definitionLevel ?? configLevel ?? "off";
11984
+ if (level === "inherit") return parentAgent.innerAgent.state.thinkingLevel ?? "medium";
11985
+ return level;
11986
+ }
11987
+ /**
11739
11988
  * 共享父 Agent 的 Model 和 API Key 给子 Agent
11740
11989
  *
11741
11990
  * 如果 Agent 类型声明了偏好的模型(definition.model),则通过 AgentLlmFacade
@@ -11753,8 +12002,14 @@ function shareParentResources(subAgent, parentAgent, definition) {
11753
12002
  subAgent.llmFacade = parentAgent.llmFacade;
11754
12003
  if (definition?.model) {
11755
12004
  const resolvedModel = resolveModelForDefinition(definition.model, parentAgent);
11756
- if (resolvedModel) subAgent.innerAgent.state.model = resolvedModel;
11757
- else subAgent.innerAgent.state.model = parentInner.state.model;
12005
+ if (resolvedModel) {
12006
+ subAgent.innerAgent.state.model = resolvedModel;
12007
+ if (resolvedModel.id !== parentInner.state.model?.id) log$10.info(`子 Agent [${definition?.typeId}] 模型切换: ${parentInner.state.model?.id ?? "N/A"} → ${resolvedModel.id}`, {
12008
+ typeId: definition?.typeId,
12009
+ parentModel: parentInner.state.model?.id,
12010
+ childModel: resolvedModel.id
12011
+ });
12012
+ } else subAgent.innerAgent.state.model = parentInner.state.model;
11758
12013
  } else subAgent.innerAgent.state.model = parentInner.state.model;
11759
12014
  subAgent.innerAgent.getApiKey = parentAgent.llmFacade.createGetApiKeyFn();
11760
12015
  return;
@@ -11788,14 +12043,14 @@ function resolveModelForDefinition(model, parentAgent) {
11788
12043
  }
11789
12044
  } catch (error) {
11790
12045
  const message = error instanceof Error ? error.message : String(error);
11791
- log$9.warn(`解析 Agent 类型偏好模型 [${model}] 失败`, { error: message });
12046
+ log$10.warn(`解析 Agent 类型偏好模型 [${model}] 失败`, { error: message });
11792
12047
  return;
11793
12048
  }
11794
12049
  }
11795
12050
 
11796
12051
  //#endregion
11797
12052
  //#region src/core/agent-team/agent-result-aggregator.ts
11798
- const log$8 = logger.child("agent-result-aggregator");
12053
+ const log$9 = logger.child("agent-result-aggregator");
11799
12054
  /** 零值 TokenUsage */
11800
12055
  const ZERO_TOKEN = {
11801
12056
  inputTokens: 0,
@@ -11825,7 +12080,7 @@ function aggregateResults(teamId, workerResults) {
11825
12080
  estimatedCostUsd: sum.estimatedCostUsd + (r.tokenUsage?.estimatedCostUsd ?? 0)
11826
12081
  }), { ...ZERO_TOKEN });
11827
12082
  const summary = buildTeamSummary(workerResults);
11828
- log$8.debug("Team 结果聚合完成", {
12083
+ log$9.debug("Team 结果聚合完成", {
11829
12084
  teamId,
11830
12085
  total: workerResults.length,
11831
12086
  succeeded,
@@ -11903,7 +12158,7 @@ function escapeMarkdownTable(text) {
11903
12158
 
11904
12159
  //#endregion
11905
12160
  //#region src/core/agent-team/agent-orchestrator.ts
11906
- const log$7 = logger.child("agent-orchestrator");
12161
+ const log$8 = logger.child("agent-orchestrator");
11907
12162
  /**
11908
12163
  * Agent 编排器
11909
12164
  *
@@ -11936,7 +12191,7 @@ var AgentOrchestrator = class {
11936
12191
  const startTime = Date.now();
11937
12192
  const defaultType = getAgentTypeRegistry().getDefault();
11938
12193
  if (!defaultType) throw new Error("无法获取默认 Agent 类型(general-purpose)");
11939
- log$7.info("开始扁平并行执行", {
12194
+ log$8.info("开始扁平并行执行", {
11940
12195
  count: specs.length,
11941
12196
  maxConcurrent: this.flatConfig.maxConcurrent,
11942
12197
  hasContext: context != null
@@ -11944,7 +12199,7 @@ var AgentOrchestrator = class {
11944
12199
  const allResults = [];
11945
12200
  for (let i = 0; i < specs.length; i += this.flatConfig.maxConcurrent) {
11946
12201
  const batch = specs.slice(i, i + this.flatConfig.maxConcurrent);
11947
- log$7.debug("执行扁平批次", {
12202
+ log$8.debug("执行扁平批次", {
11948
12203
  batchStart: i,
11949
12204
  batchSize: batch.length,
11950
12205
  totalSpecs: specs.length
@@ -11954,7 +12209,7 @@ var AgentOrchestrator = class {
11954
12209
  }
11955
12210
  const succeeded = allResults.filter((r) => r.status === "success").length;
11956
12211
  const totalDuration = Date.now() - startTime;
11957
- log$7.info("扁平并行执行完成", {
12212
+ log$8.info("扁平并行执行完成", {
11958
12213
  total: allResults.length,
11959
12214
  succeeded,
11960
12215
  failed: allResults.length - succeeded,
@@ -12029,7 +12284,7 @@ var AgentOrchestrator = class {
12029
12284
  } catch (error) {
12030
12285
  const duration = Date.now() - startTime;
12031
12286
  const message = error instanceof Error ? error.message : String(error);
12032
- log$7.warn("扁平子任务执行失败", {
12287
+ log$8.warn("扁平子任务执行失败", {
12033
12288
  specId: spec.id,
12034
12289
  error: message,
12035
12290
  duration
@@ -12086,7 +12341,7 @@ var AgentOrchestrator = class {
12086
12341
  const parentInstanceId = options?.parentInstanceId ?? "";
12087
12342
  const depth = (parentInstanceId && instanceManager.get(parentInstanceId) ? instanceManager.get(parentInstanceId)?.depth ?? 0 : 0) + 1;
12088
12343
  if (depth > this.teamConfig.maxGlobalDepth) {
12089
- log$7.warn("Worker 创建被拒绝:超过全局最大深度", {
12344
+ log$8.warn("Worker 创建被拒绝:超过全局最大深度", {
12090
12345
  typeId,
12091
12346
  depth,
12092
12347
  maxGlobalDepth: this.teamConfig.maxGlobalDepth
@@ -12208,7 +12463,7 @@ var AgentOrchestrator = class {
12208
12463
  } catch (error) {
12209
12464
  const duration = Date.now() - startTime;
12210
12465
  const message = error instanceof Error ? error.message : String(error);
12211
- log$7.warn("Worker 执行失败", {
12466
+ log$8.warn("Worker 执行失败", {
12212
12467
  typeId,
12213
12468
  instanceId,
12214
12469
  error: message,
@@ -12251,12 +12506,12 @@ var AgentOrchestrator = class {
12251
12506
  const wm = getWorktreeManager();
12252
12507
  if (wm) try {
12253
12508
  const cleanResult = await wm.autoCleanIfNoChanges(worktreeInfo.id);
12254
- if (!cleanResult.cleaned) log$7.info("Worktree 有变更,保留", {
12509
+ if (!cleanResult.cleaned) log$8.info("Worktree 有变更,保留", {
12255
12510
  id: worktreeInfo.id,
12256
12511
  path: cleanResult.worktreePath
12257
12512
  });
12258
12513
  } catch (err) {
12259
- log$7.warn("Worktree 清理失败", {
12514
+ log$8.warn("Worktree 清理失败", {
12260
12515
  id: worktreeInfo.id,
12261
12516
  error: err instanceof Error ? err.message : String(err)
12262
12517
  });
@@ -12276,7 +12531,7 @@ var AgentOrchestrator = class {
12276
12531
  */
12277
12532
  async spawnTeam(taskDescription, workerSpecs) {
12278
12533
  const teamId = `team-${Date.now()}-${++this.teamCounter}`;
12279
- log$7.info("创建 Team", {
12534
+ log$8.info("创建 Team", {
12280
12535
  teamId,
12281
12536
  workerCount: workerSpecs.length,
12282
12537
  taskDescription
@@ -12628,6 +12883,84 @@ function createReplBuiltinTools(webConfig, taskStore, skillConfig, parentAgent,
12628
12883
  return tools;
12629
12884
  }
12630
12885
 
12886
+ //#endregion
12887
+ //#region src/cli/repl/skill-watcher.ts
12888
+ /**
12889
+ * Skill 文件变更监视器
12890
+ *
12891
+ * 使用 chokidar 监视技能目录中的 SKILL.md 文件变更,
12892
+ * 通过防抖机制触发外部回调,实现动态技能发现。
12893
+ *
12894
+ * @module cli/repl/skill-watcher
12895
+ */
12896
+ const log$7 = logger.child("repl:skill-watcher");
12897
+ /** 防抖延迟(毫秒) */
12898
+ const DEBOUNCE_MS = 500;
12899
+ /**
12900
+ * Skill 文件变更监视器
12901
+ */
12902
+ var SkillWatcher = class {
12903
+ watcher = null;
12904
+ debounceTimer = null;
12905
+ _ready = false;
12906
+ /** 监视器是否已就绪 */
12907
+ get ready() {
12908
+ return this._ready;
12909
+ }
12910
+ /**
12911
+ * 启动监视
12912
+ *
12913
+ * 监视指定目录下 `**` 深层匹配 `SKILL.md` 文件变更。
12914
+ * 忽略隐藏目录,`ignoreInitial: true` 避免初始扫描触发 add 事件。
12915
+ */
12916
+ start(options) {
12917
+ if (this.watcher) return;
12918
+ this.watcher = chokidar.watch(options.watchDirs.map((dir) => `${dir}/**/SKILL.md`), {
12919
+ ignored: /(^|[/\\])\./,
12920
+ persistent: true,
12921
+ ignoreInitial: true,
12922
+ depth: 2
12923
+ });
12924
+ this.watcher.on("ready", () => {
12925
+ this._ready = true;
12926
+ log$7.info("技能文件监视已就绪", { dirs: options.watchDirs });
12927
+ }).on("add", (path) => {
12928
+ log$7.debug("技能文件新增", { path });
12929
+ this.debounceNotify(options.onChanged);
12930
+ }).on("change", (path) => {
12931
+ log$7.debug("技能文件修改", { path });
12932
+ this.debounceNotify(options.onChanged);
12933
+ }).on("unlink", (path) => {
12934
+ log$7.debug("技能文件删除", { path });
12935
+ this.debounceNotify(options.onChanged);
12936
+ });
12937
+ }
12938
+ /**
12939
+ * 停止监视并清理资源
12940
+ */
12941
+ stop() {
12942
+ if (this.debounceTimer) {
12943
+ clearTimeout(this.debounceTimer);
12944
+ this.debounceTimer = null;
12945
+ }
12946
+ if (this.watcher) {
12947
+ this.watcher.close();
12948
+ this.watcher = null;
12949
+ }
12950
+ this._ready = false;
12951
+ }
12952
+ /**
12953
+ * 防抖通知:批量操作(如 git pull)时合并为一次回调
12954
+ */
12955
+ debounceNotify(callback) {
12956
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
12957
+ this.debounceTimer = setTimeout(() => {
12958
+ this.debounceTimer = null;
12959
+ callback();
12960
+ }, DEBOUNCE_MS);
12961
+ }
12962
+ };
12963
+
12631
12964
  //#endregion
12632
12965
  //#region src/cli/repl/theme.ts
12633
12966
  /** 根据颜色开关获取 chalk 实例 */
@@ -15640,6 +15973,7 @@ var ReplSession = class {
15640
15973
  auditLogger;
15641
15974
  secretRedactor;
15642
15975
  skillGuard;
15976
+ skillWatcher;
15643
15977
  /** 交互式提问管理器 */
15644
15978
  questionManager;
15645
15979
  stats = {
@@ -15701,6 +16035,7 @@ var ReplSession = class {
15701
16035
  log.warn("记忆快照冻结失败,将使用空快照", { error: err instanceof Error ? err.message : String(err) });
15702
16036
  this.agent.memorySnapshot = "";
15703
16037
  });
16038
+ this.skillWatcher = new SkillWatcher();
15704
16039
  if (this.config.skill?.enabled !== false) this.initSkills();
15705
16040
  this.initSecurity();
15706
16041
  this.questionManager = getQuestionManager();
@@ -15762,6 +16097,7 @@ var ReplSession = class {
15762
16097
  this.cronScheduler.stop();
15763
16098
  this.cronScheduler = null;
15764
16099
  }
16100
+ this.skillWatcher.stop();
15765
16101
  if (this.auditLogger) this.auditLogger.destroy();
15766
16102
  if (this.mcpManager) {
15767
16103
  await this.mcpManager.shutdown();
@@ -15790,6 +16126,36 @@ var ReplSession = class {
15790
16126
  getHistoryStore() {
15791
16127
  return this.history;
15792
16128
  }
16129
+ /**
16130
+ * 彻底清空 Agent 会话上下文。
16131
+ *
16132
+ * - 清空消息历史、Token 统计、缓存、授权
16133
+ * - 保留配置、记忆、持久化任务、定时任务
16134
+ * - 效果等价于"新开一个会话"
16135
+ *
16136
+ * 由 /clear 命令调用。
16137
+ */
16138
+ clearAgentContext() {
16139
+ this.cancelCurrentTask();
16140
+ this.currentTaskId = null;
16141
+ this._state = "idle";
16142
+ this.agent.resetContext();
16143
+ this.conversationHistory = [];
16144
+ this.stats = {
16145
+ totalRequests: 0,
16146
+ successCount: 0,
16147
+ failureCount: 0,
16148
+ totalTokens: 0,
16149
+ totalCostUsd: 0,
16150
+ state: "idle"
16151
+ };
16152
+ this.permissionStore.clear();
16153
+ this.questionManager.rejectAll(/* @__PURE__ */ new Error("会话已重置"));
16154
+ this.editor.setExecuting(false);
16155
+ this.outputArea.clear();
16156
+ this.agentStatusBar.clearTokenStats();
16157
+ this.tui.requestRender();
16158
+ }
15793
16159
  /** 获取会话统计 */
15794
16160
  getStats() {
15795
16161
  return { ...this.stats };
@@ -15893,6 +16259,8 @@ var ReplSession = class {
15893
16259
  let spinnerActive = true;
15894
16260
  let spinnerInterval;
15895
16261
  let thinkingElapsedInterval;
16262
+ /** Token 信息推送定时器 */
16263
+ let tokenPushInterval;
15896
16264
  try {
15897
16265
  this._state = "executing";
15898
16266
  this.updateStatsState();
@@ -16066,6 +16434,15 @@ var ReplSession = class {
16066
16434
  this.currentTaskId = taskId;
16067
16435
  const originalOnToggleThinking = this.editor.onToggleThinking;
16068
16436
  if (thinkingDisplayMode === "collapse") this.editor.onToggleThinking = toggleThinking;
16437
+ this.agentStatusBar.clearTokenStats();
16438
+ const agentModel = this.agent.innerAgent.state.model;
16439
+ const modelName = typeof agentModel?.id === "string" ? agentModel.id : "unknown";
16440
+ this.agentStatusBar.setModelName(modelName);
16441
+ tokenPushInterval = setInterval(() => {
16442
+ const usage = this.agent.tokenTracker.getUsage();
16443
+ this.agentStatusBar.updateTokenStats(usage.inputTokens, usage.cacheReadTokens, usage.outputTokens, Date.now() - startTime);
16444
+ this.tui.requestRender();
16445
+ }, 2e3);
16069
16446
  log.debug("开始通过 Agent 执行目标", {
16070
16447
  taskId,
16071
16448
  taskDescription: rawInput.slice(0, 100)
@@ -16305,6 +16682,12 @@ var ReplSession = class {
16305
16682
  clearInterval(thinkingElapsedInterval);
16306
16683
  thinkingElapsedInterval = void 0;
16307
16684
  }
16685
+ if (tokenPushInterval) {
16686
+ clearInterval(tokenPushInterval);
16687
+ tokenPushInterval = void 0;
16688
+ }
16689
+ const finalUsage = this.agent.tokenTracker.getUsage();
16690
+ this.agentStatusBar.updateTokenStats(finalUsage.inputTokens, finalUsage.cacheReadTokens, finalUsage.outputTokens, Date.now() - startTime);
16308
16691
  this._state = "idle";
16309
16692
  this.updateStatsState();
16310
16693
  this.editor.setExecuting(false);
@@ -16567,33 +16950,90 @@ var ReplSession = class {
16567
16950
  }, process.cwd()).then((entries) => {
16568
16951
  setSkillEntries(entries);
16569
16952
  this.agent.skillEntries = entries;
16570
- const snapshot = buildSkillSnapshot(entries, skillConfig.maxSkillsInPrompt);
16571
- this.agent.skillPrompt = snapshot.prompt;
16953
+ this.agent.resetSentSkills();
16572
16954
  this._registerSkillCommands(entries);
16573
16955
  this.buildAutocompleteProvider();
16574
- const results = this.skillGuard.scanAll(entries);
16575
- const summary = this.skillGuard.getThreatSummary(results);
16576
- if (summary.danger > 0 || summary.warning > 0) {
16577
- log.warn("Skill 威胁扫描完成", summary);
16578
- for (const result of results) if (!result.passed) for (const v of result.violations) eventBus.emit("security:violation", {
16579
- toolId: "Skill",
16580
- type: "skill-threat",
16581
- message: `[${result.skillName}] ${v.reason}`,
16582
- params: {
16583
- skillPath: result.skillPath,
16584
- ruleId: v.ruleId
16585
- }
16586
- });
16587
- }
16956
+ this.scanSkillSecurity(entries);
16588
16957
  log.info("Skill 系统初始化完成", {
16589
- count: snapshot.count,
16590
- names: snapshot.names
16958
+ count: entries.length,
16959
+ names: entries.map((e) => e.skill.name)
16591
16960
  });
16961
+ this.startSkillWatcher();
16592
16962
  }).catch((err) => {
16593
16963
  log.error("Skill 加载失败", { error: err instanceof Error ? err.message : String(err) });
16594
16964
  });
16595
16965
  }
16596
16966
  /**
16967
+ * 启动技能文件变更监视
16968
+ *
16969
+ * 监视项目级(.agents/skills/、.zapmyco/skills/)和用户级
16970
+ *(~/.zapmyco/skills/)技能目录,文件变更时自动重新加载。
16971
+ */
16972
+ startSkillWatcher() {
16973
+ const watchDirs = [];
16974
+ for (const dir of [
16975
+ join(process.cwd(), ".agents", "skills"),
16976
+ join(process.cwd(), ".zapmyco", "skills"),
16977
+ join(homedir(), ".zapmyco", "skills")
16978
+ ]) try {
16979
+ if (statSync(dir).isDirectory()) watchDirs.push(dir);
16980
+ } catch {}
16981
+ if (watchDirs.length === 0) return;
16982
+ this.skillWatcher.start({
16983
+ watchDirs,
16984
+ onChanged: () => this.reloadSkills()
16985
+ });
16986
+ }
16987
+ /**
16988
+ * 重新加载所有技能(文件变更时调用)
16989
+ *
16990
+ * 幂等操作:重新扫描磁盘、更新全局状态、刷新命令注册和自动补全。
16991
+ */
16992
+ reloadSkills() {
16993
+ const skillConfig = this.config.skill;
16994
+ if (!skillConfig?.enabled) return;
16995
+ log.info("技能文件变更,重新加载技能...");
16996
+ loadSkills({
16997
+ enabled: true,
16998
+ extraDirs: skillConfig.loadDirs,
16999
+ maxSkillsInPrompt: skillConfig.maxSkillsInPrompt,
17000
+ maxSkillFileBytes: skillConfig.maxSkillFileBytes
17001
+ }, process.cwd()).then((entries) => {
17002
+ setSkillEntries(entries);
17003
+ this.agent.skillEntries = entries;
17004
+ this.agent.resetSentSkills();
17005
+ this.registry.unregisterBySource("skill");
17006
+ this._registerSkillCommands(entries);
17007
+ this.buildAutocompleteProvider();
17008
+ this.scanSkillSecurity(entries);
17009
+ log.info("技能重新加载完成", {
17010
+ count: entries.length,
17011
+ names: entries.map((e) => e.skill.name)
17012
+ });
17013
+ }).catch((err) => {
17014
+ log.error("技能重新加载失败", { error: err instanceof Error ? err.message : String(err) });
17015
+ });
17016
+ }
17017
+ /**
17018
+ * 扫描 Skill 安全威胁
17019
+ */
17020
+ scanSkillSecurity(entries) {
17021
+ const results = this.skillGuard.scanAll(entries);
17022
+ const summary = this.skillGuard.getThreatSummary(results);
17023
+ if (summary.danger > 0 || summary.warning > 0) {
17024
+ log.warn("Skill 威胁扫描完成", summary);
17025
+ for (const result of results) if (!result.passed) for (const v of result.violations) eventBus.emit("security:violation", {
17026
+ toolId: "Skill",
17027
+ type: "skill-threat",
17028
+ message: `[${result.skillName}] ${v.reason}`,
17029
+ params: {
17030
+ skillPath: result.skillPath,
17031
+ ruleId: v.ruleId
17032
+ }
17033
+ });
17034
+ }
17035
+ }
17036
+ /**
16597
17037
  * 为 user-invocable 技能注册斜杠命令
16598
17038
  *
16599
17039
  * 将每个用户可调用的技能注册为 /skill-name 格式的 CLI 命令。
@@ -16611,11 +17051,12 @@ var ReplSession = class {
16611
17051
  description: spec.description,
16612
17052
  aliases: [],
16613
17053
  usage: spec.name,
17054
+ source: "skill",
16614
17055
  handler: async (args) => {
16615
17056
  const argsStr = args.join(" ");
16616
17057
  const entry = getSkillEntries().find((e) => sanitizeSkillCommandName(e.skill.name) === spec.name);
16617
17058
  if (entry) {
16618
- const skillContent = formatSkillContent(entry.skill, argsStr);
17059
+ const skillContent = await formatSkillContent(entry.skill, argsStr);
16619
17060
  await this.executeGoal(skillContent, `/${spec.name}`);
16620
17061
  } else {
16621
17062
  const goalInput = argsStr ? `请使用 /${spec.name} 技能,参数: ${argsStr}` : `请使用 /${spec.name} 技能`;