jinzd-ai-cli 0.4.107 → 0.4.109

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
@@ -422,6 +422,12 @@ npm run test:watch # Watch mode
422
422
 
423
423
  ## Documentation
424
424
 
425
+ - [`docs/USAGE.md`](docs/USAGE.md) — Complete reference manual (commands, tools, config)
426
+ - [`docs/TUTORIAL.md`](docs/TUTORIAL.md) — Hands-on tutorial, zero to fluent in an hour
427
+ - [`docs/ADVANCED.md`](docs/ADVANCED.md) — Architecture and internals (for developers)
428
+ - [`docs/RECIPES.md`](docs/RECIPES.md) — Practical recipes by scenario
429
+ - [`docs/SECURITY.md`](docs/SECURITY.md) — **Security model, deployment checklist, audit history.** Read before exposing `aicli web` to a network.
430
+ - [`CHANGELOG.md`](CHANGELOG.md) — Per-version change log
425
431
  - [Chinese README](README.zh-CN.md) — 中文说明文档
426
432
 
427
433
  ## License
package/README.zh-CN.md CHANGED
@@ -184,6 +184,8 @@ AI 在对话中可自主调用 28 个工具:
184
184
  - [`docs/USAGE.zh-CN.md`](docs/USAGE.zh-CN.md) — 完整参考手册(所有命令、工具、配置)
185
185
  - [`docs/ADVANCED.zh-CN.md`](docs/ADVANCED.zh-CN.md) — 架构与内部原理(开发者向)
186
186
  - [`docs/RECIPES.zh-CN.md`](docs/RECIPES.zh-CN.md) — 实战配方(按场景查)
187
+ - [`docs/SECURITY.zh-CN.md`](docs/SECURITY.zh-CN.md) — **安全模型、部署清单、审计历史。** 把 `aicli web` 暴露到网络前请先读。
188
+ - [`CHANGELOG.md`](CHANGELOG.md) — 每个版本的更新记录
187
189
 
188
190
  ## 主要 REPL 命令
189
191
 
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-7J2GXVS5.js";
4
+ } from "./chunk-JLHY6AOZ.js";
5
5
  import "./chunk-2ZD3YTVM.js";
6
- import "./chunk-76UBRJUT.js";
6
+ import "./chunk-LZR3DYW3.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
 
9
9
  // src/cli/batch.ts
@@ -6,7 +6,7 @@ import {
6
6
  getChatIndexStatus,
7
7
  loadChatIndex,
8
8
  searchChatMemory
9
- } from "./chunk-ANYYM4CF.js";
9
+ } from "./chunk-7ZJN4KLV.js";
10
10
  import "./chunk-KHYD3WXE.js";
11
11
  import "./chunk-PDX44BCA.js";
12
12
  export {
@@ -5,7 +5,7 @@ import {
5
5
  getChatIndexStatus,
6
6
  loadChatIndex,
7
7
  searchChatMemory
8
- } from "./chunk-5S3PIG5O.js";
8
+ } from "./chunk-WJ32NAW3.js";
9
9
  import "./chunk-JV5N65KN.js";
10
10
  import "./chunk-3RG5ZIWI.js";
11
11
  export {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  schemaToJsonSchema,
4
4
  truncateForPersist
5
- } from "./chunk-QJ42A4GF.js";
5
+ } from "./chunk-N62X6OKJ.js";
6
6
  import {
7
7
  AuthError,
8
8
  ProviderError,
@@ -18,10 +18,10 @@ import {
18
18
  MCP_PROTOCOL_VERSION,
19
19
  MCP_TOOL_PREFIX,
20
20
  VERSION
21
- } from "./chunk-76UBRJUT.js";
21
+ } from "./chunk-LZR3DYW3.js";
22
22
  import {
23
23
  redactJson
24
- } from "./chunk-ANYYM4CF.js";
24
+ } from "./chunk-7ZJN4KLV.js";
25
25
 
26
26
  // src/providers/claude.ts
27
27
  import Anthropic from "@anthropic-ai/sdk";
@@ -23,6 +23,11 @@ var DEFAULT_PATTERNS = [
23
23
  { kind: "db-uri-password", regex: /(\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^:\s]+:)([^@\s]+)(@)/gi },
24
24
  // Anthropic API keys
25
25
  { kind: "anthropic-key", regex: /(sk-ant-[a-zA-Z0-9_-]{90,})/g },
26
+ // L6 (v0.4.108): Zhipu / GLM API keys — `<24+ hex/base64-ish>.<32+>`
27
+ // Two segments separated by a dot, each safely identifiable by length
28
+ // and char class. Conservative on the lower bound so we don't eat
29
+ // version strings like `1.0.0` or filenames.
30
+ { kind: "zhipu-key", regex: /\b([a-zA-Z0-9]{24,}\.[a-zA-Z0-9]{32,})\b/g },
26
31
  // OpenAI / generic sk- keys — requires length ≥32 to avoid eating short identifiers
27
32
  { kind: "openai-key", regex: /(sk-(?:proj-)?[a-zA-Z0-9_-]{32,})/g },
28
33
  // GitHub personal access tokens
@@ -46,18 +51,24 @@ var DEFAULT_PATTERNS = [
46
51
  // Private key PEM blocks — catch the header+footer together
47
52
  { kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
48
53
  ];
54
+ var MAX_CUSTOM = 32;
55
+ var MAX_PATTERN_LEN = 500;
56
+ var SUSPICIOUS_REDOS = /\([^)]*[+*][^)]*\)\s*[+*{]/;
49
57
  function render(placeholder, kind) {
50
58
  return placeholder.replace("{kind}", kind);
51
59
  }
52
60
  function redactString(input, options) {
53
61
  if (!options.enabled || !input) return { redacted: input, hits: [] };
54
62
  const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
63
+ const customSrcs = (options.customRegexes ?? []).slice(0, MAX_CUSTOM);
55
64
  const patterns = [
56
65
  ...options.patterns ?? DEFAULT_PATTERNS,
57
- ...(options.customRegexes ?? []).flatMap((src, i) => {
66
+ ...customSrcs.flatMap((src, i) => {
67
+ if (typeof src !== "string" || src.length === 0 || src.length > MAX_PATTERN_LEN) return [];
58
68
  try {
59
69
  const flags = src.match(/^\/.*\/([gimsuy]*)$/)?.[1] ?? "";
60
70
  const body = src.replace(/^\/(.*)\/[gimsuy]*$/, "$1");
71
+ if (SUSPICIOUS_REDOS.test(body)) return [];
61
72
  const regex = new RegExp(body, flags.includes("g") ? flags : flags + "g");
62
73
  return [{ kind: `custom-${i}`, regex }];
63
74
  } catch {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-76UBRJUT.js";
4
+ } from "./chunk-LZR3DYW3.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } 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-76UBRJUT.js";
11
+ } from "./chunk-LZR3DYW3.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
 
3
3
  // src/core/constants.ts
4
- var VERSION = "0.4.107";
4
+ var VERSION = "0.4.109";
5
5
  var APP_NAME = "ai-cli";
6
6
  var CONFIG_DIR_NAME = ".aicli";
7
7
  var CONFIG_FILE_NAME = "config.json";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-3BICTI5M.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-7JPMYNVW.js";
8
+ } from "./chunk-CUQ4HXHA.js";
9
9
  import {
10
10
  EnvLoader,
11
11
  NetworkError,
@@ -18,14 +18,14 @@ import {
18
18
  SUBAGENT_ALLOWED_TOOLS,
19
19
  SUBAGENT_DEFAULT_MAX_ROUNDS,
20
20
  SUBAGENT_MAX_ROUNDS_LIMIT
21
- } from "./chunk-76UBRJUT.js";
21
+ } from "./chunk-LZR3DYW3.js";
22
22
  import {
23
23
  fileCheckpoints
24
24
  } from "./chunk-4BKXL7SM.js";
25
25
  import {
26
26
  loadChatIndex,
27
27
  searchChatMemory
28
- } from "./chunk-ANYYM4CF.js";
28
+ } from "./chunk-7ZJN4KLV.js";
29
29
  import {
30
30
  indexProject
31
31
  } from "./chunk-NHNWUBXB.js";
@@ -667,6 +667,20 @@ function createCell(cellType, content) {
667
667
 
668
668
  // src/tools/builtin/read-file.ts
669
669
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
670
+ function getHardRefusal(normalizedPath) {
671
+ const home2 = homedir();
672
+ const p = normalizedPath.toLowerCase();
673
+ const base = basename(normalizedPath).toLowerCase();
674
+ if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
675
+ return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
676
+ If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
677
+ For programmatic access, use \`aicli config\` or \`aicli config get <key>\` which never expose raw keys.`;
678
+ }
679
+ if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "users.json") {
680
+ return `[Refused] Reading ${normalizedPath} is blocked because it contains password hashes and the token-signing secret.`;
681
+ }
682
+ return null;
683
+ }
670
684
  function getSensitiveWarning(normalizedPath) {
671
685
  const home2 = homedir();
672
686
  const p = normalizedPath.toLowerCase();
@@ -819,6 +833,8 @@ var readFileTool = {
819
833
  const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
820
834
  if (!filePath) throw new ToolError("read_file", "path is required");
821
835
  const normalizedPath = resolve2(filePath);
836
+ const refusal = getHardRefusal(normalizedPath);
837
+ if (refusal) return refusal;
822
838
  if (!existsSync3(normalizedPath)) {
823
839
  const suggestions = findSimilarFiles(filePath);
824
840
  if (suggestions.length > 0) {
@@ -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.107";
9
+ var VERSION = "0.4.109";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
@@ -22,6 +22,11 @@ var DEFAULT_PATTERNS = [
22
22
  { kind: "db-uri-password", regex: /(\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp|mssql):\/\/[^:\s]+:)([^@\s]+)(@)/gi },
23
23
  // Anthropic API keys
24
24
  { kind: "anthropic-key", regex: /(sk-ant-[a-zA-Z0-9_-]{90,})/g },
25
+ // L6 (v0.4.108): Zhipu / GLM API keys — `<24+ hex/base64-ish>.<32+>`
26
+ // Two segments separated by a dot, each safely identifiable by length
27
+ // and char class. Conservative on the lower bound so we don't eat
28
+ // version strings like `1.0.0` or filenames.
29
+ { kind: "zhipu-key", regex: /\b([a-zA-Z0-9]{24,}\.[a-zA-Z0-9]{32,})\b/g },
25
30
  // OpenAI / generic sk- keys — requires length ≥32 to avoid eating short identifiers
26
31
  { kind: "openai-key", regex: /(sk-(?:proj-)?[a-zA-Z0-9_-]{32,})/g },
27
32
  // GitHub personal access tokens
@@ -45,18 +50,24 @@ var DEFAULT_PATTERNS = [
45
50
  // Private key PEM blocks — catch the header+footer together
46
51
  { kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
47
52
  ];
53
+ var MAX_CUSTOM = 32;
54
+ var MAX_PATTERN_LEN = 500;
55
+ var SUSPICIOUS_REDOS = /\([^)]*[+*][^)]*\)\s*[+*{]/;
48
56
  function render(placeholder, kind) {
49
57
  return placeholder.replace("{kind}", kind);
50
58
  }
51
59
  function redactString(input, options) {
52
60
  if (!options.enabled || !input) return { redacted: input, hits: [] };
53
61
  const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
62
+ const customSrcs = (options.customRegexes ?? []).slice(0, MAX_CUSTOM);
54
63
  const patterns = [
55
64
  ...options.patterns ?? DEFAULT_PATTERNS,
56
- ...(options.customRegexes ?? []).flatMap((src, i) => {
65
+ ...customSrcs.flatMap((src, i) => {
66
+ if (typeof src !== "string" || src.length === 0 || src.length > MAX_PATTERN_LEN) return [];
57
67
  try {
58
68
  const flags = src.match(/^\/.*\/([gimsuy]*)$/)?.[1] ?? "";
59
69
  const body = src.replace(/^\/(.*)\/[gimsuy]*$/, "$1");
70
+ if (SUSPICIOUS_REDOS.test(body)) return [];
60
71
  const regex = new RegExp(body, flags.includes("g") ? flags : flags + "g");
61
72
  return [{ kind: `custom-${i}`, regex }];
62
73
  } catch {
@@ -36,7 +36,7 @@ import {
36
36
  TEST_TIMEOUT,
37
37
  VERSION,
38
38
  buildUserIdentityPrompt
39
- } from "./chunk-76UBRJUT.js";
39
+ } from "./chunk-LZR3DYW3.js";
40
40
  import "./chunk-PDX44BCA.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -36,7 +36,7 @@ import {
36
36
  VERSION,
37
37
  buildUserIdentityPrompt,
38
38
  runTestsTool
39
- } from "./chunk-PXTMQ4TC.js";
39
+ } from "./chunk-OKRTYLB6.js";
40
40
  import {
41
41
  hasSemanticIndex,
42
42
  semanticSearch
@@ -49,7 +49,7 @@ import {
49
49
  loadChatIndex,
50
50
  redactJson,
51
51
  searchChatMemory
52
- } from "./chunk-5S3PIG5O.js";
52
+ } from "./chunk-WJ32NAW3.js";
53
53
  import "./chunk-JV5N65KN.js";
54
54
  import "./chunk-3RG5ZIWI.js";
55
55
 
@@ -4073,6 +4073,20 @@ function createCell(cellType, content) {
4073
4073
 
4074
4074
  // src/tools/builtin/read-file.ts
4075
4075
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
4076
+ function getHardRefusal(normalizedPath) {
4077
+ const home2 = homedir2();
4078
+ const p = normalizedPath.toLowerCase();
4079
+ const base = basename(normalizedPath).toLowerCase();
4080
+ if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "config.json") {
4081
+ return `[Refused] Reading ${normalizedPath} is blocked because it contains all of your provider API keys.
4082
+ If you genuinely need to inspect or edit it, open it manually outside ai-cli (e.g. via your editor).
4083
+ For programmatic access, use \`aicli config\` or \`aicli config get <key>\` which never expose raw keys.`;
4084
+ }
4085
+ if (normalizedPath.startsWith(home2) && p.includes(".aicli") && base === "users.json") {
4086
+ return `[Refused] Reading ${normalizedPath} is blocked because it contains password hashes and the token-signing secret.`;
4087
+ }
4088
+ return null;
4089
+ }
4076
4090
  function getSensitiveWarning(normalizedPath) {
4077
4091
  const home2 = homedir2();
4078
4092
  const p = normalizedPath.toLowerCase();
@@ -4225,6 +4239,8 @@ var readFileTool = {
4225
4239
  const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
4226
4240
  if (!filePath) throw new ToolError("read_file", "path is required");
4227
4241
  const normalizedPath = resolve2(filePath);
4242
+ const refusal = getHardRefusal(normalizedPath);
4243
+ if (refusal) return refusal;
4228
4244
  if (!existsSync5(normalizedPath)) {
4229
4245
  const suggestions = findSimilarFiles(filePath);
4230
4246
  if (suggestions.length > 0) {
@@ -11773,7 +11789,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
11773
11789
  case "test": {
11774
11790
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
11775
11791
  try {
11776
- const { executeTests } = await import("./run-tests-GMO7E3UK.js");
11792
+ const { executeTests } = await import("./run-tests-TTP4QJVN.js");
11777
11793
  const argStr = args.join(" ").trim();
11778
11794
  let testArgs = {};
11779
11795
  if (argStr) {
@@ -12297,7 +12313,7 @@ Add .md files to create commands.` });
12297
12313
  return;
12298
12314
  }
12299
12315
  try {
12300
- const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-ADG2GPCC.js");
12316
+ const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-MY3DJTV3.js");
12301
12317
  const loaded = loadChatIndex2();
12302
12318
  if (!loaded || loaded.idx.chunks.length === 0) {
12303
12319
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -12333,7 +12349,7 @@ Add .md files to create commands.` });
12333
12349
  }
12334
12350
  async handleMemoryStatus() {
12335
12351
  try {
12336
- const { getChatIndexStatus } = await import("./chat-index-ADG2GPCC.js");
12352
+ const { getChatIndexStatus } = await import("./chat-index-MY3DJTV3.js");
12337
12353
  const s = getChatIndexStatus();
12338
12354
  this.send({
12339
12355
  type: "memory_status",
@@ -12358,7 +12374,7 @@ Add .md files to create commands.` });
12358
12374
  type: "info",
12359
12375
  message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
12360
12376
  });
12361
- const { buildChatIndex } = await import("./chat-index-ADG2GPCC.js");
12377
+ const { buildChatIndex } = await import("./chat-index-MY3DJTV3.js");
12362
12378
  const stats = await buildChatIndex({
12363
12379
  full,
12364
12380
  onProgress: (p) => {
@@ -13539,10 +13555,14 @@ function parseCookie(cookie) {
13539
13555
  }
13540
13556
  async function openBrowser(url) {
13541
13557
  try {
13542
- const { exec } = await import("child_process");
13543
- const cmd = process.platform === "win32" ? `start ${url}` : process.platform === "darwin" ? `open ${url}` : `xdg-open ${url}`;
13544
- exec(cmd, () => {
13545
- });
13558
+ const { spawn: spawn5 } = await import("child_process");
13559
+ if (process.platform === "win32") {
13560
+ spawn5("cmd", ["/c", "start", '""', url], { detached: true, stdio: "ignore" }).unref();
13561
+ } else if (process.platform === "darwin") {
13562
+ spawn5("open", [url], { detached: true, stdio: "ignore" }).unref();
13563
+ } else {
13564
+ spawn5("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
13565
+ }
13546
13566
  } catch {
13547
13567
  }
13548
13568
  }
@@ -386,7 +386,7 @@ ${content}`);
386
386
  }
387
387
  }
388
388
  async function runTaskMode(config, providers, configManager, topic) {
389
- const { TaskOrchestrator } = await import("./task-orchestrator-FAQWRMJH.js");
389
+ const { TaskOrchestrator } = await import("./task-orchestrator-XSNTJYNR.js");
390
390
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
391
391
  let interrupted = false;
392
392
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -25,10 +25,10 @@ import {
25
25
  saveDevState,
26
26
  sessionHasMeaningfulContent,
27
27
  setupProxy
28
- } from "./chunk-YMFTF57T.js";
28
+ } from "./chunk-3UJIJBKB.js";
29
29
  import {
30
30
  ConfigManager
31
- } from "./chunk-7J2GXVS5.js";
31
+ } from "./chunk-JLHY6AOZ.js";
32
32
  import {
33
33
  ToolExecutor,
34
34
  ToolRegistry,
@@ -47,10 +47,10 @@ import {
47
47
  spawnAgentContext,
48
48
  theme,
49
49
  undoStack
50
- } from "./chunk-QJ42A4GF.js";
50
+ } from "./chunk-N62X6OKJ.js";
51
51
  import "./chunk-3BICTI5M.js";
52
52
  import "./chunk-2DXY7UGF.js";
53
- import "./chunk-7JPMYNVW.js";
53
+ import "./chunk-CUQ4HXHA.js";
54
54
  import "./chunk-2ZD3YTVM.js";
55
55
  import {
56
56
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -73,7 +73,7 @@ import {
73
73
  SKILLS_DIR_NAME,
74
74
  VERSION,
75
75
  buildUserIdentityPrompt
76
- } from "./chunk-76UBRJUT.js";
76
+ } from "./chunk-LZR3DYW3.js";
77
77
  import {
78
78
  formatGitContextForPrompt,
79
79
  getGitContext,
@@ -89,7 +89,7 @@ import {
89
89
  getChatIndexStatus,
90
90
  scanString,
91
91
  searchChatMemory
92
- } from "./chunk-ANYYM4CF.js";
92
+ } from "./chunk-7ZJN4KLV.js";
93
93
  import "./chunk-KHYD3WXE.js";
94
94
  import "./chunk-NHNWUBXB.js";
95
95
  import "./chunk-6VRJGH25.js";
@@ -1594,7 +1594,7 @@ ${text}
1594
1594
  const { join: join6 } = await import("path");
1595
1595
  const { existsSync: existsSync6 } = await import("fs");
1596
1596
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1597
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-G7IWRPVI.js");
1597
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-K52FVIX5.js");
1598
1598
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1599
1599
  const cwd = process.cwd();
1600
1600
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2644,7 +2644,7 @@ ${hint}` : "")
2644
2644
  usage: "/test [command|filter]",
2645
2645
  async execute(args, ctx) {
2646
2646
  try {
2647
- const { executeTests } = await import("./run-tests-3JWA5IJ2.js");
2647
+ const { executeTests } = await import("./run-tests-ZBDJYOB2.js");
2648
2648
  const argStr = args.join(" ").trim();
2649
2649
  let testArgs = {};
2650
2650
  if (argStr) {
@@ -5068,7 +5068,7 @@ Session '${this.resumeSessionId}' not found.
5068
5068
  })();
5069
5069
  void (async () => {
5070
5070
  try {
5071
- const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-7OHUKJY5.js");
5071
+ const { getChatIndexStatus: getChatIndexStatus2, buildChatIndex: buildChatIndex2 } = await import("./chat-index-JBF4ZIQI.js");
5072
5072
  const initial = getChatIndexStatus2();
5073
5073
  this.chatMemoryStatus = {
5074
5074
  exists: initial.exists,
@@ -6800,7 +6800,7 @@ program.command("web").description("Start Web UI server with browser-based chat
6800
6800
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
6801
6801
  process.exit(1);
6802
6802
  }
6803
- const { startWebServer } = await import("./server-RTABJZZZ.js");
6803
+ const { startWebServer } = await import("./server-VDMQC43J.js");
6804
6804
  await startWebServer({ port, host: options.host });
6805
6805
  });
6806
6806
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -6923,7 +6923,7 @@ program.command("sessions").description("List recent conversation sessions").act
6923
6923
  });
6924
6924
  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) => {
6925
6925
  try {
6926
- const batch = await import("./batch-QNHPXV6U.js");
6926
+ const batch = await import("./batch-BFOFTLFS.js");
6927
6927
  switch (action) {
6928
6928
  case "submit":
6929
6929
  if (!arg) {
@@ -6966,7 +6966,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
6966
6966
  }
6967
6967
  });
6968
6968
  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) => {
6969
- const { startMcpServer } = await import("./server-2BKTE6Z4.js");
6969
+ const { startMcpServer } = await import("./server-LEPDI3OL.js");
6970
6970
  await startMcpServer({
6971
6971
  allowDestructive: !!options.allowDestructive,
6972
6972
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7093,7 +7093,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7093
7093
  }),
7094
7094
  config.get("customProviders")
7095
7095
  );
7096
- const { startHub } = await import("./hub-GBL3BURO.js");
7096
+ const { startHub } = await import("./hub-4XJ5ZTM4.js");
7097
7097
  await startHub(
7098
7098
  {
7099
7099
  topic: topic ?? "",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-PXTMQ4TC.js";
4
+ } from "./chunk-OKRTYLB6.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-7JPMYNVW.js";
6
- import "./chunk-76UBRJUT.js";
5
+ } from "./chunk-CUQ4HXHA.js";
6
+ import "./chunk-LZR3DYW3.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -3,16 +3,16 @@ import {
3
3
  ToolRegistry,
4
4
  getDangerLevel,
5
5
  schemaToJsonSchema
6
- } from "./chunk-QJ42A4GF.js";
6
+ } from "./chunk-N62X6OKJ.js";
7
7
  import "./chunk-3BICTI5M.js";
8
8
  import "./chunk-2DXY7UGF.js";
9
- import "./chunk-7JPMYNVW.js";
9
+ import "./chunk-CUQ4HXHA.js";
10
10
  import "./chunk-2ZD3YTVM.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-76UBRJUT.js";
13
+ } from "./chunk-LZR3DYW3.js";
14
14
  import "./chunk-4BKXL7SM.js";
15
- import "./chunk-ANYYM4CF.js";
15
+ import "./chunk-7ZJN4KLV.js";
16
16
  import "./chunk-KHYD3WXE.js";
17
17
  import "./chunk-NHNWUBXB.js";
18
18
  import "./chunk-6VRJGH25.js";
@@ -18,10 +18,10 @@ import {
18
18
  loadDevState,
19
19
  persistToolRound,
20
20
  setupProxy
21
- } from "./chunk-YMFTF57T.js";
21
+ } from "./chunk-3UJIJBKB.js";
22
22
  import {
23
23
  ConfigManager
24
- } from "./chunk-7J2GXVS5.js";
24
+ } from "./chunk-JLHY6AOZ.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-QJ42A4GF.js";
42
+ } from "./chunk-N62X6OKJ.js";
43
43
  import "./chunk-3BICTI5M.js";
44
44
  import "./chunk-2DXY7UGF.js";
45
- import "./chunk-7JPMYNVW.js";
45
+ import "./chunk-CUQ4HXHA.js";
46
46
  import "./chunk-2ZD3YTVM.js";
47
47
  import {
48
48
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -62,14 +62,14 @@ import {
62
62
  SKILLS_DIR_NAME,
63
63
  VERSION,
64
64
  buildUserIdentityPrompt
65
- } from "./chunk-76UBRJUT.js";
65
+ } from "./chunk-LZR3DYW3.js";
66
66
  import {
67
67
  formatGitContextForPrompt,
68
68
  getGitContext,
69
69
  getGitRoot
70
70
  } from "./chunk-HOSJZMQS.js";
71
71
  import "./chunk-4BKXL7SM.js";
72
- import "./chunk-ANYYM4CF.js";
72
+ import "./chunk-7ZJN4KLV.js";
73
73
  import "./chunk-KHYD3WXE.js";
74
74
  import "./chunk-NHNWUBXB.js";
75
75
  import "./chunk-6VRJGH25.js";
@@ -2383,7 +2383,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2383
2383
  case "test": {
2384
2384
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2385
2385
  try {
2386
- const { executeTests } = await import("./run-tests-3JWA5IJ2.js");
2386
+ const { executeTests } = await import("./run-tests-ZBDJYOB2.js");
2387
2387
  const argStr = args.join(" ").trim();
2388
2388
  let testArgs = {};
2389
2389
  if (argStr) {
@@ -2907,7 +2907,7 @@ Add .md files to create commands.` });
2907
2907
  return;
2908
2908
  }
2909
2909
  try {
2910
- const { searchChatMemory, loadChatIndex } = await import("./chat-index-7OHUKJY5.js");
2910
+ const { searchChatMemory, loadChatIndex } = await import("./chat-index-JBF4ZIQI.js");
2911
2911
  const loaded = loadChatIndex();
2912
2912
  if (!loaded || loaded.idx.chunks.length === 0) {
2913
2913
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -2943,7 +2943,7 @@ Add .md files to create commands.` });
2943
2943
  }
2944
2944
  async handleMemoryStatus() {
2945
2945
  try {
2946
- const { getChatIndexStatus } = await import("./chat-index-7OHUKJY5.js");
2946
+ const { getChatIndexStatus } = await import("./chat-index-JBF4ZIQI.js");
2947
2947
  const s = getChatIndexStatus();
2948
2948
  this.send({
2949
2949
  type: "memory_status",
@@ -2968,7 +2968,7 @@ Add .md files to create commands.` });
2968
2968
  type: "info",
2969
2969
  message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
2970
2970
  });
2971
- const { buildChatIndex } = await import("./chat-index-7OHUKJY5.js");
2971
+ const { buildChatIndex } = await import("./chat-index-JBF4ZIQI.js");
2972
2972
  const stats = await buildChatIndex({
2973
2973
  full,
2974
2974
  onProgress: (p) => {
@@ -3927,10 +3927,14 @@ function parseCookie(cookie) {
3927
3927
  }
3928
3928
  async function openBrowser(url) {
3929
3929
  try {
3930
- const { exec } = await import("child_process");
3931
- const cmd = process.platform === "win32" ? `start ${url}` : process.platform === "darwin" ? `open ${url}` : `xdg-open ${url}`;
3932
- exec(cmd, () => {
3933
- });
3930
+ const { spawn } = await import("child_process");
3931
+ if (process.platform === "win32") {
3932
+ spawn("cmd", ["/c", "start", '""', url], { detached: true, stdio: "ignore" }).unref();
3933
+ } else if (process.platform === "darwin") {
3934
+ spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
3935
+ } else {
3936
+ spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
3937
+ }
3934
3938
  } catch {
3935
3939
  }
3936
3940
  }
@@ -4,16 +4,16 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-QJ42A4GF.js";
7
+ } from "./chunk-N62X6OKJ.js";
8
8
  import "./chunk-3BICTI5M.js";
9
9
  import "./chunk-2DXY7UGF.js";
10
- import "./chunk-7JPMYNVW.js";
10
+ import "./chunk-CUQ4HXHA.js";
11
11
  import "./chunk-2ZD3YTVM.js";
12
12
  import {
13
13
  SUBAGENT_ALLOWED_TOOLS
14
- } from "./chunk-76UBRJUT.js";
14
+ } from "./chunk-LZR3DYW3.js";
15
15
  import "./chunk-4BKXL7SM.js";
16
- import "./chunk-ANYYM4CF.js";
16
+ import "./chunk-7ZJN4KLV.js";
17
17
  import "./chunk-KHYD3WXE.js";
18
18
  import "./chunk-NHNWUBXB.js";
19
19
  import "./chunk-6VRJGH25.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.107",
3
+ "version": "0.4.109",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",