jinzd-ai-cli 0.1.73 → 0.1.75

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.
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.1.73";
11
+ var VERSION = "0.1.75";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -75,9 +75,11 @@ var CONTEXT_PRESSURE_THRESHOLD = 0.8;
75
75
  var TEST_TIMEOUT = 3e5;
76
76
  var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
77
77
 
78
- **Distinguish between "understanding" and "executing"**:
78
+ **Respond appropriately to the user's intent \u2014 do NOT over-react**:
79
+ - For **greetings and casual chat** (e.g., "hello", "hi", "hey", "\u4F60\u597D", "what's up"): respond naturally with a friendly greeting. Do NOT use any tools. Do NOT explore directories, read files, or start any project work. Just chat.
79
80
  - When the user asks you to "read", "understand", "review", "analyze", "examine", or "look at" files or a project, your task is only to **read and summarize**, then wait for the user's next instruction. Do not automatically start executing tasks described in the project.
80
81
  - Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
82
+ - Project context files (CLAUDE.md, AICLI.md) provide background information about the project. They are NOT instructions to start working. Only use them as reference when the user asks a project-related question or task.
81
83
  - If you are unsure about the user's intent, use the ask_user tool to confirm with the user, rather than assuming and executing on your own.`;
82
84
  var AUTHOR = "Jin Zhengdong";
83
85
  var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
@@ -16,7 +16,7 @@ import {
16
16
  SUBAGENT_MAX_ROUNDS_LIMIT,
17
17
  VERSION,
18
18
  runTestsTool
19
- } from "./chunk-FEYESHOP.js";
19
+ } from "./chunk-KDXXHXYU.js";
20
20
 
21
21
  // src/config/config-manager.ts
22
22
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -5405,6 +5405,17 @@ var SkillManager = class {
5405
5405
  }
5406
5406
  };
5407
5407
 
5408
+ // src/core/proxy.ts
5409
+ async function setupProxy(configProxy) {
5410
+ const proxyUrl = process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY ?? process.env.https_proxy ?? process.env.http_proxy ?? configProxy;
5411
+ if (!proxyUrl) return;
5412
+ try {
5413
+ const { ProxyAgent, setGlobalDispatcher } = await import("undici");
5414
+ setGlobalDispatcher(new ProxyAgent({ uri: proxyUrl }));
5415
+ } catch {
5416
+ }
5417
+ }
5418
+
5408
5419
  // src/repl/dev-state.ts
5409
5420
  import { existsSync as existsSync14, readFileSync as readFileSync8, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2, mkdirSync as mkdirSync8 } from "fs";
5410
5421
  import { join as join10 } from "path";
@@ -5722,5 +5733,6 @@ export {
5722
5733
  loadDevState,
5723
5734
  clearDevState,
5724
5735
  McpManager,
5725
- SkillManager
5736
+ SkillManager,
5737
+ setupProxy
5726
5738
  };
package/dist/index.js CHANGED
@@ -30,11 +30,12 @@ import {
30
30
  runHook,
31
31
  saveDevState,
32
32
  sessionHasMeaningfulContent,
33
+ setupProxy,
33
34
  spawnAgentContext,
34
35
  theme,
35
36
  truncateOutput,
36
37
  undoStack
37
- } from "./chunk-ABXJS3XB.js";
38
+ } from "./chunk-MD2SRFU4.js";
38
39
  import {
39
40
  AGENTIC_BEHAVIOR_GUIDELINE,
40
41
  AUTHOR,
@@ -54,7 +55,7 @@ import {
54
55
  REPO_URL,
55
56
  SKILLS_DIR_NAME,
56
57
  VERSION
57
- } from "./chunk-FEYESHOP.js";
58
+ } from "./chunk-KDXXHXYU.js";
58
59
 
59
60
  // src/index.ts
60
61
  import { program } from "commander";
@@ -1903,7 +1904,7 @@ ${hint}` : "")
1903
1904
  description: "Run project tests and show structured report",
1904
1905
  usage: "/test [command|filter]",
1905
1906
  async execute(args, _ctx) {
1906
- const { executeTests } = await import("./run-tests-MYRF47LA.js");
1907
+ const { executeTests } = await import("./run-tests-D5RGQY6A.js");
1907
1908
  const argStr = args.join(" ").trim();
1908
1909
  let testArgs = {};
1909
1910
  if (argStr) {
@@ -5290,7 +5291,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5290
5291
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5291
5292
  process.exit(1);
5292
5293
  }
5293
- const { startWebServer } = await import("./server-AAP2OMDQ.js");
5294
+ const { startWebServer } = await import("./server-4ZYMNPYO.js");
5294
5295
  await startWebServer({ port, host: options.host });
5295
5296
  });
5296
5297
  program.command("sessions").description("List recent conversation sessions").action(async () => {
@@ -5314,15 +5315,6 @@ program.command("sessions").description("List recent conversation sessions").act
5314
5315
  console.log();
5315
5316
  });
5316
5317
  program.parse();
5317
- async function setupProxy(configProxy) {
5318
- const proxyUrl = process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY ?? process.env.https_proxy ?? process.env.http_proxy ?? configProxy;
5319
- if (!proxyUrl) return;
5320
- try {
5321
- const { ProxyAgent, setGlobalDispatcher } = await import("undici");
5322
- setGlobalDispatcher(new ProxyAgent({ uri: proxyUrl }));
5323
- } catch {
5324
- }
5325
- }
5326
5318
  async function readStdin() {
5327
5319
  if (process.stdin.isTTY) return "";
5328
5320
  return new Promise((resolve3, reject) => {
@@ -5525,3 +5517,6 @@ Provider '${options.provider}' is not configured. Run: ai-cli config
5525
5517
  });
5526
5518
  await repl.start();
5527
5519
  }
5520
+ export {
5521
+ setupProxy
5522
+ };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-FEYESHOP.js";
5
+ } from "./chunk-KDXXHXYU.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -19,11 +19,13 @@ import {
19
19
  loadDevState,
20
20
  renderDiff,
21
21
  runHook,
22
+ setupProxy,
22
23
  spawnAgentContext,
23
24
  truncateOutput
24
- } from "./chunk-ABXJS3XB.js";
25
+ } from "./chunk-MD2SRFU4.js";
25
26
  import {
26
27
  AGENTIC_BEHAVIOR_GUIDELINE,
28
+ CONTEXT_FILE_CANDIDATES,
27
29
  DEFAULT_MAX_TOKENS,
28
30
  MCP_PROJECT_CONFIG_NAME,
29
31
  MEMORY_FILE_NAME,
@@ -32,13 +34,13 @@ import {
32
34
  PLAN_MODE_SYSTEM_ADDON,
33
35
  SKILLS_DIR_NAME,
34
36
  VERSION
35
- } from "./chunk-FEYESHOP.js";
37
+ } from "./chunk-KDXXHXYU.js";
36
38
 
37
39
  // src/web/server.ts
38
40
  import express from "express";
39
41
  import { createServer } from "http";
40
42
  import { WebSocketServer } from "ws";
41
- import { join as join3, dirname, resolve } from "path";
43
+ import { join as join3, dirname, resolve as resolve2 } from "path";
42
44
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
43
45
 
44
46
  // src/web/tool-executor-web.ts
@@ -71,31 +73,31 @@ var ToolExecutorWeb = class {
71
73
  }
72
74
  /** Resolve a pending confirm from client response */
73
75
  resolveConfirm(requestId, approved) {
74
- const resolve2 = this.pendingConfirms.get(requestId);
75
- if (resolve2) {
76
+ const resolve3 = this.pendingConfirms.get(requestId);
77
+ if (resolve3) {
76
78
  this.pendingConfirms.delete(requestId);
77
79
  this.confirming = false;
78
- resolve2(approved);
80
+ resolve3(approved);
79
81
  }
80
82
  }
81
83
  /** Resolve a pending batch confirm from client response */
82
84
  resolveBatchConfirm(requestId, decision) {
83
- const resolve2 = this.pendingBatchConfirms.get(requestId);
84
- if (resolve2) {
85
+ const resolve3 = this.pendingBatchConfirms.get(requestId);
86
+ if (resolve3) {
85
87
  this.pendingBatchConfirms.delete(requestId);
86
88
  this.confirming = false;
87
89
  if (decision === "all" || decision === "none") {
88
- resolve2(decision);
90
+ resolve3(decision);
89
91
  } else {
90
- resolve2(new Set(decision));
92
+ resolve3(new Set(decision));
91
93
  }
92
94
  }
93
95
  }
94
96
  /** Cancel all pending confirms (e.g., on disconnect) */
95
97
  cancelAll() {
96
- for (const resolve2 of this.pendingConfirms.values()) resolve2(false);
98
+ for (const resolve3 of this.pendingConfirms.values()) resolve3(false);
97
99
  this.pendingConfirms.clear();
98
- for (const resolve2 of this.pendingBatchConfirms.values()) resolve2("none");
100
+ for (const resolve3 of this.pendingBatchConfirms.values()) resolve3("none");
99
101
  this.pendingBatchConfirms.clear();
100
102
  this.confirming = false;
101
103
  }
@@ -164,8 +166,8 @@ var ToolExecutorWeb = class {
164
166
  diff: this.getDiffPreview(call)
165
167
  };
166
168
  this.send(msg);
167
- return new Promise((resolve2) => {
168
- this.pendingConfirms.set(requestId, resolve2);
169
+ return new Promise((resolve3) => {
170
+ this.pendingConfirms.set(requestId, resolve3);
169
171
  });
170
172
  }
171
173
  /** WebSocket-based batch confirm */
@@ -184,8 +186,8 @@ var ToolExecutorWeb = class {
184
186
  files
185
187
  };
186
188
  this.send(msg);
187
- return new Promise((resolve2) => {
188
- this.pendingBatchConfirms.set(requestId, resolve2);
189
+ return new Promise((resolve3) => {
190
+ this.pendingBatchConfirms.set(requestId, resolve3);
189
191
  });
190
192
  }
191
193
  async execute(call) {
@@ -387,7 +389,7 @@ function loadMemoryContent(configDir) {
387
389
 
388
390
  // src/web/session-handler.ts
389
391
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
390
- import { join as join2 } from "path";
392
+ import { join as join2, resolve } from "path";
391
393
  var MAX_TOOL_ROUNDS = 25;
392
394
  var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
393
395
  var MAX_CONSECUTIVE_FREE_ROUNDS = 5;
@@ -488,10 +490,10 @@ var SessionHandler = class {
488
490
  return;
489
491
  }
490
492
  case "ask_user_response": {
491
- const resolve2 = this.pendingAskUser.get(msg.requestId);
492
- if (resolve2) {
493
+ const resolve3 = this.pendingAskUser.get(msg.requestId);
494
+ if (resolve3) {
493
495
  this.pendingAskUser.delete(msg.requestId);
494
- resolve2(msg.answer);
496
+ resolve3(msg.answer);
495
497
  }
496
498
  return;
497
499
  }
@@ -508,7 +510,7 @@ var SessionHandler = class {
508
510
  onDisconnect() {
509
511
  this.toolExecutor.cancelAll();
510
512
  if (this.abortController) this.abortController.abort();
511
- for (const resolve2 of this.pendingAskUser.values()) resolve2(null);
513
+ for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
512
514
  this.pendingAskUser.clear();
513
515
  this.sessions.save();
514
516
  }
@@ -962,19 +964,43 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
962
964
  }
963
965
  return defs;
964
966
  }
965
- loadContextFiles() {
966
- const cwd = process.cwd();
967
- const candidates = ["AICLI.md", "CLAUDE.md"];
968
- for (const name of candidates) {
969
- const fullPath = join2(cwd, name);
967
+ /**
968
+ * Find first matching context file in a directory.
969
+ */
970
+ findContextFile(dir) {
971
+ for (const name of CONTEXT_FILE_CANDIDATES) {
972
+ const fullPath = join2(dir, name);
970
973
  try {
971
974
  if (existsSync3(fullPath)) {
972
- return readFileSync3(fullPath, "utf-8").trim() || void 0;
975
+ const content = readFileSync3(fullPath, "utf-8").trim();
976
+ if (content) return content;
973
977
  }
974
978
  } catch {
975
979
  }
976
980
  }
977
- return void 0;
981
+ return null;
982
+ }
983
+ /**
984
+ * Load hierarchical context files (same as CLI):
985
+ * 1. Global: ~/.aicli/AICLI.md or CLAUDE.md
986
+ * 2. Project: <git-root>/AICLI.md or CLAUDE.md
987
+ * 3. Subdir: <cwd>/AICLI.md or CLAUDE.md (only if cwd ≠ project root)
988
+ */
989
+ loadContextFiles() {
990
+ const parts = [];
991
+ const cwd = process.cwd();
992
+ const configDir = this.config.getConfigDir();
993
+ const globalCtx = this.findContextFile(configDir);
994
+ if (globalCtx) parts.push(globalCtx);
995
+ const gitRoot = getGitRoot(cwd);
996
+ const projectRoot = gitRoot ?? cwd;
997
+ const projectCtx = this.findContextFile(projectRoot);
998
+ if (projectCtx) parts.push(projectCtx);
999
+ if (resolve(cwd) !== resolve(projectRoot)) {
1000
+ const localCtx = this.findContextFile(cwd);
1001
+ if (localCtx) parts.push(localCtx);
1002
+ }
1003
+ return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
978
1004
  }
979
1005
  };
980
1006
 
@@ -988,7 +1014,7 @@ function getModuleDir() {
988
1014
  }
989
1015
  } catch {
990
1016
  }
991
- return resolve(".");
1017
+ return resolve2(".");
992
1018
  }
993
1019
  async function startWebServer(options = {}) {
994
1020
  const port = options.port ?? 3e3;
@@ -996,6 +1022,7 @@ async function startWebServer(options = {}) {
996
1022
  console.log(`\u{1F916} ai-cli Web UI v${VERSION}`);
997
1023
  console.log(` Initializing...`);
998
1024
  const config = new ConfigManager();
1025
+ await setupProxy(config.get("proxy"));
999
1026
  const providers = new ProviderRegistry();
1000
1027
  const sessions = new SessionManager(config);
1001
1028
  const toolRegistry = new ToolRegistry();
@@ -139,20 +139,22 @@ function handleResponseDone(msg) {
139
139
  }
140
140
 
141
141
  function handleToolCallStart(msg) {
142
- const levelClass = msg.dangerLevel === 'destructive' ? 'alert-error'
143
- : msg.dangerLevel === 'write' ? 'alert-warning' : 'alert-info';
142
+ const levelBorder = msg.dangerLevel === 'destructive' ? 'tool-border-destructive'
143
+ : msg.dangerLevel === 'write' ? 'tool-border-write' : 'tool-border-safe';
144
144
  const levelIcon = msg.dangerLevel === 'destructive' ? '⚠'
145
145
  : msg.dangerLevel === 'write' ? '✎' : '⚙';
146
+ const levelBadge = msg.dangerLevel === 'destructive' ? 'badge-error'
147
+ : msg.dangerLevel === 'write' ? 'badge-warning' : 'badge-info';
146
148
 
147
149
  const el = document.createElement('div');
148
150
  el.id = `tool-${msg.callId}`;
149
- el.className = `tool-card alert ${levelClass} flex-col items-start my-1`;
151
+ el.className = `tool-card ${levelBorder} my-1`;
150
152
  el.innerHTML = `
151
- <div class="flex items-center gap-2 w-full">
152
- <span class="font-bold">${levelIcon} ${escapeHtml(msg.toolName)}</span>
153
- <span class="badge badge-ghost badge-sm">${msg.round}/${msg.totalRounds}</span>
153
+ <div class="flex items-center gap-2 w-full mb-1">
154
+ <span class="badge ${levelBadge} badge-sm gap-1">${levelIcon} ${escapeHtml(msg.toolName)}</span>
155
+ <span class="text-xs opacity-50">${msg.round}/${msg.totalRounds}</span>
154
156
  </div>
155
- <div class="tool-args opacity-70 w-full">${formatToolArgs(msg.args)}</div>
157
+ <div class="tool-args w-full">${formatToolArgs(msg.args)}</div>
156
158
  `;
157
159
  messagesEl.appendChild(el);
158
160
  scrollToBottom();
@@ -172,14 +174,17 @@ function handleToolCallResult(msg) {
172
174
  function handleConfirmRequest(msg) {
173
175
  const isDestructive = msg.dangerLevel === 'destructive';
174
176
  const el = document.createElement('div');
175
- el.className = `alert ${isDestructive ? 'alert-error' : 'alert-warning'} flex-col items-start my-1`;
177
+ el.className = `confirm-card ${isDestructive ? 'tool-border-destructive' : 'tool-border-write'} my-1`;
176
178
  el.setAttribute('data-request-id', msg.requestId);
177
179
  el.innerHTML = `
178
- <div class="font-bold text-sm">${isDestructive ? '⚠ DESTRUCTIVE' : '✎ Write'}: ${escapeHtml(msg.toolName)}</div>
180
+ <div class="flex items-center gap-2 mb-2">
181
+ <span class="badge ${isDestructive ? 'badge-error' : 'badge-warning'} badge-sm">${isDestructive ? '⚠ DESTRUCTIVE' : '✎ Write'}</span>
182
+ <span class="text-sm font-semibold">${escapeHtml(msg.toolName)}</span>
183
+ </div>
179
184
  ${msg.diff ? `<div class="confirm-diff w-full">${escapeHtml(msg.diff)}</div>` : ''}
180
- <div class="flex gap-2 mt-1">
181
- <button class="btn btn-success btn-sm" onclick="respondConfirm('${msg.requestId}', true)">✓ Approve</button>
182
- <button class="btn btn-error btn-sm" onclick="respondConfirm('${msg.requestId}', false)">✗ Deny</button>
185
+ <div class="flex gap-2 mt-2">
186
+ <button class="btn btn-success btn-sm btn-outline" onclick="respondConfirm('${msg.requestId}', true)">✓ Approve</button>
187
+ <button class="btn btn-error btn-sm btn-outline" onclick="respondConfirm('${msg.requestId}', false)">✗ Deny</button>
183
188
  </div>
184
189
  `;
185
190
  messagesEl.appendChild(el);
@@ -188,7 +193,7 @@ function handleConfirmRequest(msg) {
188
193
 
189
194
  function handleBatchConfirmRequest(msg) {
190
195
  const el = document.createElement('div');
191
- el.className = 'alert alert-warning flex-col items-start my-1 batch-confirm';
196
+ el.className = 'confirm-card tool-border-write my-1 batch-confirm';
192
197
  el.setAttribute('data-request-id', msg.requestId);
193
198
 
194
199
  const fileListHtml = msg.files.map(f => `
@@ -199,11 +204,14 @@ function handleBatchConfirmRequest(msg) {
199
204
  `).join('');
200
205
 
201
206
  el.innerHTML = `
202
- <div class="font-bold text-sm">✎ Batch file writes (${msg.files.length} files)</div>
207
+ <div class="flex items-center gap-2 mb-2">
208
+ <span class="badge badge-warning badge-sm">✎ Batch writes</span>
209
+ <span class="text-sm">${msg.files.length} files</span>
210
+ </div>
203
211
  <div class="w-full my-1">${fileListHtml}</div>
204
- <div class="flex gap-2 mt-1">
205
- <button class="btn btn-success btn-sm" onclick="respondBatchConfirm('${msg.requestId}', this)">✓ Approve Selected</button>
206
- <button class="btn btn-error btn-sm" onclick="respondBatchDeny('${msg.requestId}', this)">✗ Reject All</button>
212
+ <div class="flex gap-2 mt-2">
213
+ <button class="btn btn-success btn-sm btn-outline" onclick="respondBatchConfirm('${msg.requestId}', this)">✓ Approve Selected</button>
214
+ <button class="btn btn-error btn-sm btn-outline" onclick="respondBatchDeny('${msg.requestId}', this)">✗ Reject All</button>
207
215
  </div>
208
216
  `;
209
217
  messagesEl.appendChild(el);
@@ -212,10 +220,13 @@ function handleBatchConfirmRequest(msg) {
212
220
 
213
221
  function handleAskUserRequest(msg) {
214
222
  const el = document.createElement('div');
215
- el.className = 'alert alert-info flex-col items-start my-1';
223
+ el.className = 'confirm-card tool-border-safe my-1';
216
224
  el.innerHTML = `
217
- <div class="font-bold text-sm">❓ ${escapeHtml(msg.question)}</div>
218
- <div class="flex gap-2 w-full mt-1">
225
+ <div class="flex items-center gap-2 mb-2">
226
+ <span class="badge badge-info badge-sm">❓ Question</span>
227
+ </div>
228
+ <div class="text-sm mb-2">${escapeHtml(msg.question)}</div>
229
+ <div class="flex gap-2 w-full">
219
230
  <input type="text" id="ask-input-${msg.requestId}"
220
231
  class="input input-sm input-bordered flex-1"
221
232
  placeholder="Your answer..."
@@ -287,8 +298,8 @@ window.respondConfirm = function(requestId, approved) {
287
298
  send({ type: 'confirm_response', requestId, approved });
288
299
  const el = document.querySelector(`[data-request-id="${requestId}"]`);
289
300
  if (el) {
290
- el.className = `alert ${approved ? 'alert-success' : 'alert-error'} my-1`;
291
- el.innerHTML = `<span class="text-sm">${approved ? '✓ Approved' : '✗ Denied'}</span>`;
301
+ el.className = `confirm-card ${approved ? 'tool-border-safe' : 'tool-border-destructive'} my-1 opacity-60`;
302
+ el.innerHTML = `<span class="text-sm ${approved ? 'text-success' : 'text-error'}">${approved ? '✓ Approved' : '✗ Denied'}</span>`;
292
303
  }
293
304
  };
294
305
 
@@ -305,24 +316,24 @@ window.respondBatchConfirm = function(requestId, btn) {
305
316
  } else {
306
317
  send({ type: 'batch_confirm_response', requestId, decision: selected });
307
318
  }
308
- dialog.className = 'alert alert-success my-1';
309
- dialog.innerHTML = `<span class="text-sm">✓ Approved ${selected.length}/${checks.length} files</span>`;
319
+ dialog.className = 'confirm-card tool-border-safe my-1 opacity-60';
320
+ dialog.innerHTML = `<span class="text-sm text-success">✓ Approved ${selected.length}/${checks.length} files</span>`;
310
321
  };
311
322
 
312
323
  window.respondBatchDeny = function(requestId, btn) {
313
324
  send({ type: 'batch_confirm_response', requestId, decision: 'none' });
314
325
  const dialog = btn.closest('.batch-confirm');
315
- dialog.className = 'alert alert-error my-1';
316
- dialog.innerHTML = `<span class="text-sm">✗ All rejected</span>`;
326
+ dialog.className = 'confirm-card tool-border-destructive my-1 opacity-60';
327
+ dialog.innerHTML = `<span class="text-sm text-error">✗ All rejected</span>`;
317
328
  };
318
329
 
319
330
  window.respondAskUser = function(requestId) {
320
331
  const input = document.getElementById(`ask-input-${requestId}`);
321
332
  const answer = input ? input.value : '';
322
333
  send({ type: 'ask_user_response', requestId, answer: answer || null });
323
- const dialog = input?.closest('.alert');
334
+ const dialog = input?.closest('.confirm-card');
324
335
  if (dialog) {
325
- dialog.className = 'alert my-1 opacity-60';
336
+ dialog.className = 'confirm-card tool-border-safe my-1 opacity-60';
326
337
  dialog.innerHTML = `<span class="text-sm">↪ ${escapeHtml(answer || '(cancelled)')}</span>`;
327
338
  }
328
339
  };
@@ -360,8 +371,8 @@ function addInfoMessage(text) {
360
371
 
361
372
  function addErrorMessage(text) {
362
373
  const el = document.createElement('div');
363
- el.className = 'alert alert-error text-sm my-1';
364
- el.innerHTML = `<span>${escapeHtml(text)}</span>`;
374
+ el.className = 'error-message my-1';
375
+ el.innerHTML = `<span class="text-error text-sm">⚠ ${escapeHtml(text)}</span>`;
365
376
  messagesEl.appendChild(el);
366
377
  scrollToBottom();
367
378
  }
@@ -110,16 +110,25 @@
110
110
  50% { opacity: 0; }
111
111
  }
112
112
 
113
- /* ── Tool call cards ────────────────────────────────── */
113
+ /* ── Tool call cards (base-colored with left border) ── */
114
114
  .tool-card {
115
+ background: oklch(var(--b2));
116
+ border-radius: 0.5rem;
117
+ padding: 0.75rem 1rem;
115
118
  font-size: 0.85rem;
119
+ border-left: 3px solid transparent;
116
120
  }
121
+ .tool-border-safe { border-left-color: oklch(var(--in)); }
122
+ .tool-border-write { border-left-color: oklch(var(--wa)); }
123
+ .tool-border-destructive { border-left-color: oklch(var(--er)); }
124
+
117
125
  .tool-card .tool-args {
118
126
  white-space: pre-wrap;
119
127
  max-height: 100px;
120
128
  overflow-y: auto;
121
129
  font-size: 0.8rem;
122
- opacity: 0.7;
130
+ opacity: 0.65;
131
+ line-height: 1.5;
123
132
  }
124
133
  .tool-card .tool-result-content {
125
134
  white-space: pre-wrap;
@@ -128,12 +137,20 @@
128
137
  font-size: 0.8rem;
129
138
  }
130
139
 
131
- /* ── Confirm dialog ─────────────────────────────────── */
140
+ /* ── Confirm / ask-user card ────────────────────────── */
141
+ .confirm-card {
142
+ background: oklch(var(--b2));
143
+ border-radius: 0.5rem;
144
+ padding: 0.75rem 1rem;
145
+ border-left: 3px solid transparent;
146
+ }
147
+
132
148
  .confirm-diff {
133
149
  background: oklch(var(--b3));
150
+ color: oklch(var(--bc));
134
151
  padding: 0.5rem;
135
152
  border-radius: 0.375rem;
136
- font-family: 'Fira Code', 'Consolas', monospace;
153
+ font-family: 'Fira Code', 'JetBrains Mono', 'Consolas', monospace;
137
154
  font-size: 0.78rem;
138
155
  white-space: pre-wrap;
139
156
  max-height: 200px;
@@ -141,6 +158,14 @@
141
158
  margin-bottom: 0.5rem;
142
159
  }
143
160
 
161
+ /* ── Error message ──────────────────────────────────── */
162
+ .error-message {
163
+ background: oklch(var(--er) / 0.1);
164
+ border-left: 3px solid oklch(var(--er));
165
+ border-radius: 0.5rem;
166
+ padding: 0.5rem 1rem;
167
+ }
168
+
144
169
  /* ── Thinking block ─────────────────────────────────── */
145
170
  .thinking-block summary {
146
171
  cursor: pointer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",