jinzd-ai-cli 0.4.80 → 0.4.82

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/README.zh-CN.md CHANGED
@@ -20,7 +20,9 @@
20
20
  - **Unified Diff Patch 编辑**(v0.4.72+)— `edit_file` 支持标准 `@@ -a,b +c,d @@` hunk,大文件多处小改最省 token;容忍 ±200 行漂移与空白差异
21
21
  - **Anthropic Batches API**(v0.4.73+)— `aicli batch submit/list/status/results/cancel` 包 Message Batches(50% 折扣 + 24 小时窗口),适合离线分析和批量 eval
22
22
  - **Web UI 会话回放**(v0.4.71+)— 会话列表每项 🎬 按钮打开时间轴回放:消息、工具调用、推理内容、cache-aware token 用量一目了然
23
- - **对话分支**(v0.4.74+)— REPL 内 `/branch list/new/switch/delete/rename`,Web UI 回放面板每条消息旁 🌿 "fork here" 按钮,任意位置开辟新分支探索不同方向,原对话保持不变
23
+ - **对话分支**(v0.4.74+)— REPL 内 `/branch list/new/switch/delete/rename/diff/cherry-pick`,Web UI 回放面板每条消息旁 🌿 "fork here" 按钮,任意位置开辟新分支探索不同方向,原对话保持不变;v0.4.81 起所有子命令支持 id / title / 唯一前缀
24
+ - **符号索引**(v0.4.76+,C1)— tree-sitter 持久化索引(TS/JS/TSX/JSX/Python)+ 3 个只读 AI 工具(`find_symbol` / `get_outline` / `find_references`)+ `/index status/rebuild/clear`;启动后台增量刷新,`write_file` 后自动 upsert
25
+ - **语义代码搜索**(v0.4.77+,C2)— `search_code` AI 工具 + 本地 sentence embedding(`paraphrase-multilingual-MiniLM-L12-v2`,117 MB 一次性下载,384 维,CPU 运行);支持中英文自然语言查询代码;`/index semantic-rebuild/semantic-clear` 管理
24
26
  - **流式工具调用** — 实时流式展示 AI 推理过程和工具调用
25
27
  - **子代理系统** — 将复杂子任务委派给独立子代理执行
26
28
  - **深度推理** — Claude Extended Thinking,`/think` 一键切换
@@ -137,7 +139,7 @@ aicli
137
139
 
138
140
  ## 内置工具(Agentic 能力)
139
141
 
140
- AI 在对话中可自主调用 24 个工具:
142
+ AI 在对话中可自主调用 28 个工具:
141
143
 
142
144
  | 工具 | 安全级别 | 说明 |
143
145
  |------|---------|------|
@@ -165,9 +167,20 @@ AI 在对话中可自主调用 24 个工具:
165
167
  | `git_log` | 安全 | 显示提交历史(单行/完整,按文件/作者过滤) |
166
168
  | `git_commit` | 写入 | 创建 git 提交(暂存文件、提交信息) |
167
169
  | `notebook_edit` | 写入 | 编辑 Jupyter notebook 单元格(增/改/删/移动) |
170
+ | `find_symbol` | 安全 | 按名称精确查找函数/类/方法定义(C1,tree-sitter 索引) |
171
+ | `get_outline` | 安全 | 获取文件的符号大纲(类 → 方法树)(C1) |
172
+ | `find_references` | 安全 | 查找符号的所有引用(C1) |
173
+ | `search_code` | 安全 | 语义代码搜索(C2,自然语言查询,中英文均可) |
168
174
 
169
175
  **安全级别**:`安全` = 自动执行,`写入` = diff 预览 + 确认,`破坏性` = 醒目警告 + 确认。
170
176
 
177
+ ### 文档深入阅读
178
+
179
+ - [`docs/TUTORIAL.zh-CN.md`](docs/TUTORIAL.zh-CN.md) — **10 节动手教程**,1 小时从零到熟练
180
+ - [`docs/USAGE.zh-CN.md`](docs/USAGE.zh-CN.md) — 完整参考手册(所有命令、工具、配置)
181
+ - [`docs/ADVANCED.zh-CN.md`](docs/ADVANCED.zh-CN.md) — 架构与内部原理(开发者向)
182
+ - [`docs/RECIPES.zh-CN.md`](docs/RECIPES.zh-CN.md) — 实战配方(按场景查)
183
+
171
184
  ## 主要 REPL 命令
172
185
 
173
186
  | 命令 | 说明 |
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-C5QENJ4P.js";
4
+ } from "./chunk-CUIALV3O.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-2ERX5Q55.js";
6
+ import "./chunk-KBVJGOD6.js";
7
7
 
8
8
  // src/cli/batch.ts
9
9
  import Anthropic from "@anthropic-ai/sdk";
@@ -6,7 +6,7 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.80";
9
+ var VERSION = "0.4.82";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-2ERX5Q55.js";
4
+ } from "./chunk-KBVJGOD6.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync } from "child_process";
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-2ERX5Q55.js";
11
+ } from "./chunk-KBVJGOD6.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-6VRJGH25.js";
20
20
  import {
21
21
  runTestsTool
22
- } from "./chunk-IGHC7D62.js";
22
+ } from "./chunk-6GVBLOS3.js";
23
23
  import {
24
24
  CONFIG_DIR_NAME,
25
25
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -27,7 +27,7 @@ import {
27
27
  SUBAGENT_ALLOWED_TOOLS,
28
28
  SUBAGENT_DEFAULT_MAX_ROUNDS,
29
29
  SUBAGENT_MAX_ROUNDS_LIMIT
30
- } from "./chunk-2ERX5Q55.js";
30
+ } from "./chunk-KBVJGOD6.js";
31
31
 
32
32
  // src/tools/builtin/bash.ts
33
33
  import { execSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.80";
4
+ var VERSION = "0.4.82";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-CU5ZSHGU.js";
5
+ } from "./chunk-JHQMDIV4.js";
6
6
  import {
7
7
  AuthError,
8
8
  ProviderError,
@@ -18,7 +18,7 @@ import {
18
18
  MCP_PROTOCOL_VERSION,
19
19
  MCP_TOOL_PREFIX,
20
20
  VERSION
21
- } from "./chunk-2ERX5Q55.js";
21
+ } from "./chunk-KBVJGOD6.js";
22
22
 
23
23
  // src/providers/claude.ts
24
24
  import Anthropic from "@anthropic-ai/sdk";
@@ -2376,6 +2376,37 @@ var Session = class _Session {
2376
2376
  this.updated = /* @__PURE__ */ new Date();
2377
2377
  return true;
2378
2378
  }
2379
+ /**
2380
+ * Resolve a user-supplied branch reference (v0.4.81+).
2381
+ *
2382
+ * Accepts the 6-hex branch id, the branch's display title, or an unambiguous
2383
+ * id prefix (≥2 chars). This exists because users naturally type the title
2384
+ * they chose at `/branch new`, not the auto-generated id — every other
2385
+ * `/branch <sub>` used to error on that with "not found".
2386
+ *
2387
+ * Resolution order:
2388
+ * 1. Exact id match
2389
+ * 2. Exact title match (unique)
2390
+ * 3. Id prefix match (unique, length ≥ 2)
2391
+ *
2392
+ * @returns `{ ok: true, id }` on success; on failure `{ ok: false, reason, matches }`
2393
+ * where `matches` lists candidates for ambiguous input so callers
2394
+ * can render a helpful error.
2395
+ */
2396
+ resolveBranchRef(ref) {
2397
+ if (!ref) return { ok: false, reason: "not-found", matches: [] };
2398
+ const byId = this.branches.find((b) => b.id === ref);
2399
+ if (byId) return { ok: true, id: byId.id };
2400
+ const byTitle = this.branches.filter((b) => b.title === ref);
2401
+ if (byTitle.length === 1) return { ok: true, id: byTitle[0].id };
2402
+ if (byTitle.length > 1) return { ok: false, reason: "ambiguous", matches: byTitle };
2403
+ if (ref.length >= 2) {
2404
+ const byPrefix = this.branches.filter((b) => b.id.startsWith(ref));
2405
+ if (byPrefix.length === 1) return { ok: true, id: byPrefix[0].id };
2406
+ if (byPrefix.length > 1) return { ok: false, reason: "ambiguous", matches: byPrefix };
2407
+ }
2408
+ return { ok: false, reason: "not-found", matches: [] };
2409
+ }
2379
2410
  /** Messages of any branch (active or inactive) — read-only copy. */
2380
2411
  getBranchMessages(id) {
2381
2412
  if (id === this.activeBranchId) return this.messages.slice();
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-63R2GIRK.js";
39
+ } from "./chunk-3A6FQYJZ.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -2757,6 +2757,37 @@ var Session = class _Session {
2757
2757
  this.updated = /* @__PURE__ */ new Date();
2758
2758
  return true;
2759
2759
  }
2760
+ /**
2761
+ * Resolve a user-supplied branch reference (v0.4.81+).
2762
+ *
2763
+ * Accepts the 6-hex branch id, the branch's display title, or an unambiguous
2764
+ * id prefix (≥2 chars). This exists because users naturally type the title
2765
+ * they chose at `/branch new`, not the auto-generated id — every other
2766
+ * `/branch <sub>` used to error on that with "not found".
2767
+ *
2768
+ * Resolution order:
2769
+ * 1. Exact id match
2770
+ * 2. Exact title match (unique)
2771
+ * 3. Id prefix match (unique, length ≥ 2)
2772
+ *
2773
+ * @returns `{ ok: true, id }` on success; on failure `{ ok: false, reason, matches }`
2774
+ * where `matches` lists candidates for ambiguous input so callers
2775
+ * can render a helpful error.
2776
+ */
2777
+ resolveBranchRef(ref) {
2778
+ if (!ref) return { ok: false, reason: "not-found", matches: [] };
2779
+ const byId = this.branches.find((b) => b.id === ref);
2780
+ if (byId) return { ok: true, id: byId.id };
2781
+ const byTitle = this.branches.filter((b) => b.title === ref);
2782
+ if (byTitle.length === 1) return { ok: true, id: byTitle[0].id };
2783
+ if (byTitle.length > 1) return { ok: false, reason: "ambiguous", matches: byTitle };
2784
+ if (ref.length >= 2) {
2785
+ const byPrefix = this.branches.filter((b) => b.id.startsWith(ref));
2786
+ if (byPrefix.length === 1) return { ok: true, id: byPrefix[0].id };
2787
+ if (byPrefix.length > 1) return { ok: false, reason: "ambiguous", matches: byPrefix };
2788
+ }
2789
+ return { ok: false, reason: "not-found", matches: [] };
2790
+ }
2760
2791
  /** Messages of any branch (active or inactive) — read-only copy. */
2761
2792
  getBranchMessages(id) {
2762
2793
  if (id === this.activeBranchId) return this.messages.slice();
@@ -10622,6 +10653,17 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10622
10653
  break;
10623
10654
  }
10624
10655
  const sub = args[0]?.toLowerCase();
10656
+ const resolve6 = (ref) => {
10657
+ const r = session.resolveBranchRef(ref);
10658
+ if (r.ok) return r.id;
10659
+ if (r.reason === "ambiguous") {
10660
+ const list = r.matches.map((m) => `${m.id} (${m.title})`).join(", ");
10661
+ this.send({ type: "error", message: `Ambiguous branch reference "${ref}". Matches: ${list}. Use the 6-char id.` });
10662
+ } else {
10663
+ this.send({ type: "error", message: `Branch "${ref}" not found. Use /branch list to see all branches.` });
10664
+ }
10665
+ return null;
10666
+ };
10625
10667
  if (!sub || sub === "list") {
10626
10668
  const branches = session.listBranches();
10627
10669
  const lines = [`\u{1F33F} Branches (${branches.length}):`, ""];
@@ -10632,7 +10674,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10632
10674
  lines.push(` ${marker}${b.id.padEnd(10)} ${b.title.padEnd(20)} (${count} msgs)${parent}`);
10633
10675
  }
10634
10676
  lines.push("");
10635
- lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>");
10677
+ lines.push("Usage: /branch new <msgIndex> [title] | switch <id|title> | delete <id|title> | rename <id|title> <new title> | diff <id|title> | cherry-pick <id|title> <msgIndex>");
10636
10678
  this.send({ type: "info", message: lines.join("\n") });
10637
10679
  break;
10638
10680
  }
@@ -10660,11 +10702,13 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10660
10702
  break;
10661
10703
  }
10662
10704
  if (sub === "switch") {
10663
- const id = args[1];
10664
- if (!id) {
10665
- this.send({ type: "error", message: "Usage: /branch switch <id>" });
10705
+ const ref = args[1];
10706
+ if (!ref) {
10707
+ this.send({ type: "error", message: "Usage: /branch switch <id|title>" });
10666
10708
  break;
10667
10709
  }
10710
+ const id = resolve6(ref);
10711
+ if (!id) break;
10668
10712
  const ok = session.switchBranch(id);
10669
10713
  if (ok) {
10670
10714
  await this.sessions.save();
@@ -10673,33 +10717,37 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10673
10717
  this.sendSessionMessages();
10674
10718
  this.sendStatus();
10675
10719
  } else {
10676
- this.send({ type: "error", message: `Cannot switch to "${id}" (not found or already active).` });
10720
+ this.send({ type: "error", message: `Cannot switch to "${id}" (already active).` });
10677
10721
  }
10678
10722
  break;
10679
10723
  }
10680
10724
  if (sub === "delete") {
10681
- const id = args[1];
10682
- if (!id) {
10683
- this.send({ type: "error", message: "Usage: /branch delete <id>" });
10725
+ const ref = args[1];
10726
+ if (!ref) {
10727
+ this.send({ type: "error", message: "Usage: /branch delete <id|title>" });
10684
10728
  break;
10685
10729
  }
10730
+ const id = resolve6(ref);
10731
+ if (!id) break;
10686
10732
  const ok = session.deleteBranch(id);
10687
10733
  if (ok) {
10688
10734
  await this.sessions.save();
10689
10735
  this.send({ type: "info", message: `\u2713 Deleted branch "${id}"` });
10690
10736
  this.sendStatus();
10691
10737
  } else {
10692
- this.send({ type: "error", message: `Cannot delete "${id}" (not found, active, or last remaining branch).` });
10738
+ this.send({ type: "error", message: `Cannot delete "${id}" (active, or last remaining branch).` });
10693
10739
  }
10694
10740
  break;
10695
10741
  }
10696
10742
  if (sub === "rename") {
10697
- const id = args[1];
10743
+ const ref = args[1];
10698
10744
  const title = args.slice(2).join(" ").trim();
10699
- if (!id || !title) {
10700
- this.send({ type: "error", message: "Usage: /branch rename <id> <new title>" });
10745
+ if (!ref || !title) {
10746
+ this.send({ type: "error", message: "Usage: /branch rename <id|title> <new title>" });
10701
10747
  break;
10702
10748
  }
10749
+ const id = resolve6(ref);
10750
+ if (!id) break;
10703
10751
  const ok = session.renameBranch(id, title);
10704
10752
  if (ok) {
10705
10753
  await this.sessions.save();
@@ -10711,11 +10759,13 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10711
10759
  break;
10712
10760
  }
10713
10761
  if (sub === "diff") {
10714
- const id = args[1];
10715
- if (!id) {
10716
- this.send({ type: "error", message: "Usage: /branch diff <id>" });
10762
+ const ref = args[1];
10763
+ if (!ref) {
10764
+ this.send({ type: "error", message: "Usage: /branch diff <id|title>" });
10717
10765
  break;
10718
10766
  }
10767
+ const id = resolve6(ref);
10768
+ if (!id) break;
10719
10769
  const d = session.diffBranches(id);
10720
10770
  if (!d) {
10721
10771
  this.send({ type: "error", message: `Branch "${id}" not found.` });
@@ -10750,12 +10800,14 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10750
10800
  break;
10751
10801
  }
10752
10802
  if (sub === "cherry-pick") {
10753
- const id = args[1];
10803
+ const ref = args[1];
10754
10804
  const idxStr = args[2];
10755
- if (!id || !idxStr) {
10756
- this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id> <msg-index>" });
10805
+ if (!ref || !idxStr) {
10806
+ this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id|title> <msg-index>" });
10757
10807
  break;
10758
10808
  }
10809
+ const id = resolve6(ref);
10810
+ if (!id) break;
10759
10811
  const idx = parseInt(idxStr, 10);
10760
10812
  if (Number.isNaN(idx)) {
10761
10813
  this.send({ type: "error", message: `Invalid message index: "${idxStr}"` });
@@ -10763,7 +10815,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10763
10815
  }
10764
10816
  const picked = session.cherryPickMessage(id, idx);
10765
10817
  if (!picked) {
10766
- this.send({ type: "error", message: `Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.` });
10818
+ this.send({ type: "error", message: `Cherry-pick failed \u2014 index ${idx} out of range for branch "${id}".` });
10767
10819
  break;
10768
10820
  }
10769
10821
  await this.sessions.save();
@@ -11018,7 +11070,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11018
11070
  case "test": {
11019
11071
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11020
11072
  try {
11021
- const { executeTests } = await import("./run-tests-4PZKLURD.js");
11073
+ const { executeTests } = await import("./run-tests-QJDIKKPF.js");
11022
11074
  const argStr = args.join(" ").trim();
11023
11075
  let testArgs = {};
11024
11076
  if (argStr) {
@@ -385,7 +385,7 @@ ${content}`);
385
385
  }
386
386
  }
387
387
  async function runTaskMode(config, providers, configManager, topic) {
388
- const { TaskOrchestrator } = await import("./task-orchestrator-RVKRP6LI.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-2QGCYWCI.js");
389
389
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
390
390
  let interrupted = false;
391
391
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -30,10 +30,10 @@ import {
30
30
  saveDevState,
31
31
  sessionHasMeaningfulContent,
32
32
  setupProxy
33
- } from "./chunk-6HZTXVTF.js";
33
+ } from "./chunk-PS7IEXHQ.js";
34
34
  import {
35
35
  ConfigManager
36
- } from "./chunk-C5QENJ4P.js";
36
+ } from "./chunk-CUIALV3O.js";
37
37
  import {
38
38
  ToolExecutor,
39
39
  ToolRegistry,
@@ -49,7 +49,7 @@ import {
49
49
  spawnAgentContext,
50
50
  theme,
51
51
  undoStack
52
- } from "./chunk-CU5ZSHGU.js";
52
+ } from "./chunk-JHQMDIV4.js";
53
53
  import "./chunk-2ZD3YTVM.js";
54
54
  import {
55
55
  fileCheckpoints
@@ -58,7 +58,7 @@ import "./chunk-NHNWUBXB.js";
58
58
  import "./chunk-CQQQFNND.js";
59
59
  import "./chunk-6VRJGH25.js";
60
60
  import "./chunk-PFYAAX2S.js";
61
- import "./chunk-IGHC7D62.js";
61
+ import "./chunk-6GVBLOS3.js";
62
62
  import {
63
63
  AGENTIC_BEHAVIOR_GUIDELINE,
64
64
  AUTHOR,
@@ -80,7 +80,7 @@ import {
80
80
  SKILLS_DIR_NAME,
81
81
  VERSION,
82
82
  buildUserIdentityPrompt
83
- } from "./chunk-2ERX5Q55.js";
83
+ } from "./chunk-KBVJGOD6.js";
84
84
 
85
85
  // src/index.ts
86
86
  import { program } from "commander";
@@ -2254,7 +2254,7 @@ ${hint}` : "")
2254
2254
  {
2255
2255
  name: "branch",
2256
2256
  description: "Manage conversation branches (fork/switch/list/delete/rename)",
2257
- usage: "/branch [list | new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>]",
2257
+ usage: "/branch [list | new <msgIndex> [title] | switch <id|title> | delete <id|title> | rename <id|title> <new> | diff <id|title> | cherry-pick <id|title> <msgIndex>]",
2258
2258
  async execute(args, ctx) {
2259
2259
  const session = ctx.sessions.current;
2260
2260
  if (!session) {
@@ -2262,6 +2262,17 @@ ${hint}` : "")
2262
2262
  return;
2263
2263
  }
2264
2264
  const sub = args[0]?.toLowerCase();
2265
+ const resolve3 = (ref) => {
2266
+ const r = session.resolveBranchRef(ref);
2267
+ if (r.ok) return r.id;
2268
+ if (r.reason === "ambiguous") {
2269
+ const list = r.matches.map((m) => `${m.id} (${m.title})`).join(", ");
2270
+ ctx.renderer.renderError(`Ambiguous branch reference "${ref}". Matches: ${list}. Use the 6-char id.`);
2271
+ } else {
2272
+ ctx.renderer.renderError(`Branch "${ref}" not found. Try /branch list to see all branches.`);
2273
+ }
2274
+ return null;
2275
+ };
2265
2276
  if (!sub || sub === "list") {
2266
2277
  const branches = session.listBranches();
2267
2278
  console.log(theme.heading(`
@@ -2276,12 +2287,13 @@ ${hint}` : "")
2276
2287
  );
2277
2288
  }
2278
2289
  console.log();
2279
- console.log(theme.dim(" /branch new <msgIndex> [title] \u2014 fork active branch at message N"));
2280
- console.log(theme.dim(" /branch switch <id> \u2014 switch active branch"));
2281
- console.log(theme.dim(" /branch delete <id> \u2014 delete inactive branch"));
2282
- console.log(theme.dim(" /branch rename <id> <title> \u2014 rename a branch"));
2283
- console.log(theme.dim(" /branch diff <id> \u2014 compare branch with active, list divergent messages"));
2284
- console.log(theme.dim(" /branch cherry-pick <id> <idx> \u2014 copy one message from branch <id> into active"));
2290
+ console.log(theme.dim(" /branch new <msgIndex> [title] \u2014 fork active branch at message N"));
2291
+ console.log(theme.dim(" /branch switch <id|title> \u2014 switch active branch"));
2292
+ console.log(theme.dim(" /branch delete <id|title> \u2014 delete inactive branch"));
2293
+ console.log(theme.dim(" /branch rename <id|title> <new> \u2014 rename a branch"));
2294
+ console.log(theme.dim(" /branch diff <id|title> \u2014 compare branch with active, list divergent messages"));
2295
+ console.log(theme.dim(" /branch cherry-pick <id|title> <idx> \u2014 copy one message from branch into active"));
2296
+ console.log(theme.dim(" (id, title, or unique id-prefix all work)"));
2285
2297
  console.log();
2286
2298
  return;
2287
2299
  }
@@ -2310,11 +2322,13 @@ ${hint}` : "")
2310
2322
  return;
2311
2323
  }
2312
2324
  if (sub === "switch") {
2313
- const id = args[1];
2314
- if (!id) {
2315
- ctx.renderer.renderError("Usage: /branch switch <id>");
2325
+ const ref = args[1];
2326
+ if (!ref) {
2327
+ ctx.renderer.renderError("Usage: /branch switch <id|title>");
2316
2328
  return;
2317
2329
  }
2330
+ const id = resolve3(ref);
2331
+ if (!id) return;
2318
2332
  const ok = session.switchBranch(id);
2319
2333
  if (ok) {
2320
2334
  await ctx.sessions.save();
@@ -2324,16 +2338,18 @@ ${hint}` : "")
2324
2338
  console.log(theme.dim(` ${session.messages.length} messages on this branch
2325
2339
  `));
2326
2340
  } else {
2327
- ctx.renderer.renderError(`Cannot switch to "${id}" (not found or already active).`);
2341
+ ctx.renderer.renderError(`Cannot switch to "${id}" (already active).`);
2328
2342
  }
2329
2343
  return;
2330
2344
  }
2331
2345
  if (sub === "delete") {
2332
- const id = args[1];
2333
- if (!id) {
2334
- ctx.renderer.renderError("Usage: /branch delete <id>");
2346
+ const ref = args[1];
2347
+ if (!ref) {
2348
+ ctx.renderer.renderError("Usage: /branch delete <id|title>");
2335
2349
  return;
2336
2350
  }
2351
+ const id = resolve3(ref);
2352
+ if (!id) return;
2337
2353
  const ok = session.deleteBranch(id);
2338
2354
  if (ok) {
2339
2355
  await ctx.sessions.save();
@@ -2342,18 +2358,20 @@ ${hint}` : "")
2342
2358
  `));
2343
2359
  } else {
2344
2360
  ctx.renderer.renderError(
2345
- `Cannot delete "${id}" (not found, active, or last remaining branch).`
2361
+ `Cannot delete "${id}" (active, or last remaining branch).`
2346
2362
  );
2347
2363
  }
2348
2364
  return;
2349
2365
  }
2350
2366
  if (sub === "rename") {
2351
- const id = args[1];
2367
+ const ref = args[1];
2352
2368
  const title = args.slice(2).join(" ").trim();
2353
- if (!id || !title) {
2354
- ctx.renderer.renderError("Usage: /branch rename <id> <new title>");
2369
+ if (!ref || !title) {
2370
+ ctx.renderer.renderError("Usage: /branch rename <id|title> <new title>");
2355
2371
  return;
2356
2372
  }
2373
+ const id = resolve3(ref);
2374
+ if (!id) return;
2357
2375
  const ok = session.renameBranch(id, title);
2358
2376
  if (ok) {
2359
2377
  await ctx.sessions.save();
@@ -2366,11 +2384,13 @@ ${hint}` : "")
2366
2384
  return;
2367
2385
  }
2368
2386
  if (sub === "diff") {
2369
- const id = args[1];
2370
- if (!id) {
2371
- ctx.renderer.renderError("Usage: /branch diff <id> \u2014 diff the given branch against the active branch");
2387
+ const ref = args[1];
2388
+ if (!ref) {
2389
+ ctx.renderer.renderError("Usage: /branch diff <id|title> \u2014 diff the given branch against the active branch");
2372
2390
  return;
2373
2391
  }
2392
+ const id = resolve3(ref);
2393
+ if (!id) return;
2374
2394
  const d = session.diffBranches(id);
2375
2395
  if (!d) {
2376
2396
  ctx.renderer.renderError(`Branch "${id}" not found.`);
@@ -2408,12 +2428,14 @@ ${hint}` : "")
2408
2428
  return;
2409
2429
  }
2410
2430
  if (sub === "cherry-pick") {
2411
- const id = args[1];
2431
+ const ref = args[1];
2412
2432
  const idxStr = args[2];
2413
- if (!id || !idxStr) {
2414
- ctx.renderer.renderError("Usage: /branch cherry-pick <source-id> <msg-index>");
2433
+ if (!ref || !idxStr) {
2434
+ ctx.renderer.renderError("Usage: /branch cherry-pick <source-id|title> <msg-index>");
2415
2435
  return;
2416
2436
  }
2437
+ const id = resolve3(ref);
2438
+ if (!id) return;
2417
2439
  const idx = parseInt(idxStr, 10);
2418
2440
  if (Number.isNaN(idx)) {
2419
2441
  ctx.renderer.renderError(`Invalid message index: "${idxStr}"`);
@@ -2421,7 +2443,7 @@ ${hint}` : "")
2421
2443
  }
2422
2444
  const picked = session.cherryPickMessage(id, idx);
2423
2445
  if (!picked) {
2424
- ctx.renderer.renderError(`Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.`);
2446
+ ctx.renderer.renderError(`Cherry-pick failed \u2014 index ${idx} out of range for branch "${id}".`);
2425
2447
  return;
2426
2448
  }
2427
2449
  await ctx.sessions.save();
@@ -2570,7 +2592,7 @@ ${hint}` : "")
2570
2592
  usage: "/test [command|filter]",
2571
2593
  async execute(args, ctx) {
2572
2594
  try {
2573
- const { executeTests } = await import("./run-tests-7N2WILA3.js");
2595
+ const { executeTests } = await import("./run-tests-KDJI6VFZ.js");
2574
2596
  const argStr = args.join(" ").trim();
2575
2597
  let testArgs = {};
2576
2598
  if (argStr) {
@@ -6463,7 +6485,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6463
6485
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6464
6486
  process.exit(1);
6465
6487
  }
6466
- const { startWebServer } = await import("./server-HJ64OR7A.js");
6488
+ const { startWebServer } = await import("./server-VBR2F6YU.js");
6467
6489
  await startWebServer({ port, host: options.host });
6468
6490
  });
6469
6491
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6586,7 +6608,7 @@ program.command("sessions").description("List recent conversation sessions").act
6586
6608
  });
6587
6609
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
6588
6610
  try {
6589
- const batch = await import("./batch-OUBKAHXG.js");
6611
+ const batch = await import("./batch-2NLB5ZUL.js");
6590
6612
  switch (action) {
6591
6613
  case "submit":
6592
6614
  if (!arg) {
@@ -6746,7 +6768,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6746
6768
  }),
6747
6769
  config.get("customProviders")
6748
6770
  );
6749
- const { startHub } = await import("./hub-SFJU4KJK.js");
6771
+ const { startHub } = await import("./hub-6UHFVBNR.js");
6750
6772
  await startHub(
6751
6773
  {
6752
6774
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-IGHC7D62.js";
6
- import "./chunk-2ERX5Q55.js";
5
+ } from "./chunk-6GVBLOS3.js";
6
+ import "./chunk-KBVJGOD6.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-63R2GIRK.js";
4
+ } from "./chunk-3A6FQYJZ.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -20,10 +20,10 @@ import {
20
20
  persistToolRound,
21
21
  rebuildExtraMessages,
22
22
  setupProxy
23
- } from "./chunk-6HZTXVTF.js";
23
+ } from "./chunk-PS7IEXHQ.js";
24
24
  import {
25
25
  ConfigManager
26
- } from "./chunk-C5QENJ4P.js";
26
+ } from "./chunk-CUIALV3O.js";
27
27
  import {
28
28
  ToolExecutor,
29
29
  ToolRegistry,
@@ -41,14 +41,14 @@ import {
41
41
  spawnAgentContext,
42
42
  truncateOutput,
43
43
  undoStack
44
- } from "./chunk-CU5ZSHGU.js";
44
+ } from "./chunk-JHQMDIV4.js";
45
45
  import "./chunk-2ZD3YTVM.js";
46
46
  import "./chunk-4BKXL7SM.js";
47
47
  import "./chunk-NHNWUBXB.js";
48
48
  import "./chunk-CQQQFNND.js";
49
49
  import "./chunk-6VRJGH25.js";
50
50
  import "./chunk-PFYAAX2S.js";
51
- import "./chunk-IGHC7D62.js";
51
+ import "./chunk-6GVBLOS3.js";
52
52
  import {
53
53
  AGENTIC_BEHAVIOR_GUIDELINE,
54
54
  AUTHOR,
@@ -67,7 +67,7 @@ import {
67
67
  SKILLS_DIR_NAME,
68
68
  VERSION,
69
69
  buildUserIdentityPrompt
70
- } from "./chunk-2ERX5Q55.js";
70
+ } from "./chunk-KBVJGOD6.js";
71
71
  import {
72
72
  AuthManager
73
73
  } from "./chunk-BYNY5JPB.js";
@@ -1812,6 +1812,17 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1812
1812
  break;
1813
1813
  }
1814
1814
  const sub = args[0]?.toLowerCase();
1815
+ const resolve3 = (ref) => {
1816
+ const r = session.resolveBranchRef(ref);
1817
+ if (r.ok) return r.id;
1818
+ if (r.reason === "ambiguous") {
1819
+ const list = r.matches.map((m) => `${m.id} (${m.title})`).join(", ");
1820
+ this.send({ type: "error", message: `Ambiguous branch reference "${ref}". Matches: ${list}. Use the 6-char id.` });
1821
+ } else {
1822
+ this.send({ type: "error", message: `Branch "${ref}" not found. Use /branch list to see all branches.` });
1823
+ }
1824
+ return null;
1825
+ };
1815
1826
  if (!sub || sub === "list") {
1816
1827
  const branches = session.listBranches();
1817
1828
  const lines = [`\u{1F33F} Branches (${branches.length}):`, ""];
@@ -1822,7 +1833,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1822
1833
  lines.push(` ${marker}${b.id.padEnd(10)} ${b.title.padEnd(20)} (${count} msgs)${parent}`);
1823
1834
  }
1824
1835
  lines.push("");
1825
- lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>");
1836
+ lines.push("Usage: /branch new <msgIndex> [title] | switch <id|title> | delete <id|title> | rename <id|title> <new title> | diff <id|title> | cherry-pick <id|title> <msgIndex>");
1826
1837
  this.send({ type: "info", message: lines.join("\n") });
1827
1838
  break;
1828
1839
  }
@@ -1850,11 +1861,13 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1850
1861
  break;
1851
1862
  }
1852
1863
  if (sub === "switch") {
1853
- const id = args[1];
1854
- if (!id) {
1855
- this.send({ type: "error", message: "Usage: /branch switch <id>" });
1864
+ const ref = args[1];
1865
+ if (!ref) {
1866
+ this.send({ type: "error", message: "Usage: /branch switch <id|title>" });
1856
1867
  break;
1857
1868
  }
1869
+ const id = resolve3(ref);
1870
+ if (!id) break;
1858
1871
  const ok = session.switchBranch(id);
1859
1872
  if (ok) {
1860
1873
  await this.sessions.save();
@@ -1863,33 +1876,37 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1863
1876
  this.sendSessionMessages();
1864
1877
  this.sendStatus();
1865
1878
  } else {
1866
- this.send({ type: "error", message: `Cannot switch to "${id}" (not found or already active).` });
1879
+ this.send({ type: "error", message: `Cannot switch to "${id}" (already active).` });
1867
1880
  }
1868
1881
  break;
1869
1882
  }
1870
1883
  if (sub === "delete") {
1871
- const id = args[1];
1872
- if (!id) {
1873
- this.send({ type: "error", message: "Usage: /branch delete <id>" });
1884
+ const ref = args[1];
1885
+ if (!ref) {
1886
+ this.send({ type: "error", message: "Usage: /branch delete <id|title>" });
1874
1887
  break;
1875
1888
  }
1889
+ const id = resolve3(ref);
1890
+ if (!id) break;
1876
1891
  const ok = session.deleteBranch(id);
1877
1892
  if (ok) {
1878
1893
  await this.sessions.save();
1879
1894
  this.send({ type: "info", message: `\u2713 Deleted branch "${id}"` });
1880
1895
  this.sendStatus();
1881
1896
  } else {
1882
- this.send({ type: "error", message: `Cannot delete "${id}" (not found, active, or last remaining branch).` });
1897
+ this.send({ type: "error", message: `Cannot delete "${id}" (active, or last remaining branch).` });
1883
1898
  }
1884
1899
  break;
1885
1900
  }
1886
1901
  if (sub === "rename") {
1887
- const id = args[1];
1902
+ const ref = args[1];
1888
1903
  const title = args.slice(2).join(" ").trim();
1889
- if (!id || !title) {
1890
- this.send({ type: "error", message: "Usage: /branch rename <id> <new title>" });
1904
+ if (!ref || !title) {
1905
+ this.send({ type: "error", message: "Usage: /branch rename <id|title> <new title>" });
1891
1906
  break;
1892
1907
  }
1908
+ const id = resolve3(ref);
1909
+ if (!id) break;
1893
1910
  const ok = session.renameBranch(id, title);
1894
1911
  if (ok) {
1895
1912
  await this.sessions.save();
@@ -1901,11 +1918,13 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1901
1918
  break;
1902
1919
  }
1903
1920
  if (sub === "diff") {
1904
- const id = args[1];
1905
- if (!id) {
1906
- this.send({ type: "error", message: "Usage: /branch diff <id>" });
1921
+ const ref = args[1];
1922
+ if (!ref) {
1923
+ this.send({ type: "error", message: "Usage: /branch diff <id|title>" });
1907
1924
  break;
1908
1925
  }
1926
+ const id = resolve3(ref);
1927
+ if (!id) break;
1909
1928
  const d = session.diffBranches(id);
1910
1929
  if (!d) {
1911
1930
  this.send({ type: "error", message: `Branch "${id}" not found.` });
@@ -1940,12 +1959,14 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1940
1959
  break;
1941
1960
  }
1942
1961
  if (sub === "cherry-pick") {
1943
- const id = args[1];
1962
+ const ref = args[1];
1944
1963
  const idxStr = args[2];
1945
- if (!id || !idxStr) {
1946
- this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id> <msg-index>" });
1964
+ if (!ref || !idxStr) {
1965
+ this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id|title> <msg-index>" });
1947
1966
  break;
1948
1967
  }
1968
+ const id = resolve3(ref);
1969
+ if (!id) break;
1949
1970
  const idx = parseInt(idxStr, 10);
1950
1971
  if (Number.isNaN(idx)) {
1951
1972
  this.send({ type: "error", message: `Invalid message index: "${idxStr}"` });
@@ -1953,7 +1974,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1953
1974
  }
1954
1975
  const picked = session.cherryPickMessage(id, idx);
1955
1976
  if (!picked) {
1956
- this.send({ type: "error", message: `Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.` });
1977
+ this.send({ type: "error", message: `Cherry-pick failed \u2014 index ${idx} out of range for branch "${id}".` });
1957
1978
  break;
1958
1979
  }
1959
1980
  await this.sessions.save();
@@ -2208,7 +2229,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2208
2229
  case "test": {
2209
2230
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2210
2231
  try {
2211
- const { executeTests } = await import("./run-tests-7N2WILA3.js");
2232
+ const { executeTests } = await import("./run-tests-KDJI6VFZ.js");
2212
2233
  const argStr = args.join(" ").trim();
2213
2234
  let testArgs = {};
2214
2235
  if (argStr) {
@@ -4,17 +4,17 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-CU5ZSHGU.js";
7
+ } from "./chunk-JHQMDIV4.js";
8
8
  import "./chunk-2ZD3YTVM.js";
9
9
  import "./chunk-4BKXL7SM.js";
10
10
  import "./chunk-NHNWUBXB.js";
11
11
  import "./chunk-CQQQFNND.js";
12
12
  import "./chunk-6VRJGH25.js";
13
13
  import "./chunk-PFYAAX2S.js";
14
- import "./chunk-IGHC7D62.js";
14
+ import "./chunk-6GVBLOS3.js";
15
15
  import {
16
16
  SUBAGENT_ALLOWED_TOOLS
17
- } from "./chunk-2ERX5Q55.js";
17
+ } from "./chunk-KBVJGOD6.js";
18
18
 
19
19
  // src/hub/task-orchestrator.ts
20
20
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.80",
3
+ "version": "0.4.82",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",