jinzd-ai-cli 0.4.78 → 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-FCVQ6OP5.js";
4
+ } from "./chunk-C5QENJ4P.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-HELGL4RH.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.78";
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.78";
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-EUE6VMGO.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-HELGL4RH.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-HELGL4RH.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-KHYHG2SO.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-HELGL4RH.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-HELGL4RH.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-TNAPORYF.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 ──────────────────────────────────────────────────────
@@ -10705,12 +10819,19 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10705
10819
  this.send({ type: "error", message: "No symbol index yet. Run /index rebuild first." });
10706
10820
  break;
10707
10821
  }
10822
+ if (idx.symbolCount === 0) {
10823
+ this.send({
10824
+ type: "error",
10825
+ message: "No code symbols to embed. Semantic search needs TS/TSX/JS/JSX/Python source files \u2014 this project appears to have none. Not downloading the embedding model."
10826
+ });
10827
+ break;
10828
+ }
10708
10829
  this.send({
10709
10830
  type: "info",
10710
10831
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
10711
10832
  });
10712
10833
  try {
10713
- const { rebuildSemanticIndex } = await import("./semantic-MD7HYPWZ.js");
10834
+ const { rebuildSemanticIndex } = await import("./semantic-MYAXLDCZ.js");
10714
10835
  const stats = await rebuildSemanticIndex(root);
10715
10836
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
10716
10837
  this.send({
@@ -10897,7 +11018,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
10897
11018
  case "test": {
10898
11019
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
10899
11020
  try {
10900
- const { executeTests } = await import("./run-tests-MONKXJBT.js");
11021
+ const { executeTests } = await import("./run-tests-4PZKLURD.js");
10901
11022
  const argStr = args.join(" ").trim();
10902
11023
  let testArgs = {};
10903
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-YAWEIZE7.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-GKDETNDH.js";
33
+ } from "./chunk-6HZTXVTF.js";
34
34
  import {
35
35
  ConfigManager
36
- } from "./chunk-FCVQ6OP5.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-EUE6VMGO.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-KHYHG2SO.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-HELGL4RH.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 ────────────────────────────────────────────────────
@@ -2427,9 +2496,15 @@ ${hint}` : "")
2427
2496
  ctx.renderer.renderError("No symbol index yet. Run `/index rebuild` first, then semantic-rebuild.");
2428
2497
  return;
2429
2498
  }
2499
+ if (idx.symbolCount === 0) {
2500
+ ctx.renderer.renderError(
2501
+ "No code symbols to embed. Semantic search needs source files in TS/TSX/JS/JSX/Python \u2014 this project appears to have none (check `/index rebuild` output). Nothing to do; not downloading the embedding model."
2502
+ );
2503
+ return;
2504
+ }
2430
2505
  console.log(theme.dim(` Building semantic index for ${idx.symbolCount} symbols\u2026`));
2431
2506
  console.log(theme.dim(" (First run downloads ~117 MB embedding model to ~/.aicli/models/)"));
2432
- const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
2507
+ const { rebuildSemanticIndex } = await import("./semantic-ICJ536BG.js");
2433
2508
  try {
2434
2509
  const stats = await rebuildSemanticIndex(root, {
2435
2510
  onProgress: (done, total) => {
@@ -2495,7 +2570,7 @@ ${hint}` : "")
2495
2570
  usage: "/test [command|filter]",
2496
2571
  async execute(args, ctx) {
2497
2572
  try {
2498
- const { executeTests } = await import("./run-tests-JBM4K5FO.js");
2573
+ const { executeTests } = await import("./run-tests-7N2WILA3.js");
2499
2574
  const argStr = args.join(" ").trim();
2500
2575
  let testArgs = {};
2501
2576
  if (argStr) {
@@ -6388,7 +6463,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6388
6463
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6389
6464
  process.exit(1);
6390
6465
  }
6391
- const { startWebServer } = await import("./server-GCE5V4SL.js");
6466
+ const { startWebServer } = await import("./server-HJ64OR7A.js");
6392
6467
  await startWebServer({ port, host: options.host });
6393
6468
  });
6394
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) => {
@@ -6511,7 +6586,7 @@ program.command("sessions").description("List recent conversation sessions").act
6511
6586
  });
6512
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) => {
6513
6588
  try {
6514
- const batch = await import("./batch-CDCTUTIQ.js");
6589
+ const batch = await import("./batch-OUBKAHXG.js");
6515
6590
  switch (action) {
6516
6591
  case "submit":
6517
6592
  if (!arg) {
@@ -6671,7 +6746,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
6671
6746
  }),
6672
6747
  config.get("customProviders")
6673
6748
  );
6674
- const { startHub } = await import("./hub-BIFM6NOM.js");
6749
+ const { startHub } = await import("./hub-SFJU4KJK.js");
6675
6750
  await startHub(
6676
6751
  {
6677
6752
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-TNAPORYF.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-KHYHG2SO.js";
6
- import "./chunk-HELGL4RH.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-GKDETNDH.js";
23
+ } from "./chunk-6HZTXVTF.js";
24
24
  import {
25
25
  ConfigManager
26
- } from "./chunk-FCVQ6OP5.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-EUE6VMGO.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-KHYHG2SO.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-HELGL4RH.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 ──────────────────────────────────────────────────────
@@ -1946,12 +2009,19 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1946
2009
  this.send({ type: "error", message: "No symbol index yet. Run /index rebuild first." });
1947
2010
  break;
1948
2011
  }
2012
+ if (idx.symbolCount === 0) {
2013
+ this.send({
2014
+ type: "error",
2015
+ message: "No code symbols to embed. Semantic search needs TS/TSX/JS/JSX/Python source files \u2014 this project appears to have none. Not downloading the embedding model."
2016
+ });
2017
+ break;
2018
+ }
1949
2019
  this.send({
1950
2020
  type: "info",
1951
2021
  message: `Building semantic index for ${idx.symbolCount} symbols\u2026 (first run downloads ~117 MB model)`
1952
2022
  });
1953
2023
  try {
1954
- const { rebuildSemanticIndex } = await import("./semantic-RBWU76MD.js");
2024
+ const { rebuildSemanticIndex } = await import("./semantic-ICJ536BG.js");
1955
2025
  const stats = await rebuildSemanticIndex(root);
1956
2026
  const first = stats.modelFirstLoadMs ? ` (model load+first batch ${stats.modelFirstLoadMs}ms)` : "";
1957
2027
  this.send({
@@ -2138,7 +2208,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2138
2208
  case "test": {
2139
2209
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2140
2210
  try {
2141
- const { executeTests } = await import("./run-tests-JBM4K5FO.js");
2211
+ const { executeTests } = await import("./run-tests-7N2WILA3.js");
2142
2212
  const argStr = args.join(" ").trim();
2143
2213
  let testArgs = {};
2144
2214
  if (argStr) {
@@ -4,17 +4,17 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-EUE6VMGO.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-KHYHG2SO.js";
14
+ import "./chunk-IGHC7D62.js";
15
15
  import {
16
16
  SUBAGENT_ALLOWED_TOOLS
17
- } from "./chunk-HELGL4RH.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.78",
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",