hamster-wheel-cli 0.2.0 → 0.3.1

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
@@ -149,6 +149,15 @@ async function runCommand(command, args, options = {}) {
149
149
  function isoNow() {
150
150
  return (/* @__PURE__ */ new Date()).toISOString();
151
151
  }
152
+ function localTimestamp(date = /* @__PURE__ */ new Date()) {
153
+ const year = date.getFullYear();
154
+ const month = pad2(date.getMonth() + 1);
155
+ const day = pad2(date.getDate());
156
+ const hours = pad2(date.getHours());
157
+ const minutes = pad2(date.getMinutes());
158
+ const seconds = pad2(date.getSeconds());
159
+ return `${year}${month}${day}-${hours}${minutes}${seconds}`;
160
+ }
152
161
  function resolvePath(cwd, target) {
153
162
  return import_node_path.default.isAbsolute(target) ? target : import_node_path.default.join(cwd, target);
154
163
  }
@@ -392,10 +401,37 @@ function normalizeShortcutName(name) {
392
401
  function normalizeAliasName(name) {
393
402
  return normalizeShortcutName(name);
394
403
  }
404
+ function normalizeAgentName(name) {
405
+ return normalizeShortcutName(name);
406
+ }
395
407
  function formatTomlString(value) {
396
408
  const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
397
409
  return `"${escaped}"`;
398
410
  }
411
+ function collectSectionRanges(lines) {
412
+ const ranges = [];
413
+ let current = null;
414
+ for (let i = 0; i < lines.length; i += 1) {
415
+ const match = /^\s*\[(.+?)\]\s*$/.exec(lines[i]);
416
+ if (!match) continue;
417
+ if (current) {
418
+ current.end = i;
419
+ ranges.push(current);
420
+ }
421
+ current = {
422
+ name: match[1].trim(),
423
+ start: i,
424
+ end: lines.length
425
+ };
426
+ }
427
+ if (current) {
428
+ ranges.push(current);
429
+ }
430
+ return ranges;
431
+ }
432
+ function isAgentSection(name) {
433
+ return name === "agent" || name === "agents";
434
+ }
399
435
  function updateAliasContent(content, name, command) {
400
436
  const lines = content.split(/\r?\n/);
401
437
  const entryLine = `${name} = ${formatTomlString(command)}`;
@@ -439,6 +475,33 @@ ${entryLine}
439
475
  return output.endsWith("\n") ? output : `${output}
440
476
  `;
441
477
  }
478
+ function removeAliasContent(content, name) {
479
+ const lines = content.split(/\r?\n/);
480
+ let currentSection = null;
481
+ const removeIndices = [];
482
+ for (let i = 0; i < lines.length; i += 1) {
483
+ const sectionMatch = /^\s*\[(.+?)\]\s*$/.exec(lines[i]);
484
+ if (sectionMatch) {
485
+ currentSection = sectionMatch[1].trim();
486
+ continue;
487
+ }
488
+ if (currentSection !== "alias") continue;
489
+ const parsed = parseTomlKeyValue(stripTomlComment(lines[i]).trim());
490
+ if (!parsed) continue;
491
+ if (parsed.key === name) {
492
+ removeIndices.push(i);
493
+ }
494
+ }
495
+ if (removeIndices.length === 0) {
496
+ return { removed: false, nextContent: ensureTrailingNewline(content) };
497
+ }
498
+ removeIndices.sort((a, b) => b - a).forEach((index) => {
499
+ lines.splice(index, 1);
500
+ });
501
+ const output = lines.join("\n");
502
+ return { removed: true, nextContent: output.endsWith("\n") ? output : `${output}
503
+ ` };
504
+ }
442
505
  async function upsertAliasEntry(name, command, filePath = getGlobalConfigPath()) {
443
506
  const exists = await import_fs_extra2.default.pathExists(filePath);
444
507
  const content = exists ? await import_fs_extra2.default.readFile(filePath, "utf8") : "";
@@ -446,6 +509,104 @@ async function upsertAliasEntry(name, command, filePath = getGlobalConfigPath())
446
509
  await import_fs_extra2.default.mkdirp(import_node_path3.default.dirname(filePath));
447
510
  await import_fs_extra2.default.writeFile(filePath, nextContent, "utf8");
448
511
  }
512
+ async function removeAliasEntry(name, filePath = getGlobalConfigPath()) {
513
+ const exists = await import_fs_extra2.default.pathExists(filePath);
514
+ if (!exists) return false;
515
+ const content = await import_fs_extra2.default.readFile(filePath, "utf8");
516
+ const { removed, nextContent } = removeAliasContent(content, name);
517
+ if (!removed) return false;
518
+ await import_fs_extra2.default.writeFile(filePath, nextContent, "utf8");
519
+ return true;
520
+ }
521
+ function ensureTrailingNewline(content) {
522
+ if (!content) return "";
523
+ return content.endsWith("\n") ? content : `${content}
524
+ `;
525
+ }
526
+ function findAgentRangeWithEntry(lines, ranges, name) {
527
+ for (const range of ranges) {
528
+ for (let i = range.start + 1; i < range.end; i += 1) {
529
+ const parsed = parseTomlKeyValue(stripTomlComment(lines[i]).trim());
530
+ if (!parsed) continue;
531
+ if (parsed.key === name) return range;
532
+ }
533
+ }
534
+ return null;
535
+ }
536
+ function updateAgentContent(content, name, command) {
537
+ const lines = content.split(/\r?\n/);
538
+ const entryLine = `${name} = ${formatTomlString(command)}`;
539
+ const ranges = collectSectionRanges(lines);
540
+ const agentRanges = ranges.filter((range) => isAgentSection(range.name));
541
+ let targetRange = findAgentRangeWithEntry(lines, agentRanges, name);
542
+ if (!targetRange) {
543
+ targetRange = agentRanges.find((range) => range.name === "agent") ?? agentRanges.find((range) => range.name === "agents") ?? null;
544
+ }
545
+ if (!targetRange) {
546
+ const trimmed = content.trimEnd();
547
+ const prefix = trimmed.length > 0 ? `${trimmed}
548
+
549
+ ` : "";
550
+ return `${prefix}[agent]
551
+ ${entryLine}
552
+ `;
553
+ }
554
+ let replaced = false;
555
+ for (let i = targetRange.start + 1; i < targetRange.end; i += 1) {
556
+ const parsed = parseTomlKeyValue(stripTomlComment(lines[i]).trim());
557
+ if (!parsed) continue;
558
+ if (parsed.key === name) {
559
+ lines[i] = entryLine;
560
+ replaced = true;
561
+ break;
562
+ }
563
+ }
564
+ if (!replaced) {
565
+ lines.splice(targetRange.end, 0, entryLine);
566
+ }
567
+ const output = lines.join("\n");
568
+ return output.endsWith("\n") ? output : `${output}
569
+ `;
570
+ }
571
+ function removeAgentContent(content, name) {
572
+ const lines = content.split(/\r?\n/);
573
+ const ranges = collectSectionRanges(lines).filter((range) => isAgentSection(range.name));
574
+ const removeIndices = [];
575
+ for (const range of ranges) {
576
+ for (let i = range.start + 1; i < range.end; i += 1) {
577
+ const parsed = parseTomlKeyValue(stripTomlComment(lines[i]).trim());
578
+ if (!parsed) continue;
579
+ if (parsed.key === name) {
580
+ removeIndices.push(i);
581
+ }
582
+ }
583
+ }
584
+ if (removeIndices.length === 0) {
585
+ return { removed: false, nextContent: ensureTrailingNewline(content) };
586
+ }
587
+ removeIndices.sort((a, b) => b - a).forEach((index) => {
588
+ lines.splice(index, 1);
589
+ });
590
+ const output = lines.join("\n");
591
+ return { removed: true, nextContent: output.endsWith("\n") ? output : `${output}
592
+ ` };
593
+ }
594
+ async function upsertAgentEntry(name, command, filePath = getGlobalConfigPath()) {
595
+ const exists = await import_fs_extra2.default.pathExists(filePath);
596
+ const content = exists ? await import_fs_extra2.default.readFile(filePath, "utf8") : "";
597
+ const nextContent = updateAgentContent(content, name, command);
598
+ await import_fs_extra2.default.mkdirp(import_node_path3.default.dirname(filePath));
599
+ await import_fs_extra2.default.writeFile(filePath, nextContent, "utf8");
600
+ }
601
+ async function removeAgentEntry(name, filePath = getGlobalConfigPath()) {
602
+ const exists = await import_fs_extra2.default.pathExists(filePath);
603
+ if (!exists) return false;
604
+ const content = await import_fs_extra2.default.readFile(filePath, "utf8");
605
+ const { removed, nextContent } = removeAgentContent(content, name);
606
+ if (!removed) return false;
607
+ await import_fs_extra2.default.writeFile(filePath, nextContent, "utf8");
608
+ return true;
609
+ }
449
610
  function parseGlobalConfig(content) {
450
611
  const lines = content.split(/\r?\n/);
451
612
  let currentSection = null;
@@ -512,6 +673,34 @@ function parseAliasEntries(content) {
512
673
  }
513
674
  return entries;
514
675
  }
676
+ function parseAgentEntries(content) {
677
+ const lines = content.split(/\r?\n/);
678
+ let currentSection = null;
679
+ const entries = [];
680
+ const names = /* @__PURE__ */ new Set();
681
+ for (const rawLine of lines) {
682
+ const line = stripTomlComment(rawLine).trim();
683
+ if (!line) continue;
684
+ const sectionMatch = /^\[(.+)\]$/.exec(line);
685
+ if (sectionMatch) {
686
+ currentSection = sectionMatch[1].trim();
687
+ continue;
688
+ }
689
+ if (!isAgentSection(currentSection)) continue;
690
+ const parsed = parseTomlKeyValue(line);
691
+ if (!parsed) continue;
692
+ const name = normalizeShortcutName(parsed.key);
693
+ const command = parsed.value.trim();
694
+ if (!name || !command) continue;
695
+ if (names.has(name)) continue;
696
+ names.add(name);
697
+ entries.push({
698
+ name,
699
+ command
700
+ });
701
+ }
702
+ return entries;
703
+ }
515
704
  async function loadGlobalConfig(logger) {
516
705
  const filePath = getGlobalConfigPath();
517
706
  const exists = await import_fs_extra2.default.pathExists(filePath);
@@ -1215,7 +1404,7 @@ async function readLogLines(logFile) {
1215
1404
  }
1216
1405
  function buildListHeader(state, columns) {
1217
1406
  const total = state.logs.length;
1218
- const title = `\u65E5\u5FD7\u5217\u8868\uFF08${total} \u6761\uFF09\uFF5C\u2191/\u2193 \u9009\u62E9 Enter \u67E5\u770B q \u9000\u51FA`;
1407
+ const title = `\u65E5\u5FD7\u5217\u8868\uFF08${total} \u6761\uFF09\uFF5C\u2191/\u2193 \u9009\u62E9 PageUp/PageDown \u7FFB\u9875 Enter \u67E5\u770B q \u9000\u51FA`;
1219
1408
  return truncateLine2(title, columns);
1220
1409
  }
1221
1410
  function buildListStatus(state, columns) {
@@ -1430,6 +1619,18 @@ async function runLogsViewer() {
1430
1619
  render2(state);
1431
1620
  return;
1432
1621
  }
1622
+ if (isPageUp(input)) {
1623
+ const pageSize = getPageSize2(getTerminalSize2().rows);
1624
+ state.selectedIndex = clampIndex2(state.selectedIndex - pageSize, state.logs.length);
1625
+ render2(state);
1626
+ return;
1627
+ }
1628
+ if (isPageDown(input)) {
1629
+ const pageSize = getPageSize2(getTerminalSize2().rows);
1630
+ state.selectedIndex = clampIndex2(state.selectedIndex + pageSize, state.logs.length);
1631
+ render2(state);
1632
+ return;
1633
+ }
1433
1634
  if (isEnter(input)) {
1434
1635
  void openView();
1435
1636
  return;
@@ -2535,13 +2736,26 @@ function normalizeWebhookUrls(urls) {
2535
2736
  return urls.map((url) => url.trim()).filter((url) => url.length > 0);
2536
2737
  }
2537
2738
  function buildWebhookPayload(input) {
2538
- return {
2539
- event: input.event,
2540
- task: input.task,
2739
+ const base = {
2541
2740
  branch: input.branch ?? "",
2542
2741
  iteration: input.iteration,
2543
2742
  stage: input.stage,
2544
- timestamp: input.timestamp ?? isoNow()
2743
+ timestamp: input.timestamp ?? localTimestamp(),
2744
+ project: input.project,
2745
+ commit: input.commit ?? "",
2746
+ pr: input.pr ?? "",
2747
+ ...input.plan !== void 0 ? { plan: input.plan } : {}
2748
+ };
2749
+ if (input.event === "task_start") {
2750
+ return {
2751
+ ...base,
2752
+ event: "task_start",
2753
+ task: input.task
2754
+ };
2755
+ }
2756
+ return {
2757
+ ...base,
2758
+ event: input.event
2545
2759
  };
2546
2760
  }
2547
2761
  function resolveFetcher(fetcher) {
@@ -2599,11 +2813,14 @@ function trimOutput(output, limit = MAX_LOG_LENGTH) {
2599
2813
  return `${output.slice(0, limit)}
2600
2814
  \u2026\u2026\uFF08\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${output.length} \u5B57\u7B26\uFF09`;
2601
2815
  }
2602
- function truncateText(text, limit = 24) {
2816
+ function truncateText(text, limit = 100) {
2603
2817
  const trimmed = text.trim();
2604
2818
  if (trimmed.length <= limit) return trimmed;
2605
2819
  return `${trimmed.slice(0, limit)}...`;
2606
2820
  }
2821
+ function normalizePlanForWebhook(plan) {
2822
+ return plan.replace(/\r\n?/g, "\n");
2823
+ }
2607
2824
  async function safeCommandOutput(command, args, cwd, logger, label, verboseCommand) {
2608
2825
  const result = await runCommand(command, args, {
2609
2826
  cwd,
@@ -2617,6 +2834,41 @@ async function safeCommandOutput(command, args, cwd, logger, label, verboseComma
2617
2834
  }
2618
2835
  return result.stdout.trim();
2619
2836
  }
2837
+ function normalizeWebhookUrl(value) {
2838
+ if (!value) return "";
2839
+ const trimmed = value.trim();
2840
+ if (!/^https?:\/\//i.test(trimmed)) return "";
2841
+ return trimmed;
2842
+ }
2843
+ function normalizeRepoUrl(remoteUrl) {
2844
+ const trimmed = remoteUrl.trim();
2845
+ if (!trimmed) return null;
2846
+ const withoutGit = trimmed.replace(/\.git$/i, "");
2847
+ if (withoutGit.includes("://")) {
2848
+ try {
2849
+ const parsed = new URL(withoutGit);
2850
+ const protocol = parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.protocol : "https:";
2851
+ const pathname = parsed.pathname.replace(/\.git$/i, "");
2852
+ return `${protocol}//${parsed.host}${pathname}`.replace(/\/+$/, "");
2853
+ } catch {
2854
+ return null;
2855
+ }
2856
+ }
2857
+ const scpMatch = withoutGit.match(/^(?:[^@]+@)?([^:]+):(.+)$/);
2858
+ if (!scpMatch) return null;
2859
+ const host = scpMatch[1];
2860
+ const repoPath = scpMatch[2];
2861
+ return `https://${host}/${repoPath}`.replace(/\/+$/, "");
2862
+ }
2863
+ async function resolveCommitLink(cwd, logger) {
2864
+ const sha = await safeCommandOutput("git", ["rev-parse", "HEAD"], cwd, logger, "git", "git rev-parse HEAD");
2865
+ if (!sha) return "";
2866
+ const remote = await safeCommandOutput("git", ["remote", "get-url", "origin"], cwd, logger, "git", "git remote get-url origin");
2867
+ if (!remote) return "";
2868
+ const repoUrl = normalizeRepoUrl(remote);
2869
+ if (!repoUrl) return "";
2870
+ return `${repoUrl}/commit/${sha}`;
2871
+ }
2620
2872
  async function runSingleTest(kind, command, cwd, logger) {
2621
2873
  const label = kind === "unit" ? "\u5355\u5143\u6D4B\u8BD5" : "e2e \u6D4B\u8BD5";
2622
2874
  logger.info(`\u6267\u884C${label}: ${command}`);
@@ -2809,14 +3061,33 @@ async function runLoop(config) {
2809
3061
  let prInfo = null;
2810
3062
  let prFailed = false;
2811
3063
  let sessionIndex = 0;
3064
+ let commitLink = "";
3065
+ let prLink = "";
3066
+ let commitCreated = false;
3067
+ let pushSucceeded = false;
2812
3068
  const preWorktreeRecords = [];
2813
- const notifyWebhook = async (event, iteration, stage) => {
2814
- const payload = buildWebhookPayload({
3069
+ const resolveProjectName = () => import_node_path9.default.basename(workDir);
3070
+ const notifyWebhook = async (event, iteration, stage, plan) => {
3071
+ const project = resolveProjectName();
3072
+ const payload = event === "task_start" ? buildWebhookPayload({
2815
3073
  event,
2816
3074
  task: config.task,
2817
3075
  branch: branchName,
2818
3076
  iteration,
2819
- stage
3077
+ stage,
3078
+ project,
3079
+ commit: commitLink,
3080
+ pr: prLink,
3081
+ plan
3082
+ }) : buildWebhookPayload({
3083
+ event,
3084
+ branch: branchName,
3085
+ iteration,
3086
+ stage,
3087
+ project,
3088
+ commit: commitLink,
3089
+ pr: prLink,
3090
+ plan
2820
3091
  });
2821
3092
  await sendWebhookNotifications(config.webhooks, payload, logger);
2822
3093
  };
@@ -2894,7 +3165,8 @@ async function runLoop(config) {
2894
3165
  });
2895
3166
  const runAiSession = async (stage, prompt, extras) => {
2896
3167
  sessionIndex += 1;
2897
- await notifyWebhook("iteration_start", sessionIndex, stage);
3168
+ const webhookPlan = stage === "\u8BA1\u5212\u751F\u6210" ? normalizePlanForWebhook(await readFileSafe(workflowFiles.planFile)) : void 0;
3169
+ await notifyWebhook("iteration_start", sessionIndex, stage, webhookPlan);
2898
3170
  logger.info(`${stage} \u63D0\u793A\u6784\u5EFA\u5B8C\u6210\uFF0C\u8C03\u7528 AI CLI...`);
2899
3171
  const aiResult = await runAi(prompt, aiConfig, logger, extras?.cwd ?? workDir);
2900
3172
  accumulatedUsage = mergeTokenUsage(accumulatedUsage, aiResult.usage);
@@ -3110,6 +3382,7 @@ async function runLoop(config) {
3110
3382
  };
3111
3383
  try {
3112
3384
  const committed = await commitAll(commitMessage, workDir, logger);
3385
+ commitCreated = committed;
3113
3386
  deliveryNotes.push(committed ? `\u81EA\u52A8\u63D0\u4EA4\uFF1A\u5DF2\u63D0\u4EA4\uFF08${commitMessage.title}\uFF09` : "\u81EA\u52A8\u63D0\u4EA4\uFF1A\u672A\u751F\u6210\u63D0\u4EA4\uFF08\u53EF\u80FD\u65E0\u53D8\u66F4\u6216\u63D0\u4EA4\u5931\u8D25\uFF09");
3114
3387
  } catch (error) {
3115
3388
  deliveryNotes.push(`\u81EA\u52A8\u63D0\u4EA4\uFF1A\u5931\u8D25\uFF08${String(error)}\uFF09`);
@@ -3126,6 +3399,7 @@ async function runLoop(config) {
3126
3399
  } else {
3127
3400
  try {
3128
3401
  await pushBranch(branchName, workDir, logger);
3402
+ pushSucceeded = true;
3129
3403
  deliveryNotes.push(`\u81EA\u52A8\u63A8\u9001\uFF1A\u5DF2\u63A8\u9001\uFF08${branchName}\uFF09`);
3130
3404
  } catch (error) {
3131
3405
  deliveryNotes.push(`\u81EA\u52A8\u63A8\u9001\uFF1A\u5931\u8D25\uFF08${String(error)}\uFF09`);
@@ -3134,6 +3408,9 @@ async function runLoop(config) {
3134
3408
  } else {
3135
3409
  deliveryNotes.push("\u81EA\u52A8\u63A8\u9001\uFF1A\u672A\u5F00\u542F");
3136
3410
  }
3411
+ if (commitCreated && pushSucceeded) {
3412
+ commitLink = await resolveCommitLink(workDir, logger);
3413
+ }
3137
3414
  if (config.pr.enable) {
3138
3415
  if (lastTestFailed) {
3139
3416
  deliveryNotes.push("PR \u521B\u5EFA\uFF1A\u5DF2\u8DF3\u8FC7\uFF08\u6D4B\u8BD5\u672A\u901A\u8FC7\uFF09");
@@ -3192,6 +3469,7 @@ async function runLoop(config) {
3192
3469
  } else {
3193
3470
  deliveryNotes.push("PR \u521B\u5EFA\uFF1A\u672A\u5F00\u542F\uFF08\u7F3A\u5C11\u5206\u652F\u540D\uFF09");
3194
3471
  }
3472
+ prLink = normalizeWebhookUrl(prInfo?.url);
3195
3473
  if (deliveryNotes.length > 0) {
3196
3474
  const record = formatSystemRecord("\u63D0\u4EA4\u4E0EPR", deliveryNotes.join("\n"), isoNow());
3197
3475
  await appendSection(workflowFiles.notesFile, record);
@@ -3843,6 +4121,8 @@ var RUN_OPTION_SPECS = [
3843
4121
  var RUN_OPTION_FLAG_MAP = new Map(
3844
4122
  RUN_OPTION_SPECS.flatMap((spec) => spec.flags.map((flag) => [flag, spec]))
3845
4123
  );
4124
+ var USE_ALIAS_FLAG = "--use-alias";
4125
+ var USE_AGENT_FLAG = "--use-agent";
3846
4126
  function parseInteger(value, defaultValue) {
3847
4127
  const parsed = Number.parseInt(value, 10);
3848
4128
  if (Number.isNaN(parsed)) return defaultValue;
@@ -3872,7 +4152,21 @@ function buildBackgroundArgs(argv, logFile, branchName, injectBranch = false) {
3872
4152
  }
3873
4153
  function extractAliasCommandArgs(argv, name) {
3874
4154
  const args = argv.slice(2);
3875
- const start = args.findIndex((arg, index) => arg === "set" && args[index + 1] === "alias" && args[index + 2] === name);
4155
+ const start = args.findIndex((arg, index) => {
4156
+ const legacyMatch = arg === "set" && args[index + 1] === "alias" && args[index + 2] === name;
4157
+ const aliasMatch = isAliasCommandToken(arg) && args[index + 1] === "set" && args[index + 2] === name;
4158
+ return legacyMatch || aliasMatch;
4159
+ });
4160
+ if (start < 0) return [];
4161
+ const rest = args.slice(start + 3);
4162
+ if (rest[0] === "--") return rest.slice(1);
4163
+ return rest;
4164
+ }
4165
+ function extractAgentCommandArgs(argv, action, name) {
4166
+ const args = argv.slice(2);
4167
+ const start = args.findIndex(
4168
+ (arg, index) => arg === "agent" && args[index + 1] === action && args[index + 2] === name
4169
+ );
3876
4170
  if (start < 0) return [];
3877
4171
  const rest = args.slice(start + 3);
3878
4172
  if (rest[0] === "--") return rest.slice(1);
@@ -3891,7 +4185,7 @@ function extractAliasRunArgs(argv, name) {
3891
4185
  if (rest[0] === "--") return rest.slice(1);
3892
4186
  return rest;
3893
4187
  }
3894
- function normalizeAliasCommandArgs(args) {
4188
+ function normalizeRunCommandArgs(args) {
3895
4189
  let start = 0;
3896
4190
  if (args[start] === "wheel-ai") {
3897
4191
  start += 1;
@@ -3962,18 +4256,133 @@ function parseArgSegments(tokens) {
3962
4256
  }
3963
4257
  return segments;
3964
4258
  }
3965
- function mergeAliasCommandArgs(aliasTokens, additionTokens) {
3966
- const aliasSegments = parseArgSegments(aliasTokens);
4259
+ function mergeRunCommandArgs(baseTokens, additionTokens) {
4260
+ const baseSegments = parseArgSegments(baseTokens);
3967
4261
  const additionSegments = parseArgSegments(additionTokens);
3968
4262
  const overrideNames = new Set(
3969
4263
  additionSegments.flatMap((segment) => segment.name ? [segment.name] : [])
3970
4264
  );
3971
4265
  const merged = [
3972
- ...aliasSegments.filter((segment) => !segment.name || !overrideNames.has(segment.name)),
4266
+ ...baseSegments.filter((segment) => !segment.name || !overrideNames.has(segment.name)),
3973
4267
  ...additionSegments
3974
4268
  ];
3975
4269
  return merged.flatMap((segment) => segment.tokens);
3976
4270
  }
4271
+ function extractRunCommandArgs(argv) {
4272
+ const args = argv.slice(2);
4273
+ const start = args.findIndex((arg) => arg === "run");
4274
+ if (start < 0) return [];
4275
+ return args.slice(start + 1);
4276
+ }
4277
+ function parseUseOptionToken(token, flag) {
4278
+ if (token === flag) {
4279
+ return { matched: true };
4280
+ }
4281
+ if (token.startsWith(`${flag}=`)) {
4282
+ return { matched: true, value: token.slice(flag.length + 1) };
4283
+ }
4284
+ return { matched: false };
4285
+ }
4286
+ function resolveUseOption(tokens, index) {
4287
+ const token = tokens[index];
4288
+ const aliasMatch = parseUseOptionToken(token, USE_ALIAS_FLAG);
4289
+ if (aliasMatch.matched) {
4290
+ const value = aliasMatch.value ?? tokens[index + 1];
4291
+ if (!value) {
4292
+ throw new Error(`${USE_ALIAS_FLAG} \u9700\u8981\u63D0\u4F9B\u540D\u79F0`);
4293
+ }
4294
+ const nextIndex = aliasMatch.value ? index + 1 : index + 2;
4295
+ return { type: "alias", name: value, nextIndex };
4296
+ }
4297
+ const agentMatch = parseUseOptionToken(token, USE_AGENT_FLAG);
4298
+ if (agentMatch.matched) {
4299
+ const value = agentMatch.value ?? tokens[index + 1];
4300
+ if (!value) {
4301
+ throw new Error(`${USE_AGENT_FLAG} \u9700\u8981\u63D0\u4F9B\u540D\u79F0`);
4302
+ }
4303
+ const nextIndex = agentMatch.value ? index + 1 : index + 2;
4304
+ return { type: "agent", name: value, nextIndex };
4305
+ }
4306
+ return null;
4307
+ }
4308
+ function buildAliasRunArgs(entry) {
4309
+ return normalizeRunCommandArgs(splitCommandArgs(entry.command));
4310
+ }
4311
+ function buildAgentRunArgs(entry) {
4312
+ const tokens = normalizeRunCommandArgs(splitCommandArgs(entry.command));
4313
+ if (tokens.length === 0) return [];
4314
+ if (tokens[0].startsWith("-")) {
4315
+ return tokens;
4316
+ }
4317
+ const [command, ...args] = tokens;
4318
+ if (args.length === 0) {
4319
+ return ["--ai-cli", command];
4320
+ }
4321
+ return ["--ai-cli", command, "--ai-args", ...args];
4322
+ }
4323
+ function expandRunTokens(tokens, lookup, stack) {
4324
+ let mergedTokens = [];
4325
+ let buffer = [];
4326
+ let expanded = false;
4327
+ let index = 0;
4328
+ while (index < tokens.length) {
4329
+ const token = tokens[index];
4330
+ if (token === "--") {
4331
+ buffer.push(...tokens.slice(index));
4332
+ break;
4333
+ }
4334
+ const match = resolveUseOption(tokens, index);
4335
+ if (!match) {
4336
+ buffer.push(token);
4337
+ index += 1;
4338
+ continue;
4339
+ }
4340
+ if (buffer.length > 0) {
4341
+ mergedTokens = mergeRunCommandArgs(mergedTokens, buffer);
4342
+ buffer = [];
4343
+ }
4344
+ expanded = true;
4345
+ if (match.type === "alias") {
4346
+ const normalized2 = normalizeAliasName(match.name);
4347
+ if (!normalized2) {
4348
+ throw new Error("alias \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4349
+ }
4350
+ if (stack.alias.has(normalized2)) {
4351
+ throw new Error(`alias \u5FAA\u73AF\u5F15\u7528\uFF1A${normalized2}`);
4352
+ }
4353
+ const entry2 = lookup.aliasEntries.get(normalized2);
4354
+ if (!entry2) {
4355
+ throw new Error(`\u672A\u627E\u5230 alias\uFF1A${normalized2}`);
4356
+ }
4357
+ stack.alias.add(normalized2);
4358
+ const resolved2 = expandRunTokens(buildAliasRunArgs(entry2), lookup, stack);
4359
+ stack.alias.delete(normalized2);
4360
+ mergedTokens = mergeRunCommandArgs(mergedTokens, resolved2.tokens);
4361
+ index = match.nextIndex;
4362
+ continue;
4363
+ }
4364
+ const normalized = normalizeAgentName(match.name);
4365
+ if (!normalized) {
4366
+ throw new Error("agent \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4367
+ }
4368
+ if (stack.agent.has(normalized)) {
4369
+ throw new Error(`agent \u5FAA\u73AF\u5F15\u7528\uFF1A${normalized}`);
4370
+ }
4371
+ const entry = lookup.agentEntries.get(normalized);
4372
+ if (!entry) {
4373
+ throw new Error(`\u672A\u627E\u5230 agent\uFF1A${normalized}`);
4374
+ }
4375
+ stack.agent.add(normalized);
4376
+ const resolved = expandRunTokens(buildAgentRunArgs(entry), lookup, stack);
4377
+ stack.agent.delete(normalized);
4378
+ mergedTokens = mergeRunCommandArgs(mergedTokens, resolved.tokens);
4379
+ index = match.nextIndex;
4380
+ }
4381
+ if (buffer.length > 0) {
4382
+ mergedTokens = mergeRunCommandArgs(mergedTokens, buffer);
4383
+ }
4384
+ return { tokens: mergedTokens, expanded };
4385
+ }
3977
4386
  async function runForegroundWithDetach(options) {
3978
4387
  const args = buildBackgroundArgs(options.argv, options.logFile, options.branchName, options.injectBranch);
3979
4388
  const child = (0, import_node_child_process.spawn)(process.execPath, [...process.execArgv, ...args], {
@@ -4057,12 +4466,45 @@ async function runCli(argv) {
4057
4466
  const globalConfig = await loadGlobalConfig(defaultLogger);
4058
4467
  const effectiveArgv = applyShortcutArgv(argv, globalConfig);
4059
4468
  const program = new import_commander.Command();
4060
- program.name("wheel-ai").description("\u57FA\u4E8E AI CLI \u7684\u6301\u7EED\u8FED\u4EE3\u5F00\u53D1\u5DE5\u5177").version("0.2.0");
4469
+ program.name("wheel-ai").description("\u57FA\u4E8E AI CLI \u7684\u6301\u7EED\u8FED\u4EE3\u5F00\u53D1\u5DE5\u5177").version("0.2.1");
4061
4470
  program.addHelpText(
4062
4471
  "after",
4063
- "\n\u522B\u540D\u6267\u884C\uFF1A\n wheel-ai alias run <alias> <addition...>\n \u8FFD\u52A0\u547D\u4EE4\u4E0E alias \u91CD\u53E0\u65F6\uFF0C\u4EE5\u8FFD\u52A0\u4E3A\u51C6\u3002\n"
4472
+ "\nalias \u7BA1\u7406\uFF1A\n wheel-ai alias set <name> <options...>\n wheel-ai alias list\n wheel-ai alias delete <name>\n\nalias/agent \u53E0\u52A0\uFF1A\n wheel-ai run --use-alias <name> [--use-alias <name>...]\n wheel-ai run --use-agent <name> [--use-agent <name>...]\n \u540C\u540D\u9009\u9879\u6309\u51FA\u73B0\u987A\u5E8F\u8986\u76D6\u3002\n"
4064
4473
  );
4065
- program.command("run").option("-t, --task <task>", "\u9700\u8981\u5B8C\u6210\u7684\u4EFB\u52A1\u63CF\u8FF0\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u72EC\u7ACB\u5904\u7406\uFF09", collect, []).option("-i, --iterations <number>", "\u6700\u5927\u8FED\u4EE3\u6B21\u6570", (value) => parseInteger(value, 5), 5).option("--ai-cli <command>", "AI CLI \u547D\u4EE4", "claude").option("--ai-args <args...>", "AI CLI \u53C2\u6570", []).option("--ai-prompt-arg <flag>", "\u7528\u4E8E\u4F20\u5165 prompt \u7684\u53C2\u6570\uFF08\u4E3A\u7A7A\u5219\u4F7F\u7528 stdin\uFF09").option("--notes-file <path>", "\u6301\u4E45\u5316\u8BB0\u5FC6\u6587\u4EF6", defaultNotesPath()).option("--plan-file <path>", "\u8BA1\u5212\u6587\u4EF6", defaultPlanPath()).option("--workflow-doc <path>", "AI \u5DE5\u4F5C\u6D41\u7A0B\u8BF4\u660E\u6587\u4EF6", defaultWorkflowDoc()).option("--worktree", "\u5728\u72EC\u7ACB worktree \u4E0A\u6267\u884C", false).option("--branch <name>", "worktree \u5206\u652F\u540D\uFF08\u9ED8\u8BA4\u81EA\u52A8\u751F\u6210\u6216\u5F53\u524D\u5206\u652F\uFF09").option("--worktree-path <path>", "worktree \u8DEF\u5F84\uFF0C\u9ED8\u8BA4 ../worktrees/<branch>").option("--base-branch <name>", "\u521B\u5EFA\u5206\u652F\u7684\u57FA\u7EBF\u5206\u652F", "main").option("--skip-install", "\u8DF3\u8FC7\u5F00\u59CB\u4EFB\u52A1\u524D\u7684\u4F9D\u8D56\u68C0\u67E5", false).option("--run-tests", "\u8FD0\u884C\u5355\u5143\u6D4B\u8BD5\u547D\u4EE4", false).option("--run-e2e", "\u8FD0\u884C e2e \u6D4B\u8BD5\u547D\u4EE4", false).option("--unit-command <cmd>", "\u5355\u5143\u6D4B\u8BD5\u547D\u4EE4", "yarn test").option("--e2e-command <cmd>", "e2e \u6D4B\u8BD5\u547D\u4EE4", "yarn e2e").option("--auto-commit", "\u81EA\u52A8 git commit", false).option("--auto-push", "\u81EA\u52A8 git push", false).option("--pr", "\u4F7F\u7528 gh \u521B\u5EFA PR", false).option("--pr-title <title>", "PR \u6807\u9898").option("--pr-body <path>", "PR \u63CF\u8FF0\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u7559\u7A7A\u81EA\u52A8\u751F\u6210\uFF09").option("--draft", "\u4EE5\u8349\u7A3F\u5F62\u5F0F\u521B\u5EFA PR", false).option("--reviewer <user...>", "PR reviewers", collect, []).option("--auto-merge", "PR \u68C0\u67E5\u901A\u8FC7\u540E\u81EA\u52A8\u5408\u5E76", false).option("--webhook <url>", "webhook \u901A\u77E5 URL\uFF08\u53EF\u91CD\u590D\uFF09", collect, []).option("--webhook-timeout <ms>", "webhook \u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", (value) => parseInteger(value, 8e3)).option("--multi-task-mode <mode>", "\u591A\u4EFB\u52A1\u6267\u884C\u6A21\u5F0F\uFF08relay/serial/serial-continue/parallel\uFF0C\u6216\u4E2D\u6587\u63CF\u8FF0\uFF09", "relay").option("--stop-signal <token>", "AI \u8F93\u51FA\u4E2D\u7684\u505C\u6B62\u6807\u8BB0", "<<DONE>>").option("--log-file <path>", "\u65E5\u5FD7\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84").option("--background", "\u5207\u5165\u540E\u53F0\u8FD0\u884C", false).option("-v, --verbose", "\u8F93\u51FA\u8C03\u8BD5\u65E5\u5FD7", false).option("--skip-quality", "\u8DF3\u8FC7\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5", false).action(async (options) => {
4474
+ program.command("run").option("-t, --task <task>", "\u9700\u8981\u5B8C\u6210\u7684\u4EFB\u52A1\u63CF\u8FF0\uFF08\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u72EC\u7ACB\u5904\u7406\uFF09", collect, []).option("--use-alias <name>", "\u53E0\u52A0 alias \u914D\u7F6E\uFF08\u53EF\u91CD\u590D\uFF09", collect, []).option("--use-agent <name>", "\u53E0\u52A0 agent \u914D\u7F6E\uFF08\u53EF\u91CD\u590D\uFF09", collect, []).option("-i, --iterations <number>", "\u6700\u5927\u8FED\u4EE3\u6B21\u6570", (value) => parseInteger(value, 5), 5).option("--ai-cli <command>", "AI CLI \u547D\u4EE4", "claude").option("--ai-args <args...>", "AI CLI \u53C2\u6570", []).option("--ai-prompt-arg <flag>", "\u7528\u4E8E\u4F20\u5165 prompt \u7684\u53C2\u6570\uFF08\u4E3A\u7A7A\u5219\u4F7F\u7528 stdin\uFF09").option("--notes-file <path>", "\u6301\u4E45\u5316\u8BB0\u5FC6\u6587\u4EF6", defaultNotesPath()).option("--plan-file <path>", "\u8BA1\u5212\u6587\u4EF6", defaultPlanPath()).option("--workflow-doc <path>", "AI \u5DE5\u4F5C\u6D41\u7A0B\u8BF4\u660E\u6587\u4EF6", defaultWorkflowDoc()).option("--worktree", "\u5728\u72EC\u7ACB worktree \u4E0A\u6267\u884C", false).option("--branch <name>", "worktree \u5206\u652F\u540D\uFF08\u9ED8\u8BA4\u81EA\u52A8\u751F\u6210\u6216\u5F53\u524D\u5206\u652F\uFF09").option("--worktree-path <path>", "worktree \u8DEF\u5F84\uFF0C\u9ED8\u8BA4 ../worktrees/<branch>").option("--base-branch <name>", "\u521B\u5EFA\u5206\u652F\u7684\u57FA\u7EBF\u5206\u652F", "main").option("--skip-install", "\u8DF3\u8FC7\u5F00\u59CB\u4EFB\u52A1\u524D\u7684\u4F9D\u8D56\u68C0\u67E5", false).option("--run-tests", "\u8FD0\u884C\u5355\u5143\u6D4B\u8BD5\u547D\u4EE4", false).option("--run-e2e", "\u8FD0\u884C e2e \u6D4B\u8BD5\u547D\u4EE4", false).option("--unit-command <cmd>", "\u5355\u5143\u6D4B\u8BD5\u547D\u4EE4", "yarn test").option("--e2e-command <cmd>", "e2e \u6D4B\u8BD5\u547D\u4EE4", "yarn e2e").option("--auto-commit", "\u81EA\u52A8 git commit", false).option("--auto-push", "\u81EA\u52A8 git push", false).option("--pr", "\u4F7F\u7528 gh \u521B\u5EFA PR", false).option("--pr-title <title>", "PR \u6807\u9898").option("--pr-body <path>", "PR \u63CF\u8FF0\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u7559\u7A7A\u81EA\u52A8\u751F\u6210\uFF09").option("--draft", "\u4EE5\u8349\u7A3F\u5F62\u5F0F\u521B\u5EFA PR", false).option("--reviewer <user...>", "PR reviewers", collect, []).option("--auto-merge", "PR \u68C0\u67E5\u901A\u8FC7\u540E\u81EA\u52A8\u5408\u5E76", false).option("--webhook <url>", "webhook \u901A\u77E5 URL\uFF08\u53EF\u91CD\u590D\uFF09", collect, []).option("--webhook-timeout <ms>", "webhook \u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09", (value) => parseInteger(value, 8e3)).option("--multi-task-mode <mode>", "\u591A\u4EFB\u52A1\u6267\u884C\u6A21\u5F0F\uFF08relay/serial/serial-continue/parallel\uFF0C\u6216\u4E2D\u6587\u63CF\u8FF0\uFF09", "relay").option("--stop-signal <token>", "AI \u8F93\u51FA\u4E2D\u7684\u505C\u6B62\u6807\u8BB0", "<<DONE>>").option("--log-file <path>", "\u65E5\u5FD7\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84").option("--background", "\u5207\u5165\u540E\u53F0\u8FD0\u884C", false).option("-v, --verbose", "\u8F93\u51FA\u8C03\u8BD5\u65E5\u5FD7", false).option("--skip-quality", "\u8DF3\u8FC7\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5", false).action(async (options) => {
4475
+ const rawRunArgs = extractRunCommandArgs(effectiveArgv);
4476
+ const hasUseOptions = rawRunArgs.some(
4477
+ (token) => token === USE_ALIAS_FLAG || token.startsWith(`${USE_ALIAS_FLAG}=`) || token === USE_AGENT_FLAG || token.startsWith(`${USE_AGENT_FLAG}=`)
4478
+ );
4479
+ if (hasUseOptions) {
4480
+ const filePath = getGlobalConfigPath();
4481
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4482
+ const content = exists ? await import_fs_extra12.default.readFile(filePath, "utf8") : "";
4483
+ const aliasEntries = parseAliasEntries(content);
4484
+ const agentEntries = parseAgentEntries(content);
4485
+ const resolved = expandRunTokens(
4486
+ rawRunArgs,
4487
+ {
4488
+ aliasEntries: new Map(aliasEntries.map((entry) => [entry.name, entry])),
4489
+ agentEntries: new Map(agentEntries.map((entry) => [entry.name, entry]))
4490
+ },
4491
+ {
4492
+ alias: /* @__PURE__ */ new Set(),
4493
+ agent: /* @__PURE__ */ new Set()
4494
+ }
4495
+ );
4496
+ if (resolved.expanded) {
4497
+ const nextArgv = [process.argv[0], process.argv[1], "run", ...resolved.tokens];
4498
+ const originalArgv = process.argv;
4499
+ process.argv = nextArgv;
4500
+ try {
4501
+ await runCli(nextArgv);
4502
+ } finally {
4503
+ process.argv = originalArgv;
4504
+ }
4505
+ return;
4506
+ }
4507
+ }
4066
4508
  const tasks = normalizeTaskList(options.task);
4067
4509
  if (tasks.length === 0) {
4068
4510
  throw new Error("\u9700\u8981\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u4EFB\u52A1\u63CF\u8FF0");
@@ -4225,7 +4667,78 @@ async function runCli(argv) {
4225
4667
  program.command("logs").description("\u67E5\u770B\u5386\u53F2\u65E5\u5FD7").action(async () => {
4226
4668
  await runLogsViewer();
4227
4669
  });
4228
- program.command("set").description("\u5199\u5165\u5168\u5C40\u914D\u7F6E").command("alias <name> [options...]").description("\u8BBE\u7F6E alias").allowUnknownOption(true).action(async (name) => {
4670
+ const agentCommand = program.command("agent").description("\u7BA1\u7406 AI CLI agent \u914D\u7F6E");
4671
+ agentCommand.command("add <name> [command...]").description("\u65B0\u589E agent").allowUnknownOption(true).allowExcessArguments(true).action(async (name) => {
4672
+ const normalized = normalizeAgentName(name);
4673
+ if (!normalized) {
4674
+ throw new Error("agent \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4675
+ }
4676
+ const commandArgs = extractAgentCommandArgs(effectiveArgv, "add", name);
4677
+ const commandLine = formatCommandLine(commandArgs);
4678
+ if (!commandLine) {
4679
+ throw new Error("agent \u547D\u4EE4\u4E0D\u80FD\u4E3A\u7A7A");
4680
+ }
4681
+ const filePath = getGlobalConfigPath();
4682
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4683
+ const content = exists ? await import_fs_extra12.default.readFile(filePath, "utf8") : "";
4684
+ const entries = parseAgentEntries(content);
4685
+ if (entries.some((entry) => entry.name === normalized)) {
4686
+ throw new Error(`agent \u5DF2\u5B58\u5728\uFF1A${normalized}`);
4687
+ }
4688
+ await upsertAgentEntry(normalized, commandLine);
4689
+ console.log(`\u5DF2\u65B0\u589E agent\uFF1A${normalized}`);
4690
+ });
4691
+ agentCommand.command("set <name> [command...]").description("\u5199\u5165 agent").allowUnknownOption(true).allowExcessArguments(true).action(async (name) => {
4692
+ const normalized = normalizeAgentName(name);
4693
+ if (!normalized) {
4694
+ throw new Error("agent \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4695
+ }
4696
+ const commandArgs = extractAgentCommandArgs(effectiveArgv, "set", name);
4697
+ const commandLine = formatCommandLine(commandArgs);
4698
+ if (!commandLine) {
4699
+ throw new Error("agent \u547D\u4EE4\u4E0D\u80FD\u4E3A\u7A7A");
4700
+ }
4701
+ await upsertAgentEntry(normalized, commandLine);
4702
+ console.log(`\u5DF2\u5199\u5165 agent\uFF1A${normalized}`);
4703
+ });
4704
+ agentCommand.command("delete <name>").description("\u5220\u9664 agent").action(async (name) => {
4705
+ const normalized = normalizeAgentName(name);
4706
+ if (!normalized) {
4707
+ throw new Error("agent \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4708
+ }
4709
+ const filePath = getGlobalConfigPath();
4710
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4711
+ if (!exists) {
4712
+ throw new Error(`\u672A\u627E\u5230 agent \u914D\u7F6E\u6587\u4EF6\uFF1A${filePath}`);
4713
+ }
4714
+ const removed = await removeAgentEntry(normalized);
4715
+ if (!removed) {
4716
+ throw new Error(`\u672A\u627E\u5230 agent\uFF1A${normalized}`);
4717
+ }
4718
+ console.log(`\u5DF2\u5220\u9664 agent\uFF1A${normalized}`);
4719
+ });
4720
+ agentCommand.command("list").description("\u5217\u51FA agent \u914D\u7F6E").action(async () => {
4721
+ const filePath = getGlobalConfigPath();
4722
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4723
+ if (!exists) {
4724
+ console.log("\u672A\u53D1\u73B0 agent \u914D\u7F6E");
4725
+ return;
4726
+ }
4727
+ const content = await import_fs_extra12.default.readFile(filePath, "utf8");
4728
+ const entries = parseAgentEntries(content);
4729
+ if (entries.length === 0) {
4730
+ console.log("\u672A\u53D1\u73B0 agent \u914D\u7F6E");
4731
+ return;
4732
+ }
4733
+ entries.forEach((entry) => {
4734
+ console.log(`${entry.name}: ${entry.command}`);
4735
+ });
4736
+ });
4737
+ agentCommand.action(() => {
4738
+ agentCommand.help();
4739
+ });
4740
+ const aliasCommand = program.command("alias").alias("aliases").description("\u7BA1\u7406\u5168\u5C40 alias \u914D\u7F6E");
4741
+ aliasCommand.command("set <name> [options...]").description("\u5199\u5165 alias").allowUnknownOption(true).allowExcessArguments(true).action(async (name) => {
4229
4742
  const normalized = normalizeAliasName(name);
4230
4743
  if (!normalized) {
4231
4744
  throw new Error("alias \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
@@ -4238,7 +4751,39 @@ async function runCli(argv) {
4238
4751
  await upsertAliasEntry(normalized, commandLine);
4239
4752
  console.log(`\u5DF2\u5199\u5165 alias\uFF1A${normalized}`);
4240
4753
  });
4241
- const aliasCommand = program.command("alias").alias("aliases").description("\u6D4F\u89C8\u5168\u5C40 alias \u914D\u7F6E\uFF08alias run \u53EF\u6267\u884C\u5E76\u8FFD\u52A0\u547D\u4EE4\uFF09");
4754
+ aliasCommand.command("list").description("\u5217\u51FA alias \u914D\u7F6E").action(async () => {
4755
+ const filePath = getGlobalConfigPath();
4756
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4757
+ if (!exists) {
4758
+ console.log("\u672A\u53D1\u73B0 alias \u914D\u7F6E");
4759
+ return;
4760
+ }
4761
+ const content = await import_fs_extra12.default.readFile(filePath, "utf8");
4762
+ const entries = parseAliasEntries(content).filter((entry) => entry.source === "alias");
4763
+ if (entries.length === 0) {
4764
+ console.log("\u672A\u53D1\u73B0 alias \u914D\u7F6E");
4765
+ return;
4766
+ }
4767
+ entries.forEach((entry) => {
4768
+ console.log(`${entry.name}: ${entry.command}`);
4769
+ });
4770
+ });
4771
+ aliasCommand.command("delete <name>").description("\u5220\u9664 alias").action(async (name) => {
4772
+ const normalized = normalizeAliasName(name);
4773
+ if (!normalized) {
4774
+ throw new Error("alias \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u5B57\u7B26");
4775
+ }
4776
+ const filePath = getGlobalConfigPath();
4777
+ const exists = await import_fs_extra12.default.pathExists(filePath);
4778
+ if (!exists) {
4779
+ throw new Error(`\u672A\u627E\u5230 alias \u914D\u7F6E\u6587\u4EF6\uFF1A${filePath}`);
4780
+ }
4781
+ const removed = await removeAliasEntry(normalized);
4782
+ if (!removed) {
4783
+ throw new Error(`\u672A\u627E\u5230 alias\uFF1A${normalized}`);
4784
+ }
4785
+ console.log(`\u5DF2\u5220\u9664 alias\uFF1A${normalized}`);
4786
+ });
4242
4787
  aliasCommand.command("run <name> [addition...]").description("\u6267\u884C alias \u5E76\u8FFD\u52A0\u547D\u4EE4").allowUnknownOption(true).allowExcessArguments(true).action(async (name) => {
4243
4788
  const normalized = normalizeAliasName(name);
4244
4789
  if (!normalized) {
@@ -4255,9 +4800,9 @@ async function runCli(argv) {
4255
4800
  if (!entry) {
4256
4801
  throw new Error(`\u672A\u627E\u5230 alias\uFF1A${normalized}`);
4257
4802
  }
4258
- const aliasTokens = normalizeAliasCommandArgs(splitCommandArgs(entry.command));
4803
+ const aliasTokens = normalizeRunCommandArgs(splitCommandArgs(entry.command));
4259
4804
  const additionTokens = extractAliasRunArgs(effectiveArgv, normalized);
4260
- const mergedTokens = mergeAliasCommandArgs(aliasTokens, additionTokens);
4805
+ const mergedTokens = mergeRunCommandArgs(aliasTokens, additionTokens);
4261
4806
  if (mergedTokens.length === 0) {
4262
4807
  throw new Error("alias \u547D\u4EE4\u4E0D\u80FD\u4E3A\u7A7A");
4263
4808
  }