jinzd-ai-cli 0.4.169 → 0.4.171

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.md CHANGED
@@ -7,11 +7,11 @@
7
7
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
10
- [![Tests](https://img.shields.io/badge/tests-647%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-1097%20passing-brightgreen)]()
11
11
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
12
12
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
13
13
 
14
- **ai-cli** is a powerful AI assistant that connects to 8 providers (including local Ollama models) and executes tasks autonomously through agentic tool calling. Use it as a terminal REPL, a browser-based Web UI, or a standalone Electron desktop app.
14
+ **ai-cli** is a powerful AI assistant that connects to 9 providers (including local Ollama models) and executes tasks autonomously through agentic tool calling. Use it as a terminal REPL, a browser-based Web UI, or a standalone Electron desktop app.
15
15
 
16
16
  <p align="center">
17
17
  <img src="https://img.shields.io/badge/CLI-Terminal-blue" alt="CLI" />
@@ -21,7 +21,7 @@
21
21
 
22
22
  ## Highlights
23
23
 
24
- - **8 Built-in Providers** — Claude, Gemini, DeepSeek, OpenAI, Zhipu GLM, Kimi, OpenRouter (300+ models), **Ollama** (local models, no API key needed)
24
+ - **9 Built-in Providers** — Claude, Gemini, DeepSeek, OpenAI, Zhipu GLM, Kimi, **MiniMax (海螺)**, OpenRouter (300+ models), **Ollama** (local models, no API key needed)
25
25
  - **3 Interfaces** — Terminal CLI, browser Web UI (`aicli web`), Electron desktop app
26
26
  - **Agentic Tool Calling** — AI autonomously runs shell commands, reads/writes files, searches code, fetches web, runs tests (default 200 rounds, configurable up to 10000 via `config.maxToolRounds` or `--max-tool-rounds`)
27
27
  - **Prompt Caching** *(v0.4.70+)* — System prompt split into stable/volatile halves so Claude caches the stable part with `cache_control: ephemeral`; cached tokens bill at ~10% of the input price
@@ -31,7 +31,7 @@
31
31
  - **Conversation Branching** *(v0.4.74+)* — `/branch list/new/switch/delete/rename` inside the REPL, plus a 🌿 "fork here" button on every replay step — explore alternate directions without losing the original thread
32
32
  - **Symbol Index** *(v0.4.76+, multi-language since v0.4.143)* — persistent tree-sitter index for TypeScript / JavaScript / TSX / Python / Go / Rust / Java / C/C++ powers three new AI tools: `find_symbol`, `get_outline`, `find_references`. Orders of magnitude faster than grep for definition lookups; background refresh on REPL startup, `/index status|rebuild|clear` to manage
33
33
  - **Semantic Code Search** *(v0.4.77+)* — `search_code` tool finds code by meaning, not name. Local sentence embeddings (multilingual MiniLM, 117 MB one-time download) score symbols by cosine similarity against natural-language queries in English or Chinese ("where are users authenticated", "哪里做了速率限制"). No API key, runs on CPU. Manage with `/index semantic-rebuild|semantic-clear`
34
- - **MCP Server Mode** *(v0.4.84+)* — `aicli mcp-serve` reverses ai-cli into an MCP server (JSON-RPC 2.0 over stdio), exposing its 26 built-in tools (incl. `find_symbol` / `search_code` / `run_tests`) to Claude Desktop / Cursor / any MCP client. Opt-in destructive-tool allow, `--tools` whitelist, `--cwd` override
34
+ - **MCP Server Mode** *(v0.4.84+)* — `aicli mcp-serve` reverses ai-cli into an MCP server (JSON-RPC 2.0 over stdio), exposing its 28 built-in tools (incl. `find_symbol` / `search_code` / `run_tests`) to Claude Desktop / Cursor / any MCP client. Opt-in destructive-tool allow, `--tools` whitelist, `--cwd` override
35
35
  - **Session Sensitive-Data Redaction** *(v0.4.88+)* — unified redactor scrubs `password=` / `api_key` / bearer tokens / OpenAI-style keys from every message **before it hits disk**. Query text is redacted too, so secrets never reach embeddings or logs. `/security status` + `/security scan` to audit
36
36
  - **Human-like Long-Term Memory** *(v0.4.89+, B4)* — semantic index over every past chat session + `recall_memory` AI tool + `/memory rebuild|refresh|status|recall` commands. AI is prompted to auto-recall when it sees "last time" / "之前" / ambiguous references. Reuses the same MiniLM embedder as semantic code search
37
37
  - **Web UI Memory Panel** *(v0.4.90+, B4)* — new 🧠 Memory sidebar tab with semantic search across past chats; each hit has **➕ Inject** (quotes the snippet into the chat input as a markdown blockquote so you can review/edit before sending — no silent context injection) and **↗ Load** (jumps to source session). Bulk "Inject top 3" for recall bundles
package/README.zh-CN.md CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  # ai-cli
4
4
 
5
- > 跨平台 AI 编程助手 — CLI 终端、Web 界面、桌面应用三合一,支持 8 大 Provider(含本地 Ollama)与 Agentic 工具调用
5
+ > 跨平台 AI 编程助手 — CLI 终端、Web 界面、桌面应用三合一,支持 9 大 Provider(含本地 Ollama)与 Agentic 工具调用
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/jinzd-ai-cli)](https://www.npmjs.com/package/jinzd-ai-cli)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org)
10
- [![Tests](https://img.shields.io/badge/tests-647%20passing-brightgreen)]()
10
+ [![Tests](https://img.shields.io/badge/tests-1097%20passing-brightgreen)]()
11
11
  [![GitHub Release](https://img.shields.io/github/v/release/jinzhengdong/ai-cli)](https://github.com/jinzhengdong/ai-cli/releases)
12
12
  [![CI](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jinzhengdong/ai-cli/actions/workflows/ci.yml)
13
13
 
14
14
  ## 特性亮点
15
15
 
16
- - **8 大内置 Provider** — Claude、Gemini、DeepSeek、OpenAI、智谱 GLM、KimiOpenRouter(300+ 模型)、**Ollama**(本地模型,无需 API Key)
16
+ - **9 大内置 Provider** — Claude、Gemini、DeepSeek、OpenAI、智谱 GLM、Kimi、**MiniMax(海螺)**、OpenRouter(300+ 模型)、**Ollama**(本地模型,无需 API Key)
17
17
  - **三种使用方式** — 终端 CLI、浏览器 Web UI(`aicli web`)、Electron 桌面应用
18
18
  - **Agentic 工具调用** — AI 自主执行 bash 命令、读写文件、搜索代码、抓取网页、运行测试(默认 200 轮,可通过 `config.maxToolRounds` 或 `--max-tool-rounds` 调整,上限 10000)
19
19
  - **Prompt Caching**(v0.4.70+)— system prompt 拆分稳定/易变两段,Claude 对稳定段启用 `cache_control: ephemeral`,命中时按 10% 计费
@@ -23,7 +23,7 @@
23
23
  - **对话分支**(v0.4.74+)— REPL 内 `/branch list/new/switch/delete/rename/diff/cherry-pick`,Web UI 回放面板每条消息旁 🌿 "fork here" 按钮,任意位置开辟新分支探索不同方向,原对话保持不变;v0.4.81 起所有子命令支持 id / title / 唯一前缀
24
24
  - **符号索引**(v0.4.76+,C1;v0.4.143 起多语言)— tree-sitter 持久化索引(TS / JS / TSX / JSX / Python / Go / Rust / Java / C/C++)+ 3 个只读 AI 工具(`find_symbol` / `get_outline` / `find_references`)+ `/index status/rebuild/clear`;启动后台增量刷新,`write_file` 后自动 upsert
25
25
  - **语义代码搜索**(v0.4.77+,C2)— `search_code` AI 工具 + 本地 sentence embedding(`paraphrase-multilingual-MiniLM-L12-v2`,117 MB 一次性下载,384 维,CPU 运行);支持中英文自然语言查询代码;`/index semantic-rebuild/semantic-clear` 管理
26
- - **MCP Server 模式**(v0.4.84+,E1)— `aicli mcp-serve` 把 aicli 反转为 MCP 服务器(JSON-RPC 2.0 over stdio),把 26 个内置工具(含 `find_symbol` / `search_code` / `run_tests`)输出给 Claude Desktop / Cursor / 任何 MCP 客户端;支持 `--tools` 白名单、`--cwd` 覆盖、`--allow-destructive`
26
+ - **MCP Server 模式**(v0.4.84+,E1)— `aicli mcp-serve` 把 aicli 反转为 MCP 服务器(JSON-RPC 2.0 over stdio),把 28 个内置工具(含 `find_symbol` / `search_code` / `run_tests`)输出给 Claude Desktop / Cursor / 任何 MCP 客户端;支持 `--tools` 白名单、`--cwd` 覆盖、`--allow-destructive`
27
27
  - **Session 敏感数据脱敏**(v0.4.88+)— 统一 redactor 在 session **落盘前**自动替换 `password=` / `api_key` / bearer token / OpenAI key 为 `[REDACTED:*]`;查询文本也走脱敏,secret 不会进 embedding 或日志;`/security status` + `/security scan` 审计
28
28
  - **类人长期记忆**(v0.4.89+,B4)— 聊天记录语义索引跨 session 可召回 + `recall_memory` AI 工具 + `/memory rebuild/refresh/status/recall/index-clear`;AI 看到"上次"/"之前"/指代不明时自动回忆。复用 C2 的 MiniLM embedder
29
29
  - **Web UI Memory 面板**(v0.4.90+,B4)— sidebar 新增 🧠 Memory 标签页,跨 session 语义搜索;每条 hit 带 **➕ Inject**(把片段作为 markdown 引用块塞进聊天输入框,用户可在上面继续打字编辑——不是静默注入上下文)和 **↗ Load**(跳转对应 session);顶部"➕ Inject top 3"一键批量
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-MD3SNDQ6.js";
4
+ } from "./chunk-6JTVUGSF.js";
5
5
  import "./chunk-TZQHYZKT.js";
6
- import "./chunk-QIFUVX5K.js";
6
+ import "./chunk-RSZWLQU4.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  truncateForPersist
4
- } from "./chunk-RNMT4XBQ.js";
4
+ } from "./chunk-WG4THOHH.js";
5
5
  import {
6
6
  APP_NAME,
7
7
  CONFIG_DIR_NAME,
@@ -11,7 +11,7 @@ import {
11
11
  MCP_PROTOCOL_VERSION,
12
12
  MCP_TOOL_PREFIX,
13
13
  VERSION
14
- } from "./chunk-QIFUVX5K.js";
14
+ } from "./chunk-RSZWLQU4.js";
15
15
 
16
16
  // src/mcp/client.ts
17
17
  import { spawn } 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-QIFUVX5K.js";
11
+ } from "./chunk-RSZWLQU4.js";
12
12
 
13
13
  // src/config/config-manager.ts
14
14
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR_NAME
4
- } from "./chunk-QIFUVX5K.js";
4
+ } from "./chunk-RSZWLQU4.js";
5
5
 
6
6
  // src/diagnostics/tool-stats.ts
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CONFIG_DIR_NAME,
4
4
  VERSION
5
- } from "./chunk-QIFUVX5K.js";
5
+ } from "./chunk-RSZWLQU4.js";
6
6
 
7
7
  // src/diagnostics/crash-log.ts
8
8
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-QIFUVX5K.js";
4
+ } from "./chunk-RSZWLQU4.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } 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.169";
4
+ var VERSION = "0.4.171";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -5,10 +5,10 @@ import {
5
5
  } from "./chunk-HDSKW7Q3.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-6JVMTB2S.js";
8
+ } from "./chunk-MELFYYS7.js";
9
9
  import {
10
10
  runTool
11
- } from "./chunk-QVHML2M7.js";
11
+ } from "./chunk-H53UCPFF.js";
12
12
  import {
13
13
  getDangerLevel,
14
14
  isFileWriteTool
@@ -25,7 +25,7 @@ import {
25
25
  SUBAGENT_ALLOWED_TOOLS,
26
26
  SUBAGENT_DEFAULT_MAX_ROUNDS,
27
27
  SUBAGENT_MAX_ROUNDS_LIMIT
28
- } from "./chunk-QIFUVX5K.js";
28
+ } from "./chunk-RSZWLQU4.js";
29
29
  import {
30
30
  fileCheckpoints
31
31
  } from "./chunk-4BKXL7SM.js";
@@ -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.169";
9
+ var VERSION = "0.4.171";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -10,11 +10,11 @@ import {
10
10
  import "./chunk-NXXNLLSG.js";
11
11
  import {
12
12
  ConfigManager
13
- } from "./chunk-MD3SNDQ6.js";
13
+ } from "./chunk-6JTVUGSF.js";
14
14
  import "./chunk-TZQHYZKT.js";
15
15
  import {
16
16
  VERSION
17
- } from "./chunk-QIFUVX5K.js";
17
+ } from "./chunk-RSZWLQU4.js";
18
18
  import "./chunk-PDX44BCA.js";
19
19
 
20
20
  // src/cli/ci.ts
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-QIFUVX5K.js";
39
+ } from "./chunk-RSZWLQU4.js";
40
40
  import "./chunk-PDX44BCA.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getConfigDirUsage,
4
4
  listRecentCrashes
5
- } from "./chunk-I4ZOULB6.js";
5
+ } from "./chunk-IZY4MCKG.js";
6
6
  import {
7
7
  ProviderRegistry
8
8
  } from "./chunk-GNJOC6ZN.js";
@@ -11,17 +11,17 @@ import {
11
11
  getTopFailingTools,
12
12
  getTopUsedTools,
13
13
  resetStats
14
- } from "./chunk-QVHML2M7.js";
14
+ } from "./chunk-H53UCPFF.js";
15
15
  import "./chunk-NXXNLLSG.js";
16
16
  import {
17
17
  ConfigManager
18
- } from "./chunk-MD3SNDQ6.js";
18
+ } from "./chunk-6JTVUGSF.js";
19
19
  import "./chunk-TZQHYZKT.js";
20
20
  import {
21
21
  DEV_STATE_FILE_NAME,
22
22
  MEMORY_FILE_NAME,
23
23
  VERSION
24
- } from "./chunk-QIFUVX5K.js";
24
+ } from "./chunk-RSZWLQU4.js";
25
25
  import "./chunk-PDX44BCA.js";
26
26
 
27
27
  // src/diagnostics/doctor-cli.ts
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-X6N4EAMM.js";
39
+ } from "./chunk-YMH6BKAE.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -13548,7 +13548,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
13548
13548
  case "test": {
13549
13549
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
13550
13550
  try {
13551
- const { executeTests } = await import("./run-tests-X5VFC62T.js");
13551
+ const { executeTests } = await import("./run-tests-A2Q5VMHF.js");
13552
13552
  const argStr = args.join(" ").trim();
13553
13553
  let testArgs = {};
13554
13554
  if (argStr) {
@@ -155,7 +155,7 @@ ${content}`);
155
155
  }
156
156
  }
157
157
  async function runTaskMode(config, providers, configManager, topic) {
158
- const { TaskOrchestrator } = await import("./task-orchestrator-SBTDEKS5.js");
158
+ const { TaskOrchestrator } = await import("./task-orchestrator-U7KU4SOS.js");
159
159
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
160
160
  let interrupted = false;
161
161
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  saveDevState,
19
19
  sessionHasMeaningfulContent,
20
20
  setupProxy
21
- } from "./chunk-2QLZGNFP.js";
21
+ } from "./chunk-57H6YNXY.js";
22
22
  import {
23
23
  ToolExecutor,
24
24
  ToolRegistry,
@@ -37,10 +37,10 @@ import {
37
37
  spawnAgentContext,
38
38
  theme,
39
39
  undoStack
40
- } from "./chunk-RNMT4XBQ.js";
40
+ } from "./chunk-WG4THOHH.js";
41
41
  import "./chunk-HDSKW7Q3.js";
42
42
  import "./chunk-ZWVIDFGY.js";
43
- import "./chunk-6JVMTB2S.js";
43
+ import "./chunk-MELFYYS7.js";
44
44
  import {
45
45
  SessionManager,
46
46
  getContentText
@@ -49,7 +49,7 @@ import {
49
49
  getConfigDirUsage,
50
50
  listRecentCrashes,
51
51
  writeCrashLog
52
- } from "./chunk-I4ZOULB6.js";
52
+ } from "./chunk-IZY4MCKG.js";
53
53
  import {
54
54
  CONTENT_ONLY_STREAM_REMINDER,
55
55
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -74,11 +74,11 @@ import {
74
74
  getTopFailingTools,
75
75
  getTopUsedTools,
76
76
  installFlushOnExit
77
- } from "./chunk-QVHML2M7.js";
77
+ } from "./chunk-H53UCPFF.js";
78
78
  import "./chunk-NXXNLLSG.js";
79
79
  import {
80
80
  ConfigManager
81
- } from "./chunk-MD3SNDQ6.js";
81
+ } from "./chunk-6JTVUGSF.js";
82
82
  import {
83
83
  AuthError,
84
84
  ProviderError,
@@ -105,7 +105,7 @@ import {
105
105
  SKILLS_DIR_NAME,
106
106
  VERSION,
107
107
  buildUserIdentityPrompt
108
- } from "./chunk-QIFUVX5K.js";
108
+ } from "./chunk-RSZWLQU4.js";
109
109
  import {
110
110
  formatGitContextForPrompt,
111
111
  getGitContext,
@@ -208,6 +208,13 @@ function isInterruptedSession(messages) {
208
208
  import chalk from "chalk";
209
209
  import { createWriteStream, mkdirSync } from "fs";
210
210
  import { dirname } from "path";
211
+ function partialTagTail(s, tag) {
212
+ const max = Math.min(s.length, tag.length - 1);
213
+ for (let k = max; k > 0; k--) {
214
+ if (s.endsWith(tag.slice(0, k))) return k;
215
+ }
216
+ return 0;
217
+ }
211
218
  function fmtContextWindow(tokens) {
212
219
  if (tokens >= 1e6) return `${Math.round(tokens / 1e5) / 10}M`;
213
220
  if (tokens >= 1e3) return `${Math.round(tokens / 1024)}K`;
@@ -489,34 +496,46 @@ var Renderer = class {
489
496
  let fullContent = "";
490
497
  let usage;
491
498
  let inThinking = false;
492
- let thinkingShown = false;
493
- let buf = "";
494
- const flushBuf = () => {
495
- if (!buf) return;
496
- let out = buf;
497
- while (!inThinking) {
498
- const openIdx = out.indexOf("<think>");
499
- if (openIdx === -1) break;
500
- if (openIdx > 0) process.stdout.write(out.slice(0, openIdx));
501
- inThinking = true;
502
- thinkingShown = true;
503
- out = out.slice(openIdx + "<think>".length);
504
- }
505
- if (inThinking) {
506
- const closeIdx = out.indexOf("</think>");
507
- if (closeIdx !== -1) {
508
- inThinking = false;
509
- out = out.slice(closeIdx + "</think>".length);
510
- buf = out;
511
- flushBuf();
512
- return;
499
+ let thinkBuf = "";
500
+ const emitText = (raw) => {
501
+ thinkBuf += raw;
502
+ let out = "";
503
+ while (thinkBuf.length > 0) {
504
+ if (!inThinking) {
505
+ const open = thinkBuf.indexOf("<think>");
506
+ if (open === -1) {
507
+ const keep = partialTagTail(thinkBuf, "<think>");
508
+ out += thinkBuf.slice(0, thinkBuf.length - keep);
509
+ thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
510
+ break;
511
+ }
512
+ out += thinkBuf.slice(0, open);
513
+ thinkBuf = thinkBuf.slice(open + "<think>".length);
514
+ inThinking = true;
513
515
  } else {
514
- buf = "";
515
- return;
516
+ const close = thinkBuf.indexOf("</think>");
517
+ if (close === -1) {
518
+ const keep = partialTagTail(thinkBuf, "</think>");
519
+ thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
520
+ break;
521
+ }
522
+ thinkBuf = thinkBuf.slice(close + "</think>".length);
523
+ inThinking = false;
516
524
  }
517
525
  }
518
- if (out) process.stdout.write(out);
519
- buf = "";
526
+ if (out) {
527
+ process.stdout.write(out);
528
+ if (fileStream) fileStream.write(out);
529
+ fullContent += out;
530
+ }
531
+ };
532
+ const flushTail = () => {
533
+ if (!inThinking && thinkBuf) {
534
+ process.stdout.write(thinkBuf);
535
+ if (fileStream) fileStream.write(thinkBuf);
536
+ fullContent += thinkBuf;
537
+ thinkBuf = "";
538
+ }
520
539
  };
521
540
  let interrupted = false;
522
541
  let streamErr = null;
@@ -530,23 +549,19 @@ var Renderer = class {
530
549
  usage = chunk.usage;
531
550
  }
532
551
  if (chunk.done) {
533
- flushBuf();
534
552
  break;
535
553
  }
536
554
  if (!chunk.delta) continue;
537
- fullContent += chunk.delta;
538
- buf += chunk.delta;
539
- if (fileStream) fileStream.write(chunk.delta);
540
- flushBuf();
555
+ emitText(chunk.delta);
541
556
  }
542
557
  } catch (err) {
543
558
  if (err?.name === "AbortError") {
544
559
  interrupted = true;
545
- if (!inThinking) flushBuf();
546
560
  } else {
547
561
  streamErr = err;
548
562
  }
549
563
  }
564
+ flushTail();
550
565
  if (interrupted) {
551
566
  process.stdout.write(theme.dim(" [interrupted]\n"));
552
567
  }
@@ -1771,7 +1786,7 @@ No tools match "${filter}".
1771
1786
  const { join: join6 } = await import("path");
1772
1787
  const { existsSync: existsSync6 } = await import("fs");
1773
1788
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1774
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-FJD3QUHI.js");
1789
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-OMTUJRKU.js");
1775
1790
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1776
1791
  const cwd = process.cwd();
1777
1792
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2832,7 +2847,7 @@ ${hint}` : "")
2832
2847
  usage: "/test [command|filter]",
2833
2848
  async execute(args, ctx) {
2834
2849
  try {
2835
- const { executeTests } = await import("./run-tests-OLKEYSWX.js");
2850
+ const { executeTests } = await import("./run-tests-AYADOMOF.js");
2836
2851
  const argStr = args.join(" ").trim();
2837
2852
  let testArgs = {};
2838
2853
  if (argStr) {
@@ -4683,7 +4698,7 @@ var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
4683
4698
  var MAX_CONSECUTIVE_FREE_ROUNDS = 3;
4684
4699
  var MAX_REPEATED_TOOL_CALLS = 2;
4685
4700
  var DEFAULT_AUTO_PAUSE_INTERVAL = 50;
4686
- function partialTagTail(s, tag) {
4701
+ function partialTagTail2(s, tag) {
4687
4702
  const max = Math.min(s.length, tag.length - 1);
4688
4703
  for (let k = max; k > 0; k--) {
4689
4704
  if (s.endsWith(tag.slice(0, k))) return k;
@@ -6381,7 +6396,7 @@ Session '${this.resumeSessionId}' not found.
6381
6396
  if (!inThink) {
6382
6397
  const open = thinkBuf.indexOf("<think>");
6383
6398
  if (open === -1) {
6384
- const keep = partialTagTail(thinkBuf, "<think>");
6399
+ const keep = partialTagTail2(thinkBuf, "<think>");
6385
6400
  out += thinkBuf.slice(0, thinkBuf.length - keep);
6386
6401
  thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
6387
6402
  break;
@@ -6393,7 +6408,7 @@ Session '${this.resumeSessionId}' not found.
6393
6408
  } else {
6394
6409
  const close = thinkBuf.indexOf("</think>");
6395
6410
  if (close === -1) {
6396
- const keep = partialTagTail(thinkBuf, "</think>");
6411
+ const keep = partialTagTail2(thinkBuf, "</think>");
6397
6412
  thinkBuf = thinkBuf.slice(thinkBuf.length - keep);
6398
6413
  break;
6399
6414
  }
@@ -7581,7 +7596,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7581
7596
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7582
7597
  process.exit(1);
7583
7598
  }
7584
- const { startWebServer } = await import("./server-EOMEJL2N.js");
7599
+ const { startWebServer } = await import("./server-7B7HTSLE.js");
7585
7600
  await startWebServer({ port, host: options.host });
7586
7601
  });
7587
7602
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -7748,12 +7763,12 @@ program.command("sessions").description("List recent conversation sessions").opt
7748
7763
  console.log(footer + "\n");
7749
7764
  });
7750
7765
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7751
- const { runDoctorCli } = await import("./doctor-cli-T4N3V2OT.js");
7766
+ const { runDoctorCli } = await import("./doctor-cli-OZJZKETL.js");
7752
7767
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7753
7768
  });
7754
7769
  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) => {
7755
7770
  try {
7756
- const batch = await import("./batch-UA6OWP5B.js");
7771
+ const batch = await import("./batch-6WDD57ZG.js");
7757
7772
  switch (action) {
7758
7773
  case "submit":
7759
7774
  if (!arg) {
@@ -7796,7 +7811,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7796
7811
  }
7797
7812
  });
7798
7813
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7799
- const { startMcpServer } = await import("./server-QK64LBV2.js");
7814
+ const { startMcpServer } = await import("./server-RXWYMDAH.js");
7800
7815
  await startMcpServer({
7801
7816
  allowDestructive: !!options.allowDestructive,
7802
7817
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7805,7 +7820,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7805
7820
  });
7806
7821
  });
7807
7822
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7808
- const { runCi } = await import("./ci-34RE6YZA.js");
7823
+ const { runCi } = await import("./ci-Y4FEHLDL.js");
7809
7824
  const result = await runCi({
7810
7825
  pr: options.pr,
7811
7826
  base: options.base,
@@ -7950,7 +7965,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7950
7965
  }),
7951
7966
  config.get("customProviders")
7952
7967
  );
7953
- const { startHub } = await import("./hub-7GSXGHN7.js");
7968
+ const { startHub } = await import("./hub-IVVW7BTJ.js");
7954
7969
  await startHub(
7955
7970
  {
7956
7971
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-X6N4EAMM.js";
4
+ } from "./chunk-YMH6BKAE.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-6JVMTB2S.js";
6
- import "./chunk-QIFUVX5K.js";
5
+ } from "./chunk-MELFYYS7.js";
6
+ import "./chunk-RSZWLQU4.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -21,7 +21,7 @@ import {
21
21
  loadDevState,
22
22
  persistToolRound,
23
23
  setupProxy
24
- } from "./chunk-2QLZGNFP.js";
24
+ } from "./chunk-57H6YNXY.js";
25
25
  import {
26
26
  ToolExecutor,
27
27
  ToolRegistry,
@@ -39,10 +39,10 @@ import {
39
39
  spawnAgentContext,
40
40
  truncateOutput,
41
41
  undoStack
42
- } from "./chunk-RNMT4XBQ.js";
42
+ } from "./chunk-WG4THOHH.js";
43
43
  import "./chunk-HDSKW7Q3.js";
44
44
  import "./chunk-ZWVIDFGY.js";
45
- import "./chunk-6JVMTB2S.js";
45
+ import "./chunk-MELFYYS7.js";
46
46
  import {
47
47
  SessionManager,
48
48
  getContentText
@@ -64,13 +64,13 @@ import {
64
64
  } from "./chunk-GNJOC6ZN.js";
65
65
  import {
66
66
  runTool
67
- } from "./chunk-QVHML2M7.js";
67
+ } from "./chunk-H53UCPFF.js";
68
68
  import {
69
69
  getDangerLevel
70
70
  } from "./chunk-NXXNLLSG.js";
71
71
  import {
72
72
  ConfigManager
73
- } from "./chunk-MD3SNDQ6.js";
73
+ } from "./chunk-6JTVUGSF.js";
74
74
  import "./chunk-TZQHYZKT.js";
75
75
  import {
76
76
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -90,7 +90,7 @@ import {
90
90
  SKILLS_DIR_NAME,
91
91
  VERSION,
92
92
  buildUserIdentityPrompt
93
- } from "./chunk-QIFUVX5K.js";
93
+ } from "./chunk-RSZWLQU4.js";
94
94
  import {
95
95
  formatGitContextForPrompt,
96
96
  getGitContext,
@@ -2577,7 +2577,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2577
2577
  case "test": {
2578
2578
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2579
2579
  try {
2580
- const { executeTests } = await import("./run-tests-OLKEYSWX.js");
2580
+ const { executeTests } = await import("./run-tests-AYADOMOF.js");
2581
2581
  const argStr = args.join(" ").trim();
2582
2582
  let testArgs = {};
2583
2583
  if (argStr) {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-RNMT4XBQ.js";
4
+ } from "./chunk-WG4THOHH.js";
5
5
  import "./chunk-HDSKW7Q3.js";
6
6
  import "./chunk-ZWVIDFGY.js";
7
- import "./chunk-6JVMTB2S.js";
7
+ import "./chunk-MELFYYS7.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-QVHML2M7.js";
10
+ } from "./chunk-H53UCPFF.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
@@ -15,7 +15,7 @@ import {
15
15
  import "./chunk-TZQHYZKT.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-QIFUVX5K.js";
18
+ } from "./chunk-RSZWLQU4.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
20
  import "./chunk-MM3F43H6.js";
21
21
  import "./chunk-KHYD3WXE.js";
@@ -3,20 +3,20 @@ import {
3
3
  ToolRegistry,
4
4
  googleSearchContext,
5
5
  truncateOutput
6
- } from "./chunk-RNMT4XBQ.js";
6
+ } from "./chunk-WG4THOHH.js";
7
7
  import "./chunk-HDSKW7Q3.js";
8
8
  import "./chunk-ZWVIDFGY.js";
9
- import "./chunk-6JVMTB2S.js";
9
+ import "./chunk-MELFYYS7.js";
10
10
  import {
11
11
  runTool
12
- } from "./chunk-QVHML2M7.js";
12
+ } from "./chunk-H53UCPFF.js";
13
13
  import {
14
14
  getDangerLevel
15
15
  } from "./chunk-NXXNLLSG.js";
16
16
  import "./chunk-TZQHYZKT.js";
17
17
  import {
18
18
  SUBAGENT_ALLOWED_TOOLS
19
- } from "./chunk-QIFUVX5K.js";
19
+ } from "./chunk-RSZWLQU4.js";
20
20
  import "./chunk-4BKXL7SM.js";
21
21
  import "./chunk-MM3F43H6.js";
22
22
  import "./chunk-KHYD3WXE.js";
@@ -2566,6 +2566,12 @@ userInput.focus();
2566
2566
  const btnSidebarToggle = document.getElementById('btn-sidebar-toggle');
2567
2567
  const sidebarBackdrop = document.getElementById('sidebar-backdrop');
2568
2568
 
2569
+ const SIDEBAR_COLLAPSED_KEY = 'aicli-sidebar-collapsed';
2570
+
2571
+ function isMobileView() {
2572
+ return window.innerWidth <= 768;
2573
+ }
2574
+
2569
2575
  function openSidebar() {
2570
2576
  sidebar.classList.add('sidebar-open');
2571
2577
  sidebarBackdrop?.classList.remove('hidden');
@@ -2576,14 +2582,34 @@ function closeSidebar() {
2576
2582
  sidebarBackdrop?.classList.add('hidden');
2577
2583
  }
2578
2584
 
2585
+ // Desktop: collapse the whole left panel so the chat gets all the space.
2586
+ function setSidebarCollapsed(collapsed) {
2587
+ sidebar.classList.toggle('sidebar-collapsed', collapsed);
2588
+ try { localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed ? '1' : '0'); } catch { /* ignore */ }
2589
+ }
2590
+
2579
2591
  function toggleSidebar() {
2580
- if (sidebar.classList.contains('sidebar-open')) {
2581
- closeSidebar();
2592
+ if (isMobileView()) {
2593
+ // Mobile: slide-over overlay
2594
+ if (sidebar.classList.contains('sidebar-open')) {
2595
+ closeSidebar();
2596
+ } else {
2597
+ openSidebar();
2598
+ }
2582
2599
  } else {
2583
- openSidebar();
2600
+ // Desktop: fully collapse / restore the panel
2601
+ setSidebarCollapsed(!sidebar.classList.contains('sidebar-collapsed'));
2584
2602
  }
2585
2603
  }
2586
2604
 
2605
+ // Restore the desktop collapsed state from a previous session. The CSS rule
2606
+ // is scoped to ≥769px, so adding the class is inert on mobile.
2607
+ try {
2608
+ if (localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === '1') {
2609
+ sidebar.classList.add('sidebar-collapsed');
2610
+ }
2611
+ } catch { /* ignore */ }
2612
+
2587
2613
  if (btnSidebarToggle) {
2588
2614
  btnSidebarToggle.addEventListener('click', toggleSidebar);
2589
2615
  }
@@ -724,8 +724,23 @@
724
724
  font-size: 0.85rem;
725
725
  }
726
726
 
727
- /* ── Sidebar toggle button (hidden on desktop) ──────── */
728
- .sidebar-toggle-btn { display: none; }
727
+ /* ── Sidebar toggle button ──────────────────────────── */
728
+ /* Visible on every width: on desktop it collapses the panel to give all
729
+ space to the chat; on mobile (≤768px) it opens/closes the slide-over. */
730
+ .sidebar-toggle-btn { display: flex; }
731
+
732
+ /* Desktop collapse: scoped to ≥769px so it never fights the mobile
733
+ slide-over rules (which use .sidebar-open). width:0 !important is needed
734
+ to override the inline width set by the drag-resize handle. */
735
+ @media (min-width: 769px) {
736
+ .sidebar.sidebar-collapsed {
737
+ width: 0 !important;
738
+ min-width: 0;
739
+ border-right-width: 0;
740
+ overflow: hidden;
741
+ }
742
+ .sidebar.sidebar-collapsed + .sidebar-resize-handle { display: none; }
743
+ }
729
744
 
730
745
  /* ── Sidebar backdrop (mobile overlay) ───────────────── */
731
746
  .sidebar-backdrop {
@@ -845,132 +860,132 @@ button, a, .session-item, .file-tree-row, .template-item, .tool-item, .mcp-serve
845
860
  @media (display-mode: standalone) {
846
861
  .navbar { padding-top: env(safe-area-inset-top, 0px); }
847
862
  }
848
-
849
- /* ── Session Replay (B1) ───────────────────────────── */
850
- .replay-step {
851
- border-left: 3px solid hsl(var(--b3));
852
- padding: 0.5rem 0.6rem;
853
- background: hsl(var(--b1));
854
- border-radius: 0 0.35rem 0.35rem 0;
855
- font-size: 0.85rem;
856
- }
857
- .replay-step.role-user { border-left-color: #3b82f6; }
858
- .replay-step.role-assistant { border-left-color: #10b981; }
859
- .replay-step.role-tool { border-left-color: #f59e0b; }
860
- .replay-step.role-tool.error { border-left-color: #ef4444; }
861
- .replay-step-header {
862
- display: flex;
863
- gap: 0.5rem;
864
- align-items: center;
865
- font-size: 0.72rem;
866
- opacity: 0.75;
867
- margin-bottom: 0.25rem;
868
- }
869
- .replay-step-header .role-tag {
870
- font-weight: 600;
871
- padding: 0 0.35rem;
872
- border-radius: 0.25rem;
873
- background: hsl(var(--b3));
874
- }
875
- .replay-step-body {
876
- white-space: pre-wrap;
877
- word-break: break-word;
878
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
879
- font-size: 0.78rem;
880
- max-height: 18rem;
881
- overflow-y: auto;
882
- }
883
- .replay-step-body.text-body {
884
- font-family: inherit;
885
- font-size: 0.85rem;
886
- }
887
- .replay-tool-block {
888
- margin-top: 0.3rem;
889
- padding: 0.4rem;
890
- background: hsl(var(--b2));
891
- border-radius: 0.3rem;
892
- font-size: 0.78rem;
893
- }
894
- .replay-tool-block .tool-name {
895
- font-weight: 600;
896
- color: #f59e0b;
897
- }
898
- .replay-tool-block pre {
899
- margin: 0.2rem 0 0;
900
- white-space: pre-wrap;
901
- word-break: break-word;
902
- font-size: 0.72rem;
903
- max-height: 12rem;
904
- overflow-y: auto;
905
- }
906
-
907
- /* ── B2 Branch picker (sidebar) ─────────────────────────── */
908
- .branch-item {
909
- display: flex;
910
- align-items: center;
911
- gap: 0.35rem;
912
- padding: 0.35rem 0.5rem;
913
- border-radius: 0.35rem;
914
- cursor: pointer;
915
- border: 1px solid transparent;
916
- transition: background 0.1s, border-color 0.1s;
917
- font-size: 0.78rem;
918
- line-height: 1.25;
919
- position: relative;
920
- }
921
- .branch-item:hover {
922
- background: rgba(128, 128, 128, 0.12);
923
- }
924
- .branch-item.active {
925
- background: rgba(34, 197, 94, 0.12);
926
- border-color: rgba(34, 197, 94, 0.45);
927
- }
928
- .branch-item .branch-marker {
929
- flex-shrink: 0;
930
- width: 0.8rem;
931
- color: rgb(34, 197, 94);
932
- }
933
- .branch-item .branch-title {
934
- flex: 1;
935
- min-width: 0;
936
- overflow: hidden;
937
- text-overflow: ellipsis;
938
- white-space: nowrap;
939
- }
940
- .branch-item .branch-id {
941
- flex-shrink: 0;
942
- opacity: 0.5;
943
- font-family: ui-monospace, SFMono-Regular, monospace;
944
- font-size: 0.7rem;
945
- }
946
- .branch-item .branch-count {
947
- flex-shrink: 0;
948
- opacity: 0.55;
949
- font-size: 0.7rem;
950
- }
951
- .branch-item .branch-actions {
952
- display: none;
953
- gap: 0.15rem;
954
- flex-shrink: 0;
955
- }
956
- .branch-item:hover .branch-actions {
957
- display: flex;
958
- }
959
- .branch-item .branch-actions button {
960
- background: transparent;
961
- border: none;
962
- padding: 0 0.2rem;
963
- font-size: 0.72rem;
964
- cursor: pointer;
965
- opacity: 0.7;
966
- }
967
- .branch-item .branch-actions button:hover {
968
- opacity: 1;
969
- }
970
- .branch-item .branch-indent {
971
- flex-shrink: 0;
972
- color: rgba(128, 128, 128, 0.5);
973
- font-family: ui-monospace, SFMono-Regular, monospace;
974
- font-size: 0.72rem;
975
- white-space: pre;
976
- }
863
+
864
+ /* ── Session Replay (B1) ───────────────────────────── */
865
+ .replay-step {
866
+ border-left: 3px solid hsl(var(--b3));
867
+ padding: 0.5rem 0.6rem;
868
+ background: hsl(var(--b1));
869
+ border-radius: 0 0.35rem 0.35rem 0;
870
+ font-size: 0.85rem;
871
+ }
872
+ .replay-step.role-user { border-left-color: #3b82f6; }
873
+ .replay-step.role-assistant { border-left-color: #10b981; }
874
+ .replay-step.role-tool { border-left-color: #f59e0b; }
875
+ .replay-step.role-tool.error { border-left-color: #ef4444; }
876
+ .replay-step-header {
877
+ display: flex;
878
+ gap: 0.5rem;
879
+ align-items: center;
880
+ font-size: 0.72rem;
881
+ opacity: 0.75;
882
+ margin-bottom: 0.25rem;
883
+ }
884
+ .replay-step-header .role-tag {
885
+ font-weight: 600;
886
+ padding: 0 0.35rem;
887
+ border-radius: 0.25rem;
888
+ background: hsl(var(--b3));
889
+ }
890
+ .replay-step-body {
891
+ white-space: pre-wrap;
892
+ word-break: break-word;
893
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
894
+ font-size: 0.78rem;
895
+ max-height: 18rem;
896
+ overflow-y: auto;
897
+ }
898
+ .replay-step-body.text-body {
899
+ font-family: inherit;
900
+ font-size: 0.85rem;
901
+ }
902
+ .replay-tool-block {
903
+ margin-top: 0.3rem;
904
+ padding: 0.4rem;
905
+ background: hsl(var(--b2));
906
+ border-radius: 0.3rem;
907
+ font-size: 0.78rem;
908
+ }
909
+ .replay-tool-block .tool-name {
910
+ font-weight: 600;
911
+ color: #f59e0b;
912
+ }
913
+ .replay-tool-block pre {
914
+ margin: 0.2rem 0 0;
915
+ white-space: pre-wrap;
916
+ word-break: break-word;
917
+ font-size: 0.72rem;
918
+ max-height: 12rem;
919
+ overflow-y: auto;
920
+ }
921
+
922
+ /* ── B2 Branch picker (sidebar) ─────────────────────────── */
923
+ .branch-item {
924
+ display: flex;
925
+ align-items: center;
926
+ gap: 0.35rem;
927
+ padding: 0.35rem 0.5rem;
928
+ border-radius: 0.35rem;
929
+ cursor: pointer;
930
+ border: 1px solid transparent;
931
+ transition: background 0.1s, border-color 0.1s;
932
+ font-size: 0.78rem;
933
+ line-height: 1.25;
934
+ position: relative;
935
+ }
936
+ .branch-item:hover {
937
+ background: rgba(128, 128, 128, 0.12);
938
+ }
939
+ .branch-item.active {
940
+ background: rgba(34, 197, 94, 0.12);
941
+ border-color: rgba(34, 197, 94, 0.45);
942
+ }
943
+ .branch-item .branch-marker {
944
+ flex-shrink: 0;
945
+ width: 0.8rem;
946
+ color: rgb(34, 197, 94);
947
+ }
948
+ .branch-item .branch-title {
949
+ flex: 1;
950
+ min-width: 0;
951
+ overflow: hidden;
952
+ text-overflow: ellipsis;
953
+ white-space: nowrap;
954
+ }
955
+ .branch-item .branch-id {
956
+ flex-shrink: 0;
957
+ opacity: 0.5;
958
+ font-family: ui-monospace, SFMono-Regular, monospace;
959
+ font-size: 0.7rem;
960
+ }
961
+ .branch-item .branch-count {
962
+ flex-shrink: 0;
963
+ opacity: 0.55;
964
+ font-size: 0.7rem;
965
+ }
966
+ .branch-item .branch-actions {
967
+ display: none;
968
+ gap: 0.15rem;
969
+ flex-shrink: 0;
970
+ }
971
+ .branch-item:hover .branch-actions {
972
+ display: flex;
973
+ }
974
+ .branch-item .branch-actions button {
975
+ background: transparent;
976
+ border: none;
977
+ padding: 0 0.2rem;
978
+ font-size: 0.72rem;
979
+ cursor: pointer;
980
+ opacity: 0.7;
981
+ }
982
+ .branch-item .branch-actions button:hover {
983
+ opacity: 1;
984
+ }
985
+ .branch-item .branch-indent {
986
+ flex-shrink: 0;
987
+ color: rgba(128, 128, 128, 0.5);
988
+ font-family: ui-monospace, SFMono-Regular, monospace;
989
+ font-size: 0.72rem;
990
+ white-space: pre;
991
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.169",
3
+ "version": "0.4.171",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -124,6 +124,7 @@
124
124
  "extraMetadata": {
125
125
  "main": "electron/main.cjs"
126
126
  },
127
+ "npmRebuild": false,
127
128
  "files": [
128
129
  "dist/**/*",
129
130
  "electron/**/*.cjs"