jinzd-ai-cli 0.4.79 → 0.4.80

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,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-34OKHMTQ.js";
4
+ } from "./chunk-C5QENJ4P.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-RRL5572W.js";
6
+ import "./chunk-2ERX5Q55.js";
7
7
 
8
8
  // src/cli/batch.ts
9
9
  import Anthropic from "@anthropic-ai/sdk";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.79";
4
+ var VERSION = "0.4.80";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -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.79";
9
+ var VERSION = "0.4.80";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-MOE2D3NR.js";
5
+ } from "./chunk-CU5ZSHGU.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-RRL5572W.js";
21
+ } from "./chunk-2ERX5Q55.js";
22
22
 
23
23
  // src/providers/claude.ts
24
24
  import Anthropic from "@anthropic-ai/sdk";
@@ -2135,6 +2135,12 @@ function getContentText(content) {
2135
2135
  function makeBranchId() {
2136
2136
  return Math.random().toString(16).slice(2, 8);
2137
2137
  }
2138
+ function messagesEqual(a, b) {
2139
+ if (a.role !== b.role) return false;
2140
+ if (JSON.stringify(a.content) !== JSON.stringify(b.content)) return false;
2141
+ if (JSON.stringify(a.toolCalls ?? null) !== JSON.stringify(b.toolCalls ?? null)) return false;
2142
+ return true;
2143
+ }
2138
2144
  var Session = class _Session {
2139
2145
  id;
2140
2146
  provider;
@@ -2376,6 +2382,51 @@ var Session = class _Session {
2376
2382
  const m = this._inactiveBranchMessages.get(id);
2377
2383
  return m ? m.slice() : null;
2378
2384
  }
2385
+ // ── B3 Branch diff + cherry-pick (v0.4.80+) ────────────────────────
2386
+ /**
2387
+ * Compare messages between two branches. Finds the longest common prefix
2388
+ * by message equality (role + content + toolCalls shape) — NOT by fork
2389
+ * point metadata, so user-edited histories still diff cleanly.
2390
+ *
2391
+ * @param sourceId Branch to compare
2392
+ * @param targetId Branch to compare against (defaults to active branch)
2393
+ * @returns BranchDiff, or null if either branch id is unknown
2394
+ */
2395
+ diffBranches(sourceId, targetId) {
2396
+ const tgt = targetId ?? this.activeBranchId;
2397
+ const src = this.getBranchMessages(sourceId);
2398
+ const dst = this.getBranchMessages(tgt);
2399
+ if (!src || !dst) return null;
2400
+ let i = 0;
2401
+ while (i < src.length && i < dst.length && messagesEqual(src[i], dst[i])) i++;
2402
+ return {
2403
+ sourceId,
2404
+ targetId: tgt,
2405
+ commonPrefix: i,
2406
+ sourceOnly: src.slice(i),
2407
+ targetOnly: dst.slice(i)
2408
+ };
2409
+ }
2410
+ /**
2411
+ * Copy a single message from another branch into the active branch
2412
+ * (appended at the end). Timestamp is reset to now so it's clear when
2413
+ * the cherry-pick happened.
2414
+ *
2415
+ * @returns the cloned message, or null if sourceId / msgIndex is invalid
2416
+ */
2417
+ cherryPickMessage(sourceId, msgIndex) {
2418
+ const src = this.getBranchMessages(sourceId);
2419
+ if (!src) return null;
2420
+ if (msgIndex < 0 || msgIndex >= src.length) return null;
2421
+ const orig = src[msgIndex];
2422
+ const copy = {
2423
+ ...orig,
2424
+ timestamp: /* @__PURE__ */ new Date()
2425
+ };
2426
+ this.messages.push(copy);
2427
+ this.updated = /* @__PURE__ */ new Date();
2428
+ return copy;
2429
+ }
2379
2430
  getMeta() {
2380
2431
  return {
2381
2432
  id: this.id,
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-RRL5572W.js";
11
+ } from "./chunk-2ERX5Q55.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -12,8 +12,42 @@ import {
12
12
  } from "./chunk-PFYAAX2S.js";
13
13
 
14
14
  // src/symbols/semantic.ts
15
- function buildEmbeddingText(s) {
16
- const parts = [s.kind, s.name];
15
+ function pathTokens(absFile, root) {
16
+ if (!absFile) return "";
17
+ const norm = absFile.replace(/\\/g, "/");
18
+ let rel = norm;
19
+ if (root) {
20
+ const rootNorm = root.replace(/\\/g, "/").replace(/\/$/, "") + "/";
21
+ if (norm.toLowerCase().startsWith(rootNorm.toLowerCase())) {
22
+ rel = norm.slice(rootNorm.length);
23
+ }
24
+ }
25
+ const noExt = rel.replace(/\.[^./]+$/, "");
26
+ const parts = noExt.split(/[/.\-_]/).filter((t) => {
27
+ if (!t) return false;
28
+ if (t.length < 2) return false;
29
+ if (/^\d+$/.test(t)) return false;
30
+ if (t === "src" || t === "lib" || t === "app" || t === "dist") return false;
31
+ return true;
32
+ });
33
+ const seen = /* @__PURE__ */ new Set();
34
+ const out = [];
35
+ for (const t of parts) {
36
+ const lower = t.toLowerCase();
37
+ if (seen.has(lower)) continue;
38
+ seen.add(lower);
39
+ out.push(t);
40
+ if (out.length >= 6) break;
41
+ }
42
+ return out.join(" ");
43
+ }
44
+ function buildEmbeddingText(s, root) {
45
+ const parts = [];
46
+ if (root) {
47
+ const tokens = pathTokens(s.location.file, root);
48
+ if (tokens) parts.push(tokens);
49
+ }
50
+ parts.push(s.kind, s.name);
17
51
  if (s.container) parts.push(`in ${s.container}`);
18
52
  if (s.signature) parts.push(s.signature);
19
53
  if (s.doc) parts.push(s.doc);
@@ -42,7 +76,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
42
76
  const batch = [];
43
77
  for (let j = i; j < end; j++) {
44
78
  symbolIdx[j] = j;
45
- batch.push(buildEmbeddingText(index.symbols[j]));
79
+ batch.push(buildEmbeddingText(index.symbols[j], index.root));
46
80
  }
47
81
  const batchStart = Date.now();
48
82
  const rows = await embed(batch);
@@ -77,6 +111,7 @@ function hasSemanticIndex(root) {
77
111
  }
78
112
 
79
113
  export {
114
+ pathTokens,
80
115
  buildEmbeddingText,
81
116
  rebuildSemanticIndex,
82
117
  semanticSearch,
@@ -13,13 +13,13 @@ import {
13
13
  import {
14
14
  hasSemanticIndex,
15
15
  semanticSearch
16
- } from "./chunk-HPDDAXFY.js";
16
+ } from "./chunk-CQQQFNND.js";
17
17
  import {
18
18
  loadIndex
19
19
  } from "./chunk-6VRJGH25.js";
20
20
  import {
21
21
  runTestsTool
22
- } from "./chunk-ZT7RZYWE.js";
22
+ } from "./chunk-IGHC7D62.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-RRL5572W.js";
30
+ } from "./chunk-2ERX5Q55.js";
31
31
 
32
32
  // src/tools/builtin/bash.ts
33
33
  import { execSync } from "child_process";
@@ -11,8 +11,42 @@ import {
11
11
  } from "./chunk-XMA222FQ.js";
12
12
 
13
13
  // src/symbols/semantic.ts
14
- function buildEmbeddingText(s) {
15
- const parts = [s.kind, s.name];
14
+ function pathTokens(absFile, root) {
15
+ if (!absFile) return "";
16
+ const norm = absFile.replace(/\\/g, "/");
17
+ let rel = norm;
18
+ if (root) {
19
+ const rootNorm = root.replace(/\\/g, "/").replace(/\/$/, "") + "/";
20
+ if (norm.toLowerCase().startsWith(rootNorm.toLowerCase())) {
21
+ rel = norm.slice(rootNorm.length);
22
+ }
23
+ }
24
+ const noExt = rel.replace(/\.[^./]+$/, "");
25
+ const parts = noExt.split(/[/.\-_]/).filter((t) => {
26
+ if (!t) return false;
27
+ if (t.length < 2) return false;
28
+ if (/^\d+$/.test(t)) return false;
29
+ if (t === "src" || t === "lib" || t === "app" || t === "dist") return false;
30
+ return true;
31
+ });
32
+ const seen = /* @__PURE__ */ new Set();
33
+ const out = [];
34
+ for (const t of parts) {
35
+ const lower = t.toLowerCase();
36
+ if (seen.has(lower)) continue;
37
+ seen.add(lower);
38
+ out.push(t);
39
+ if (out.length >= 6) break;
40
+ }
41
+ return out.join(" ");
42
+ }
43
+ function buildEmbeddingText(s, root) {
44
+ const parts = [];
45
+ if (root) {
46
+ const tokens = pathTokens(s.location.file, root);
47
+ if (tokens) parts.push(tokens);
48
+ }
49
+ parts.push(s.kind, s.name);
16
50
  if (s.container) parts.push(`in ${s.container}`);
17
51
  if (s.signature) parts.push(s.signature);
18
52
  if (s.doc) parts.push(s.doc);
@@ -41,7 +75,7 @@ async function rebuildSemanticIndex(root, opts = {}) {
41
75
  const batch = [];
42
76
  for (let j = i; j < end; j++) {
43
77
  symbolIdx[j] = j;
44
- batch.push(buildEmbeddingText(index.symbols[j]));
78
+ batch.push(buildEmbeddingText(index.symbols[j], index.root));
45
79
  }
46
80
  const batchStart = Date.now();
47
81
  const rows = await embed(batch);
@@ -76,6 +110,7 @@ function hasSemanticIndex(root) {
76
110
  }
77
111
 
78
112
  export {
113
+ pathTokens,
79
114
  buildEmbeddingText,
80
115
  rebuildSemanticIndex,
81
116
  semanticSearch,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-RRL5572W.js";
4
+ } from "./chunk-2ERX5Q55.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync } from "child_process";
@@ -36,11 +36,11 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-FU6WMYXS.js";
39
+ } from "./chunk-63R2GIRK.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
43
- } from "./chunk-UTCC3UMT.js";
43
+ } from "./chunk-GTKJUEBS.js";
44
44
  import {
45
45
  loadIndex
46
46
  } from "./chunk-BJAT4GNC.js";
@@ -2516,6 +2516,12 @@ function getContentText(content) {
2516
2516
  function makeBranchId() {
2517
2517
  return Math.random().toString(16).slice(2, 8);
2518
2518
  }
2519
+ function messagesEqual(a, b) {
2520
+ if (a.role !== b.role) return false;
2521
+ if (JSON.stringify(a.content) !== JSON.stringify(b.content)) return false;
2522
+ if (JSON.stringify(a.toolCalls ?? null) !== JSON.stringify(b.toolCalls ?? null)) return false;
2523
+ return true;
2524
+ }
2519
2525
  var Session = class _Session {
2520
2526
  id;
2521
2527
  provider;
@@ -2757,6 +2763,51 @@ var Session = class _Session {
2757
2763
  const m = this._inactiveBranchMessages.get(id);
2758
2764
  return m ? m.slice() : null;
2759
2765
  }
2766
+ // ── B3 Branch diff + cherry-pick (v0.4.80+) ────────────────────────
2767
+ /**
2768
+ * Compare messages between two branches. Finds the longest common prefix
2769
+ * by message equality (role + content + toolCalls shape) — NOT by fork
2770
+ * point metadata, so user-edited histories still diff cleanly.
2771
+ *
2772
+ * @param sourceId Branch to compare
2773
+ * @param targetId Branch to compare against (defaults to active branch)
2774
+ * @returns BranchDiff, or null if either branch id is unknown
2775
+ */
2776
+ diffBranches(sourceId, targetId) {
2777
+ const tgt = targetId ?? this.activeBranchId;
2778
+ const src = this.getBranchMessages(sourceId);
2779
+ const dst = this.getBranchMessages(tgt);
2780
+ if (!src || !dst) return null;
2781
+ let i = 0;
2782
+ while (i < src.length && i < dst.length && messagesEqual(src[i], dst[i])) i++;
2783
+ return {
2784
+ sourceId,
2785
+ targetId: tgt,
2786
+ commonPrefix: i,
2787
+ sourceOnly: src.slice(i),
2788
+ targetOnly: dst.slice(i)
2789
+ };
2790
+ }
2791
+ /**
2792
+ * Copy a single message from another branch into the active branch
2793
+ * (appended at the end). Timestamp is reset to now so it's clear when
2794
+ * the cherry-pick happened.
2795
+ *
2796
+ * @returns the cloned message, or null if sourceId / msgIndex is invalid
2797
+ */
2798
+ cherryPickMessage(sourceId, msgIndex) {
2799
+ const src = this.getBranchMessages(sourceId);
2800
+ if (!src) return null;
2801
+ if (msgIndex < 0 || msgIndex >= src.length) return null;
2802
+ const orig = src[msgIndex];
2803
+ const copy = {
2804
+ ...orig,
2805
+ timestamp: /* @__PURE__ */ new Date()
2806
+ };
2807
+ this.messages.push(copy);
2808
+ this.updated = /* @__PURE__ */ new Date();
2809
+ return copy;
2810
+ }
2760
2811
  getMeta() {
2761
2812
  return {
2762
2813
  id: this.id,
@@ -10241,7 +10292,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
10241
10292
  " /diff [--stats] \u2014 Show file modifications in this session",
10242
10293
  " /checkpoint [save|restore|delete] <name> \u2014 Session checkpoints",
10243
10294
  " /fork [checkpoint] \u2014 Fork session from checkpoint or current",
10244
- " /branch [list|new|switch|delete|rename] \u2014 Manage conversation branches",
10295
+ " /branch [list|new|switch|delete|rename|diff|cherry-pick] \u2014 Manage conversation branches (fork tree, cross-branch picks)",
10245
10296
  " /index [status|rebuild|clear|semantic-rebuild|semantic-clear] \u2014 Symbol + semantic index (find_symbol / search_code)",
10246
10297
  " /review [--staged] \u2014 AI code review from git diff",
10247
10298
  " /security-review \u2014 Security vulnerability scan on git diff",
@@ -10581,7 +10632,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10581
10632
  lines.push(` ${marker}${b.id.padEnd(10)} ${b.title.padEnd(20)} (${count} msgs)${parent}`);
10582
10633
  }
10583
10634
  lines.push("");
10584
- lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title>");
10635
+ lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>");
10585
10636
  this.send({ type: "info", message: lines.join("\n") });
10586
10637
  break;
10587
10638
  }
@@ -10659,7 +10710,70 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10659
10710
  }
10660
10711
  break;
10661
10712
  }
10662
- this.send({ type: "error", message: `Unknown subcommand: ${sub}. Use list/new/switch/delete/rename.` });
10713
+ if (sub === "diff") {
10714
+ const id = args[1];
10715
+ if (!id) {
10716
+ this.send({ type: "error", message: "Usage: /branch diff <id>" });
10717
+ break;
10718
+ }
10719
+ const d = session.diffBranches(id);
10720
+ if (!d) {
10721
+ this.send({ type: "error", message: `Branch "${id}" not found.` });
10722
+ break;
10723
+ }
10724
+ const preview = (m) => {
10725
+ const text = typeof m.content === "string" ? m.content : "[complex content]";
10726
+ return text.replace(/\s+/g, " ").trim().slice(0, 70);
10727
+ };
10728
+ const lines = [
10729
+ `\u{1F500} diff ${id} (source) \u2194 ${session.activeBranchId} (active)`,
10730
+ `Common prefix: ${d.commonPrefix} message(s)`
10731
+ ];
10732
+ if (d.sourceOnly.length === 0 && d.targetOnly.length === 0) {
10733
+ lines.push("Branches are identical.");
10734
+ } else {
10735
+ if (d.sourceOnly.length > 0) {
10736
+ lines.push("", `Only in ${id} (${d.sourceOnly.length}):`);
10737
+ d.sourceOnly.forEach(
10738
+ (m, i) => lines.push(` [${d.commonPrefix + i}] ${m.role}: ${preview(m)}`)
10739
+ );
10740
+ }
10741
+ if (d.targetOnly.length > 0) {
10742
+ lines.push("", `Only in ${session.activeBranchId} (active) (${d.targetOnly.length}):`);
10743
+ d.targetOnly.forEach(
10744
+ (m, i) => lines.push(` [${d.commonPrefix + i}] ${m.role}: ${preview(m)}`)
10745
+ );
10746
+ }
10747
+ lines.push("", `Use /branch cherry-pick ${id} <index> to copy a message into the active branch.`);
10748
+ }
10749
+ this.send({ type: "info", message: lines.join("\n") });
10750
+ break;
10751
+ }
10752
+ if (sub === "cherry-pick") {
10753
+ const id = args[1];
10754
+ const idxStr = args[2];
10755
+ if (!id || !idxStr) {
10756
+ this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id> <msg-index>" });
10757
+ break;
10758
+ }
10759
+ const idx = parseInt(idxStr, 10);
10760
+ if (Number.isNaN(idx)) {
10761
+ this.send({ type: "error", message: `Invalid message index: "${idxStr}"` });
10762
+ break;
10763
+ }
10764
+ const picked = session.cherryPickMessage(id, idx);
10765
+ if (!picked) {
10766
+ this.send({ type: "error", message: `Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.` });
10767
+ break;
10768
+ }
10769
+ await this.sessions.save();
10770
+ const preview = typeof picked.content === "string" ? picked.content.replace(/\s+/g, " ").trim().slice(0, 60) : "[complex content]";
10771
+ this.send({ type: "info", message: `\u2713 Cherry-picked ${picked.role} message from "${id}"[${idx}] \u2192 active branch (${session.messages.length} msgs): ${preview}` });
10772
+ this.sendSessionMessages();
10773
+ this.sendStatus();
10774
+ break;
10775
+ }
10776
+ this.send({ type: "error", message: `Unknown subcommand: ${sub}. Use list/new/switch/delete/rename/diff/cherry-pick.` });
10663
10777
  break;
10664
10778
  }
10665
10779
  // ── /index ──────────────────────────────────────────────────────
@@ -10717,7 +10831,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10717
10831
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
10718
10832
  });
10719
10833
  try {
10720
- const { rebuildSemanticIndex } = await import("./semantic-MD7HYPWZ.js");
10834
+ const { rebuildSemanticIndex } = await import("./semantic-MYAXLDCZ.js");
10721
10835
  const stats = await rebuildSemanticIndex(root);
10722
10836
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
10723
10837
  this.send({
@@ -10904,7 +11018,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10904
11018
  case "test": {
10905
11019
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
10906
11020
  try {
10907
- const { executeTests } = await import("./run-tests-O2CYJF3Y.js");
11021
+ const { executeTests } = await import("./run-tests-4PZKLURD.js");
10908
11022
  const argStr = args.join(" ").trim();
10909
11023
  let testArgs = {};
10910
11024
  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-MWZ7NMES.js");
388
+ const { TaskOrchestrator } = await import("./task-orchestrator-RVKRP6LI.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-EGJAMKAZ.js";
33
+ } from "./chunk-6HZTXVTF.js";
34
34
  import {
35
35
  ConfigManager
36
- } from "./chunk-34OKHMTQ.js";
36
+ } from "./chunk-C5QENJ4P.js";
37
37
  import {
38
38
  ToolExecutor,
39
39
  ToolRegistry,
@@ -49,16 +49,16 @@ import {
49
49
  spawnAgentContext,
50
50
  theme,
51
51
  undoStack
52
- } from "./chunk-MOE2D3NR.js";
52
+ } from "./chunk-CU5ZSHGU.js";
53
53
  import "./chunk-2ZD3YTVM.js";
54
54
  import {
55
55
  fileCheckpoints
56
56
  } from "./chunk-4BKXL7SM.js";
57
57
  import "./chunk-NHNWUBXB.js";
58
- import "./chunk-HPDDAXFY.js";
58
+ import "./chunk-CQQQFNND.js";
59
59
  import "./chunk-6VRJGH25.js";
60
60
  import "./chunk-PFYAAX2S.js";
61
- import "./chunk-ZT7RZYWE.js";
61
+ import "./chunk-IGHC7D62.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-RRL5572W.js";
83
+ } from "./chunk-2ERX5Q55.js";
84
84
 
85
85
  // src/index.ts
86
86
  import { program } from "commander";
@@ -978,7 +978,7 @@ function createDefaultCommands() {
978
978
  " /bug [--copy] - Generate bug report template (--copy to clipboard)",
979
979
  " /diff [--stats] - Show all file modifications in this session",
980
980
  " /fork [checkpoint] - Fork session from checkpoint or current position",
981
- " /branch [list|new|switch|delete|rename] - Manage conversation branches (fork tree)",
981
+ " /branch [list|new|switch|delete|rename|diff|cherry-pick] - Manage conversation branches (fork tree, cross-branch picks)",
982
982
  " /index [status|rebuild|clear|semantic-rebuild|semantic-clear] - Symbol + semantic index (find_symbol / search_code)",
983
983
  " /yolo [on|off] - Toggle session auto-approve (skip confirmations)",
984
984
  " /exit - Exit"
@@ -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>]",
2257
+ usage: "/branch [list | new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>]",
2258
2258
  async execute(args, ctx) {
2259
2259
  const session = ctx.sessions.current;
2260
2260
  if (!session) {
@@ -2280,6 +2280,8 @@ ${hint}` : "")
2280
2280
  console.log(theme.dim(" /branch switch <id> \u2014 switch active branch"));
2281
2281
  console.log(theme.dim(" /branch delete <id> \u2014 delete inactive branch"));
2282
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"));
2283
2285
  console.log();
2284
2286
  return;
2285
2287
  }
@@ -2363,7 +2365,74 @@ ${hint}` : "")
2363
2365
  }
2364
2366
  return;
2365
2367
  }
2366
- ctx.renderer.renderError(`Unknown subcommand: ${sub}. Use list/new/switch/delete/rename.`);
2368
+ 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");
2372
+ return;
2373
+ }
2374
+ const d = session.diffBranches(id);
2375
+ if (!d) {
2376
+ ctx.renderer.renderError(`Branch "${id}" not found.`);
2377
+ return;
2378
+ }
2379
+ const active = session.activeBranchId;
2380
+ console.log(`
2381
+ ${theme.info("diff")} ${id} (source) \u2194 ${active} (active)`);
2382
+ console.log(theme.dim(` Common prefix: ${d.commonPrefix} message(s)`));
2383
+ if (d.sourceOnly.length === 0 && d.targetOnly.length === 0) {
2384
+ console.log(theme.success(" Branches are identical.\n"));
2385
+ return;
2386
+ }
2387
+ const preview = (m) => {
2388
+ const text = typeof m.content === "string" ? m.content : "[complex content]";
2389
+ return text.replace(/\s+/g, " ").trim().slice(0, 70);
2390
+ };
2391
+ if (d.sourceOnly.length > 0) {
2392
+ console.log(theme.dim(`
2393
+ Only in ${id} (${d.sourceOnly.length}):`));
2394
+ d.sourceOnly.forEach((m, i) => {
2395
+ console.log(` ${theme.dim(`[${d.commonPrefix + i}]`)} ${m.role}: ${preview(m)}`);
2396
+ });
2397
+ }
2398
+ if (d.targetOnly.length > 0) {
2399
+ console.log(theme.dim(`
2400
+ Only in ${active} (active) (${d.targetOnly.length}):`));
2401
+ d.targetOnly.forEach((m, i) => {
2402
+ console.log(` ${theme.dim(`[${d.commonPrefix + i}]`)} ${m.role}: ${preview(m)}`);
2403
+ });
2404
+ }
2405
+ console.log(theme.dim(`
2406
+ Use /branch cherry-pick ${id} <index> to copy a message into the active branch.
2407
+ `));
2408
+ return;
2409
+ }
2410
+ if (sub === "cherry-pick") {
2411
+ const id = args[1];
2412
+ const idxStr = args[2];
2413
+ if (!id || !idxStr) {
2414
+ ctx.renderer.renderError("Usage: /branch cherry-pick <source-id> <msg-index>");
2415
+ return;
2416
+ }
2417
+ const idx = parseInt(idxStr, 10);
2418
+ if (Number.isNaN(idx)) {
2419
+ ctx.renderer.renderError(`Invalid message index: "${idxStr}"`);
2420
+ return;
2421
+ }
2422
+ const picked = session.cherryPickMessage(id, idx);
2423
+ if (!picked) {
2424
+ ctx.renderer.renderError(`Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.`);
2425
+ return;
2426
+ }
2427
+ await ctx.sessions.save();
2428
+ const preview = typeof picked.content === "string" ? picked.content.replace(/\s+/g, " ").trim().slice(0, 60) : "[complex content]";
2429
+ console.log(theme.success(`
2430
+ \u2713 Cherry-picked ${picked.role} message from "${id}"[${idx}] \u2192 active branch (now ${session.messages.length} msgs)`));
2431
+ console.log(theme.dim(` ${preview}
2432
+ `));
2433
+ return;
2434
+ }
2435
+ ctx.renderer.renderError(`Unknown subcommand: ${sub}. Use list/new/switch/delete/rename/diff/cherry-pick.`);
2367
2436
  }
2368
2437
  },
2369
2438
  // ── /index ────────────────────────────────────────────────────
@@ -2435,7 +2504,7 @@ ${hint}` : "")
2435
2504
  }
2436
2505
  console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
2437
2506
  console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
2438
- const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
2507
+ const { rebuildSemanticIndex } = await import("./semantic-ICJ536BG.js");
2439
2508
  try {
2440
2509
  const stats = await rebuildSemanticIndex(root, {
2441
2510
  onProgress: (done, total) => {
@@ -2501,7 +2570,7 @@ ${hint}` : "")
2501
2570
  usage: "/test [command|filter]",
2502
2571
  async execute(args, ctx) {
2503
2572
  try {
2504
- const { executeTests } = await import("./run-tests-WPSQCOIG.js");
2573
+ const { executeTests } = await import("./run-tests-7N2WILA3.js");
2505
2574
  const argStr = args.join(" ").trim();
2506
2575
  let testArgs = {};
2507
2576
  if (argStr) {
@@ -6394,7 +6463,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6394
6463
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6395
6464
  process.exit(1);
6396
6465
  }
6397
- const { startWebServer } = await import("./server-MHY4E453.js");
6466
+ const { startWebServer } = await import("./server-HJ64OR7A.js");
6398
6467
  await startWebServer({ port, host: options.host });
6399
6468
  });
6400
6469
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6517,7 +6586,7 @@ program.command("sessions").description("List recent conversation sessions").act
6517
6586
  });
6518
6587
  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) => {
6519
6588
  try {
6520
- const batch = await import("./batch-SMUURFMA.js");
6589
+ const batch = await import("./batch-OUBKAHXG.js");
6521
6590
  switch (action) {
6522
6591
  case "submit":
6523
6592
  if (!arg) {
@@ -6677,7 +6746,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6677
6746
  }),
6678
6747
  config.get("customProviders")
6679
6748
  );
6680
- const { startHub } = await import("./hub-BDEFIXFF.js");
6749
+ const { startHub } = await import("./hub-SFJU4KJK.js");
6681
6750
  await startHub(
6682
6751
  {
6683
6752
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-FU6WMYXS.js";
4
+ } from "./chunk-63R2GIRK.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-ZT7RZYWE.js";
6
- import "./chunk-RRL5572W.js";
5
+ } from "./chunk-IGHC7D62.js";
6
+ import "./chunk-2ERX5Q55.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -2,14 +2,16 @@
2
2
  import {
3
3
  buildEmbeddingText,
4
4
  hasSemanticIndex,
5
+ pathTokens,
5
6
  rebuildSemanticIndex,
6
7
  semanticSearch
7
- } from "./chunk-HPDDAXFY.js";
8
+ } from "./chunk-CQQQFNND.js";
8
9
  import "./chunk-6VRJGH25.js";
9
10
  import "./chunk-PFYAAX2S.js";
10
11
  export {
11
12
  buildEmbeddingText,
12
13
  hasSemanticIndex,
14
+ pathTokens,
13
15
  rebuildSemanticIndex,
14
16
  semanticSearch
15
17
  };
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  buildEmbeddingText,
3
3
  hasSemanticIndex,
4
+ pathTokens,
4
5
  rebuildSemanticIndex,
5
6
  semanticSearch
6
- } from "./chunk-UTCC3UMT.js";
7
+ } from "./chunk-GTKJUEBS.js";
7
8
  import "./chunk-BJAT4GNC.js";
8
9
  import "./chunk-XMA222FQ.js";
9
10
  export {
10
11
  buildEmbeddingText,
11
12
  hasSemanticIndex,
13
+ pathTokens,
12
14
  rebuildSemanticIndex,
13
15
  semanticSearch
14
16
  };
@@ -20,10 +20,10 @@ import {
20
20
  persistToolRound,
21
21
  rebuildExtraMessages,
22
22
  setupProxy
23
- } from "./chunk-EGJAMKAZ.js";
23
+ } from "./chunk-6HZTXVTF.js";
24
24
  import {
25
25
  ConfigManager
26
- } from "./chunk-34OKHMTQ.js";
26
+ } from "./chunk-C5QENJ4P.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-MOE2D3NR.js";
44
+ } from "./chunk-CU5ZSHGU.js";
45
45
  import "./chunk-2ZD3YTVM.js";
46
46
  import "./chunk-4BKXL7SM.js";
47
47
  import "./chunk-NHNWUBXB.js";
48
- import "./chunk-HPDDAXFY.js";
48
+ import "./chunk-CQQQFNND.js";
49
49
  import "./chunk-6VRJGH25.js";
50
50
  import "./chunk-PFYAAX2S.js";
51
- import "./chunk-ZT7RZYWE.js";
51
+ import "./chunk-IGHC7D62.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-RRL5572W.js";
70
+ } from "./chunk-2ERX5Q55.js";
71
71
  import {
72
72
  AuthManager
73
73
  } from "./chunk-BYNY5JPB.js";
@@ -1482,7 +1482,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1482
1482
  " /diff [--stats] \u2014 Show file modifications in this session",
1483
1483
  " /checkpoint [save|restore|delete] <name> \u2014 Session checkpoints",
1484
1484
  " /fork [checkpoint] \u2014 Fork session from checkpoint or current",
1485
- " /branch [list|new|switch|delete|rename] \u2014 Manage conversation branches",
1485
+ " /branch [list|new|switch|delete|rename|diff|cherry-pick] \u2014 Manage conversation branches (fork tree, cross-branch picks)",
1486
1486
  " /index [status|rebuild|clear|semantic-rebuild|semantic-clear] \u2014 Symbol + semantic index (find_symbol / search_code)",
1487
1487
  " /review [--staged] \u2014 AI code review from git diff",
1488
1488
  " /security-review \u2014 Security vulnerability scan on git diff",
@@ -1822,7 +1822,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1822
1822
  lines.push(` ${marker}${b.id.padEnd(10)} ${b.title.padEnd(20)} (${count} msgs)${parent}`);
1823
1823
  }
1824
1824
  lines.push("");
1825
- lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title>");
1825
+ lines.push("Usage: /branch new <msgIndex> [title] | switch <id> | delete <id> | rename <id> <title> | diff <id> | cherry-pick <id> <msgIndex>");
1826
1826
  this.send({ type: "info", message: lines.join("\n") });
1827
1827
  break;
1828
1828
  }
@@ -1900,7 +1900,70 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1900
1900
  }
1901
1901
  break;
1902
1902
  }
1903
- this.send({ type: "error", message: `Unknown subcommand: ${sub}. Use list/new/switch/delete/rename.` });
1903
+ if (sub === "diff") {
1904
+ const id = args[1];
1905
+ if (!id) {
1906
+ this.send({ type: "error", message: "Usage: /branch diff <id>" });
1907
+ break;
1908
+ }
1909
+ const d = session.diffBranches(id);
1910
+ if (!d) {
1911
+ this.send({ type: "error", message: `Branch "${id}" not found.` });
1912
+ break;
1913
+ }
1914
+ const preview = (m) => {
1915
+ const text = typeof m.content === "string" ? m.content : "[complex content]";
1916
+ return text.replace(/\s+/g, " ").trim().slice(0, 70);
1917
+ };
1918
+ const lines = [
1919
+ `\u{1F500} diff ${id} (source) \u2194 ${session.activeBranchId} (active)`,
1920
+ `Common prefix: ${d.commonPrefix} message(s)`
1921
+ ];
1922
+ if (d.sourceOnly.length === 0 && d.targetOnly.length === 0) {
1923
+ lines.push("Branches are identical.");
1924
+ } else {
1925
+ if (d.sourceOnly.length > 0) {
1926
+ lines.push("", `Only in ${id} (${d.sourceOnly.length}):`);
1927
+ d.sourceOnly.forEach(
1928
+ (m, i) => lines.push(` [${d.commonPrefix + i}] ${m.role}: ${preview(m)}`)
1929
+ );
1930
+ }
1931
+ if (d.targetOnly.length > 0) {
1932
+ lines.push("", `Only in ${session.activeBranchId} (active) (${d.targetOnly.length}):`);
1933
+ d.targetOnly.forEach(
1934
+ (m, i) => lines.push(` [${d.commonPrefix + i}] ${m.role}: ${preview(m)}`)
1935
+ );
1936
+ }
1937
+ lines.push("", `Use /branch cherry-pick ${id} <index> to copy a message into the active branch.`);
1938
+ }
1939
+ this.send({ type: "info", message: lines.join("\n") });
1940
+ break;
1941
+ }
1942
+ if (sub === "cherry-pick") {
1943
+ const id = args[1];
1944
+ const idxStr = args[2];
1945
+ if (!id || !idxStr) {
1946
+ this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id> <msg-index>" });
1947
+ break;
1948
+ }
1949
+ const idx = parseInt(idxStr, 10);
1950
+ if (Number.isNaN(idx)) {
1951
+ this.send({ type: "error", message: `Invalid message index: "${idxStr}"` });
1952
+ break;
1953
+ }
1954
+ const picked = session.cherryPickMessage(id, idx);
1955
+ if (!picked) {
1956
+ this.send({ type: "error", message: `Cherry-pick failed \u2014 branch "${id}" not found, or index ${idx} out of range.` });
1957
+ break;
1958
+ }
1959
+ await this.sessions.save();
1960
+ const preview = typeof picked.content === "string" ? picked.content.replace(/\s+/g, " ").trim().slice(0, 60) : "[complex content]";
1961
+ this.send({ type: "info", message: `\u2713 Cherry-picked ${picked.role} message from "${id}"[${idx}] \u2192 active branch (${session.messages.length} msgs): ${preview}` });
1962
+ this.sendSessionMessages();
1963
+ this.sendStatus();
1964
+ break;
1965
+ }
1966
+ this.send({ type: "error", message: `Unknown subcommand: ${sub}. Use list/new/switch/delete/rename/diff/cherry-pick.` });
1904
1967
  break;
1905
1968
  }
1906
1969
  // ── /index ──────────────────────────────────────────────────────
@@ -1958,7 +2021,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1958
2021
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
1959
2022
  });
1960
2023
  try {
1961
- const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
2024
+ const { rebuildSemanticIndex } = await import("./semantic-ICJ536BG.js");
1962
2025
  const stats = await rebuildSemanticIndex(root);
1963
2026
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
1964
2027
  this.send({
@@ -2145,7 +2208,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2145
2208
  case "test": {
2146
2209
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2147
2210
  try {
2148
- const { executeTests } = await import("./run-tests-WPSQCOIG.js");
2211
+ const { executeTests } = await import("./run-tests-7N2WILA3.js");
2149
2212
  const argStr = args.join(" ").trim();
2150
2213
  let testArgs = {};
2151
2214
  if (argStr) {
@@ -4,17 +4,17 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-MOE2D3NR.js";
7
+ } from "./chunk-CU5ZSHGU.js";
8
8
  import "./chunk-2ZD3YTVM.js";
9
9
  import "./chunk-4BKXL7SM.js";
10
10
  import "./chunk-NHNWUBXB.js";
11
- import "./chunk-HPDDAXFY.js";
11
+ import "./chunk-CQQQFNND.js";
12
12
  import "./chunk-6VRJGH25.js";
13
13
  import "./chunk-PFYAAX2S.js";
14
- import "./chunk-ZT7RZYWE.js";
14
+ import "./chunk-IGHC7D62.js";
15
15
  import {
16
16
  SUBAGENT_ALLOWED_TOOLS
17
- } from "./chunk-RRL5572W.js";
17
+ } from "./chunk-2ERX5Q55.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.79",
3
+ "version": "0.4.80",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",