jinzd-ai-cli 0.4.13 → 0.4.15

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
@@ -34,7 +34,7 @@
34
34
  - **PWA Support** — Install Web UI as a desktop/mobile app, accessible over LAN
35
35
  - **Hierarchical Context** — 3-layer context files (global / project / subdirectory) auto-injected
36
36
  - **Headless Mode** — `ai-cli -p "prompt"` for CI/CD pipelines and scripting
37
- - **35+ REPL Commands** — Session management, checkpointing, code review, scaffolding, and more
37
+ - **37 REPL Commands** — Session management, checkpointing, code review, scaffolding, and more
38
38
  - **GitHub Actions CI/CD** — Automated testing on Node 20/22 + npm publish on release tags
39
39
  - **Cross-Platform** — Windows, macOS, Linux
40
40
 
@@ -172,7 +172,7 @@ AI autonomously invokes these 16 tools during conversations:
172
172
 
173
173
  **Multi-line input**: Use `\` at end of line for continuation, or paste multi-line content directly (auto-detected and merged).
174
174
 
175
- See the [full command reference](USAGE.md) for all 35+ commands.
175
+ Type `/help` in the REPL to see all 37 commands.
176
176
 
177
177
  ## CLI Parameters
178
178
 
@@ -356,7 +356,6 @@ npm run test:watch # Watch mode
356
356
 
357
357
  ## Documentation
358
358
 
359
- - [Full Usage Guide](USAGE.md) — Comprehensive reference for all features
360
359
  - [Chinese README](README.zh-CN.md) — 中文说明文档
361
360
 
362
361
  ## License
package/README.zh-CN.md CHANGED
@@ -26,7 +26,7 @@
26
26
  - **PWA 支持** — Web UI 可安装为桌面/移动应用,支持局域网访问
27
27
  - **三层级上下文** — 全局 / 项目 / 子目录上下文文件自动注入
28
28
  - **无头模式** — `aicli -p "提示词"` 用于 CI/CD 管道和脚本
29
- - **35+ REPL 命令** — 会话管理、检查点、代码审查、脚手架等
29
+ - **37 REPL 命令** — 会话管理、检查点、代码审查、脚手架等
30
30
  - **GitHub Actions CI/CD** — Node 20/22 自动测试 + Release tag 自动发布 npm
31
31
  - **跨平台** — Windows、macOS、Linux
32
32
 
@@ -164,7 +164,7 @@ AI 在对话中可自主调用 16 个工具:
164
164
 
165
165
  **多行输入**:行末加 `\` 续行,或直接粘贴多行内容(自动检测合并)。
166
166
 
167
- 完整命令参考见 [使用说明](USAGE.md)。
167
+ REPL 中输入 `/help` 查看全部 37 个命令。
168
168
 
169
169
  ## CLI 参数
170
170
 
@@ -369,7 +369,6 @@ npm run test:watch # 监听模式
369
369
 
370
370
  ## 文档
371
371
 
372
- - [完整使用说明](USAGE.md) — 所有功能的详细参考
373
372
  - [English README](README.md) — English documentation
374
373
 
375
374
  ## License
@@ -6,7 +6,7 @@ import {
6
6
  SUBAGENT_DEFAULT_MAX_ROUNDS,
7
7
  SUBAGENT_MAX_ROUNDS_LIMIT,
8
8
  runTestsTool
9
- } from "./chunk-DP3M26PP.js";
9
+ } from "./chunk-KOD3C2CU.js";
10
10
 
11
11
  // src/tools/builtin/bash.ts
12
12
  import { execSync } from "child_process";
@@ -1854,10 +1854,6 @@ var EnvLoader = class {
1854
1854
  */
1855
1855
  static getApiKey(providerId) {
1856
1856
  const fixedEnvVar = ENV_KEY_MAP[providerId];
1857
- if (fixedEnvVar) {
1858
- const val = process.env[fixedEnvVar];
1859
- if (val) return val;
1860
- }
1861
1857
  const dynamicEnvVar = `AICLI_API_KEY_${providerId.toUpperCase().replace(/-/g, "_")}`;
1862
1858
  if (fixedEnvVar && fixedEnvVar !== dynamicEnvVar) {
1863
1859
  const fixedVal = process.env[fixedEnvVar];
@@ -1867,6 +1863,10 @@ var EnvLoader = class {
1867
1863
  `);
1868
1864
  }
1869
1865
  }
1866
+ if (fixedEnvVar) {
1867
+ const val = process.env[fixedEnvVar];
1868
+ if (val) return val;
1869
+ }
1870
1870
  return process.env[dynamicEnvVar] || void 0;
1871
1871
  }
1872
1872
  static getDefaultProvider() {
@@ -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.4.13";
11
+ var VERSION = "0.4.15";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  EnvLoader,
4
4
  schemaToJsonSchema
5
- } from "./chunk-TXCLZ72H.js";
5
+ } from "./chunk-HOOMQACE.js";
6
6
  import {
7
7
  APP_NAME,
8
8
  CONFIG_DIR_NAME,
@@ -15,7 +15,7 @@ import {
15
15
  MCP_TOOL_PREFIX,
16
16
  PLUGINS_DIR_NAME,
17
17
  VERSION
18
- } from "./chunk-DP3M26PP.js";
18
+ } from "./chunk-KOD3C2CU.js";
19
19
 
20
20
  // src/config/config-manager.ts
21
21
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
@@ -6,13 +6,14 @@ import { platform } from "os";
6
6
  import chalk from "chalk";
7
7
 
8
8
  // src/core/constants.ts
9
- var VERSION = "0.4.13";
9
+ var VERSION = "0.4.15";
10
10
  var APP_NAME = "ai-cli";
11
11
  var CONFIG_DIR_NAME = ".aicli";
12
12
  var CONFIG_FILE_NAME = "config.json";
13
13
  var HISTORY_DIR_NAME = "history";
14
14
  var PLUGINS_DIR_NAME = "plugins";
15
15
  var SKILLS_DIR_NAME = "skills";
16
+ var CUSTOM_COMMANDS_DIR_NAME = "commands";
16
17
  var CONTEXT_FILE_CANDIDATES = ["AICLI.md", "CLAUDE.md"];
17
18
  var MEMORY_FILE_NAME = "memory.md";
18
19
  var MEMORY_MAX_CHARS = 1e4;
@@ -84,6 +85,9 @@ var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
84
85
  - Only begin using write/execute tools when the user **explicitly requests** an action (e.g., "generate", "create", "modify", "run", "start", etc.).
85
86
  - 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.
86
87
  - 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.`;
88
+ var AUTHOR = "Jin Zhengdong";
89
+ var AUTHOR_EMAIL = "zhengdong.jin@gmail.com";
90
+ var DESCRIPTION = "Cross-platform REPL-style AI conversation tool with multi-provider and agentic tool calling support";
87
91
 
88
92
  // src/tools/builtin/run-tests.ts
89
93
  var IS_WINDOWS = platform() === "win32";
@@ -447,6 +451,7 @@ export {
447
451
  HISTORY_DIR_NAME,
448
452
  PLUGINS_DIR_NAME,
449
453
  SKILLS_DIR_NAME,
454
+ CUSTOM_COMMANDS_DIR_NAME,
450
455
  CONTEXT_FILE_CANDIDATES,
451
456
  MEMORY_FILE_NAME,
452
457
  MEMORY_MAX_CHARS,
@@ -463,6 +468,9 @@ export {
463
468
  SUBAGENT_MAX_ROUNDS_LIMIT,
464
469
  SUBAGENT_ALLOWED_TOOLS,
465
470
  AGENTIC_BEHAVIOR_GUIDELINE,
471
+ AUTHOR,
472
+ AUTHOR_EMAIL,
473
+ DESCRIPTION,
466
474
  executeTests,
467
475
  runTestsTool
468
476
  };
@@ -381,7 +381,7 @@ ${content}`);
381
381
  }
382
382
  }
383
383
  async function runTaskMode(config, providers, configManager, topic) {
384
- const { TaskOrchestrator } = await import("./task-orchestrator-XXOS7WHR.js");
384
+ const { TaskOrchestrator } = await import("./task-orchestrator-AFMHSW4N.js");
385
385
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
386
386
  let interrupted = false;
387
387
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  saveDevState,
24
24
  sessionHasMeaningfulContent,
25
25
  setupProxy
26
- } from "./chunk-52BIUGQ2.js";
26
+ } from "./chunk-KYWU74D5.js";
27
27
  import {
28
28
  ToolRegistry,
29
29
  askUserContext,
@@ -38,7 +38,7 @@ import {
38
38
  theme,
39
39
  truncateOutput,
40
40
  undoStack
41
- } from "./chunk-TXCLZ72H.js";
41
+ } from "./chunk-HOOMQACE.js";
42
42
  import {
43
43
  AGENTIC_BEHAVIOR_GUIDELINE,
44
44
  AUTHOR,
@@ -58,7 +58,7 @@ import {
58
58
  REPO_URL,
59
59
  SKILLS_DIR_NAME,
60
60
  VERSION
61
- } from "./chunk-DP3M26PP.js";
61
+ } from "./chunk-KOD3C2CU.js";
62
62
 
63
63
  // src/index.ts
64
64
  import { program } from "commander";
@@ -1914,7 +1914,7 @@ ${hint}` : "")
1914
1914
  description: "Run project tests and show structured report",
1915
1915
  usage: "/test [command|filter]",
1916
1916
  async execute(args, _ctx) {
1917
- const { executeTests } = await import("./run-tests-PVVT37LK.js");
1917
+ const { executeTests } = await import("./run-tests-H7IVHUZO.js");
1918
1918
  const argStr = args.join(" ").trim();
1919
1919
  let testArgs = {};
1920
1920
  if (argStr) {
@@ -5528,7 +5528,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5528
5528
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5529
5529
  process.exit(1);
5530
5530
  }
5531
- const { startWebServer } = await import("./server-KPNMFMDU.js");
5531
+ const { startWebServer } = await import("./server-CL6Q5R2O.js");
5532
5532
  await startWebServer({ port, host: options.host });
5533
5533
  });
5534
5534
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
@@ -5761,7 +5761,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
5761
5761
  }),
5762
5762
  config.get("customProviders")
5763
5763
  );
5764
- const { startHub } = await import("./hub-LURPCJ4Z.js");
5764
+ const { startHub } = await import("./hub-34MLKYPL.js");
5765
5765
  await startHub(
5766
5766
  {
5767
5767
  topic: topic ?? "",
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-DP3M26PP.js";
5
+ } from "./chunk-KOD3C2CU.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-TLOCRYJC.js";
4
+ } from "./chunk-SNUHVNSD.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -18,7 +18,7 @@ import {
18
18
  renderDiff,
19
19
  runHook,
20
20
  setupProxy
21
- } from "./chunk-52BIUGQ2.js";
21
+ } from "./chunk-KYWU74D5.js";
22
22
  import {
23
23
  AuthManager
24
24
  } from "./chunk-BYNY5JPB.js";
@@ -32,19 +32,24 @@ import {
32
32
  spawnAgentContext,
33
33
  truncateOutput,
34
34
  undoStack
35
- } from "./chunk-TXCLZ72H.js";
35
+ } from "./chunk-HOOMQACE.js";
36
36
  import {
37
37
  AGENTIC_BEHAVIOR_GUIDELINE,
38
+ AUTHOR,
39
+ AUTHOR_EMAIL,
38
40
  CONTEXT_FILE_CANDIDATES,
41
+ CUSTOM_COMMANDS_DIR_NAME,
39
42
  DEFAULT_MAX_TOKENS,
43
+ DESCRIPTION,
40
44
  MCP_PROJECT_CONFIG_NAME,
41
45
  MEMORY_FILE_NAME,
42
46
  MEMORY_MAX_CHARS,
43
47
  PLAN_MODE_READONLY_TOOLS,
44
48
  PLAN_MODE_SYSTEM_ADDON,
49
+ PLUGINS_DIR_NAME,
45
50
  SKILLS_DIR_NAME,
46
51
  VERSION
47
- } from "./chunk-DP3M26PP.js";
52
+ } from "./chunk-KOD3C2CU.js";
48
53
 
49
54
  // src/web/server.ts
50
55
  import express from "express";
@@ -475,6 +480,8 @@ var SessionHandler = class _SessionHandler {
475
480
  pendingAskUser = /* @__PURE__ */ new Map();
476
481
  /** Active system prompt from context files */
477
482
  activeSystemPrompt;
483
+ /** Directories added via /add-dir */
484
+ addedDirs = /* @__PURE__ */ new Set();
478
485
  constructor(ws, shared) {
479
486
  this.ws = ws;
480
487
  this.config = shared.config;
@@ -1078,27 +1085,25 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1078
1085
  message: [
1079
1086
  "\u{1F4D6} Available Web UI commands:",
1080
1087
  "",
1088
+ " /help \u2014 Show this help message",
1089
+ " /about \u2014 Version & author info",
1081
1090
  " /provider <id> \u2014 Switch AI provider",
1082
1091
  " /model <id> \u2014 Switch model",
1083
1092
  " /clear \u2014 Clear conversation & start new session",
1084
1093
  " /compact [hint] \u2014 Compress conversation history",
1085
1094
  " /think [on|off] \u2014 Toggle extended thinking mode",
1086
1095
  " /plan [enter|exit] \u2014 Toggle read-only planning mode",
1087
- " /session new \u2014 Create a new session",
1088
- " /session list \u2014 List saved sessions",
1089
- " /session load <id> \u2014 Resume a saved session",
1090
- " /session delete <id> \u2014 Delete a session",
1096
+ " /session new|list|load|delete <id> \u2014 Session management",
1097
+ " /system [prompt|clear] \u2014 Set/view/reset system prompt",
1098
+ " /context [reload] \u2014 Show/reload context layers",
1091
1099
  " /status \u2014 Show session info & token usage",
1092
1100
  " /cost \u2014 Show cumulative token usage",
1093
- " /tools \u2014 Show tools, MCP servers & skills in sidebar",
1094
- " /export [md|json] \u2014 Export conversation as Markdown or JSON",
1095
- " /skill \u2014 List available skills",
1096
- " /skill <name> \u2014 Activate a skill",
1097
- " /skill off \u2014 Deactivate current skill",
1098
- " /memory \u2014 Show persistent memory contents",
1099
- " /memory add <text> \u2014 Add entry to persistent memory",
1100
- " /memory clear \u2014 Clear persistent memory",
1101
- " /yolo [on|off] \u2014 Toggle session auto-approve (skip confirmations)",
1101
+ " /config [show|get|set] \u2014 View/modify configuration",
1102
+ " /tools \u2014 Show tools, MCP servers & skills",
1103
+ " /export [md|json] \u2014 Export conversation",
1104
+ " /skill [name|off|list|reload] \u2014 Manage agent skills",
1105
+ " /memory [show|add|clear] \u2014 Persistent memory management",
1106
+ " /yolo [on|off] \u2014 Toggle auto-approve (skip confirmations)",
1102
1107
  " /search <keyword> \u2014 Search across all session histories",
1103
1108
  " /undo [list|<n>] \u2014 Undo file operations",
1104
1109
  " /diff [--stats] \u2014 Show file modifications in this session",
@@ -1107,8 +1112,13 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
1107
1112
  " /review [--staged] \u2014 AI code review from git diff",
1108
1113
  " /test [command] \u2014 Run project tests",
1109
1114
  " /init [--force] \u2014 Generate AICLI.md by scanning project",
1115
+ " /scaffold <desc> \u2014 Generate project scaffolding with AI",
1116
+ " /add-dir [path|remove] \u2014 Add/remove directory from AI context",
1117
+ " /mcp [reconnect] \u2014 Show/manage MCP servers",
1118
+ " /plugins \u2014 Show loaded plugins",
1119
+ " /commands \u2014 List custom commands",
1110
1120
  " /doctor \u2014 Health check (API keys, config, MCP)",
1111
- " /help \u2014 Show this help message",
1121
+ " /bug \u2014 Generate bug report template",
1112
1122
  "",
1113
1123
  "\u{1F4A1} Tips:",
1114
1124
  " \u2022 Change provider/model with the dropdowns above",
@@ -1472,7 +1482,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
1472
1482
  case "test": {
1473
1483
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
1474
1484
  try {
1475
- const { executeTests } = await import("./run-tests-PVVT37LK.js");
1485
+ const { executeTests } = await import("./run-tests-H7IVHUZO.js");
1476
1486
  const argStr = args.join(" ").trim();
1477
1487
  let testArgs = {};
1478
1488
  if (argStr) {
@@ -1560,6 +1570,264 @@ Use /context reload to load it.` });
1560
1570
  this.send({ type: "info", message: lines.join("\n") });
1561
1571
  break;
1562
1572
  }
1573
+ // ── /about ──────────────────────────────────────────────────────
1574
+ case "about": {
1575
+ const providerList = this.providers.listAll();
1576
+ const toolCount = this.toolRegistry.getDefinitions().length;
1577
+ const mcpTools = this.mcpManager?.getTotalToolCount() ?? 0;
1578
+ const lines = [
1579
+ `\u{1F916} **ai-cli** v${VERSION}`,
1580
+ "",
1581
+ `${DESCRIPTION}`,
1582
+ `Author: ${AUTHOR} <${AUTHOR_EMAIL}>`,
1583
+ "",
1584
+ `**Providers:** ${providerList.length} (${providerList.map((p) => p.id).join(", ")})`,
1585
+ `**Tools:** ${toolCount + mcpTools} (${toolCount} built-in${mcpTools > 0 ? ` + ${mcpTools} MCP` : ""})`,
1586
+ `**REPL Commands:** 37`,
1587
+ "",
1588
+ `GitHub: https://github.com/jinzhengdong/ai-cli`
1589
+ ];
1590
+ this.send({ type: "info", message: lines.join("\n") });
1591
+ break;
1592
+ }
1593
+ // ── /system ─────────────────────────────────────────────────────
1594
+ case "system": {
1595
+ const text = args.join(" ").trim();
1596
+ if (!text) {
1597
+ const current = this.activeSystemPrompt;
1598
+ if (current) {
1599
+ const preview = current.length > 500 ? current.slice(0, 500) + "..." : current;
1600
+ this.send({ type: "info", message: `\u{1F4CB} Current system prompt (${current.length} chars):
1601
+
1602
+ ${preview}` });
1603
+ } else {
1604
+ this.send({ type: "info", message: "No custom system prompt set. Using default.\nUsage: /system <prompt>" });
1605
+ }
1606
+ break;
1607
+ }
1608
+ if (text === "clear" || text === "reset") {
1609
+ this.activeSystemPrompt = this.loadContextFiles();
1610
+ this.send({ type: "info", message: "\u2713 System prompt reset to default (context files)." });
1611
+ } else {
1612
+ this.activeSystemPrompt = text;
1613
+ this.send({ type: "info", message: `\u2713 System prompt set (${text.length} chars).` });
1614
+ }
1615
+ break;
1616
+ }
1617
+ // ── /config ─────────────────────────────────────────────────────
1618
+ case "config": {
1619
+ const sub = args[0];
1620
+ if (sub === "show" || !sub) {
1621
+ this.send({ type: "info", message: `\u2699\uFE0F Configuration:
1622
+ \`\`\`json
1623
+ ${this.config.toFormattedJSON()}
1624
+ \`\`\`` });
1625
+ } else if (sub === "get" && args[1]) {
1626
+ const val = this.config.getByPath(args[1]);
1627
+ const display = typeof val === "object" ? JSON.stringify(val, null, 2) : String(val ?? "undefined");
1628
+ this.send({ type: "info", message: `${args[1]} = ${display}` });
1629
+ } else if (sub === "set" && args[1] && args[2] !== void 0) {
1630
+ try {
1631
+ this.config.setByPath(args[1], args.slice(2).join(" "));
1632
+ this.send({ type: "info", message: `\u2713 ${args[1]} = ${args.slice(2).join(" ")}` });
1633
+ } catch (err) {
1634
+ this.send({ type: "error", message: `Config error: ${err.message}` });
1635
+ }
1636
+ } else {
1637
+ this.send({ type: "info", message: "Usage: /config [show] | /config get <key> | /config set <key> <value>" });
1638
+ }
1639
+ break;
1640
+ }
1641
+ // ── /context ────────────────────────────────────────────────────
1642
+ case "context": {
1643
+ const sub = args[0];
1644
+ if (sub === "reload") {
1645
+ this.activeSystemPrompt = this.loadContextFiles();
1646
+ this.send({ type: "info", message: this.activeSystemPrompt ? `\u2713 Context reloaded (${this.activeSystemPrompt.length} chars).` : "\u2713 No context files found." });
1647
+ break;
1648
+ }
1649
+ const configDir = this.config.getConfigDir();
1650
+ const cwd = process.cwd();
1651
+ const gitRoot = getGitRoot(cwd);
1652
+ const projectRoot = gitRoot ?? cwd;
1653
+ const layers = ["\u{1F4DA} **Context Layers:**", ""];
1654
+ const checkLayer = (label, dir) => {
1655
+ for (const name2 of CONTEXT_FILE_CANDIDATES) {
1656
+ const fullPath = join2(dir, name2);
1657
+ if (existsSync3(fullPath)) {
1658
+ try {
1659
+ const size = statSync(fullPath).size;
1660
+ layers.push(` \u2713 ${label}: ${fullPath} (${size} bytes)`);
1661
+ return;
1662
+ } catch {
1663
+ }
1664
+ }
1665
+ }
1666
+ layers.push(` \u2013 ${label}: (none)`);
1667
+ };
1668
+ checkLayer("Global", configDir);
1669
+ checkLayer("Project", projectRoot);
1670
+ if (resolve(cwd) !== resolve(projectRoot)) {
1671
+ checkLayer("Subdir", cwd);
1672
+ }
1673
+ layers.push("");
1674
+ layers.push(`Total prompt length: ${this.activeSystemPrompt?.length ?? 0} chars`);
1675
+ layers.push("Use /context reload to refresh.");
1676
+ this.send({ type: "info", message: layers.join("\n") });
1677
+ break;
1678
+ }
1679
+ // ── /mcp ────────────────────────────────────────────────────────
1680
+ case "mcp": {
1681
+ if (!this.mcpManager) {
1682
+ this.send({ type: "info", message: "MCP not configured. Add mcpServers to config.json." });
1683
+ break;
1684
+ }
1685
+ const sub = args[0];
1686
+ if (sub === "reconnect") {
1687
+ const targetId = args[1];
1688
+ this.send({ type: "info", message: "\u{1F504} Reconnecting MCP servers..." });
1689
+ try {
1690
+ await this.mcpManager.reconnectAll();
1691
+ this.send({ type: "info", message: "\u2713 MCP reconnect complete." });
1692
+ } catch (err) {
1693
+ this.send({ type: "error", message: `Reconnect failed: ${err.message}` });
1694
+ }
1695
+ this.sendToolsList();
1696
+ break;
1697
+ }
1698
+ const statuses = this.mcpManager.getStatus();
1699
+ if (statuses.length === 0) {
1700
+ this.send({ type: "info", message: "No MCP servers configured." });
1701
+ break;
1702
+ }
1703
+ const lines = [`\u{1F50C} **MCP Servers (${statuses.length}):**`, ""];
1704
+ for (const s of statuses) {
1705
+ const state = s.connected ? `\u2713 connected \xB7 ${s.serverName} \xB7 ${s.toolCount} tools` : `\u2717 disconnected${s.error ? ` \xB7 ${s.error}` : ""}`;
1706
+ lines.push(` ${s.serverId}: ${state}`);
1707
+ }
1708
+ lines.push("");
1709
+ lines.push("Use /mcp reconnect to reconnect.");
1710
+ this.send({ type: "info", message: lines.join("\n") });
1711
+ break;
1712
+ }
1713
+ // ── /scaffold ───────────────────────────────────────────────────
1714
+ case "scaffold": {
1715
+ const description = args.join(" ").trim();
1716
+ if (!description) {
1717
+ this.send({ type: "error", message: "Usage: /scaffold <project description>" });
1718
+ break;
1719
+ }
1720
+ this.send({ type: "info", message: "\u{1F3D7}\uFE0F Generating project scaffold..." });
1721
+ const scaffoldPrompt = `Please scaffold a project based on this description: ${description}
1722
+
1723
+ Create the necessary files and directory structure. Use write_file and bash tools to set up the project.`;
1724
+ await this.handleChat(scaffoldPrompt);
1725
+ break;
1726
+ }
1727
+ // ── /add-dir ────────────────────────────────────────────────────
1728
+ case "add-dir": {
1729
+ const sub = args[0]?.trim();
1730
+ if (!sub) {
1731
+ this.send({ type: "info", message: this.addedDirs.size > 0 ? `\u{1F4C1} Added directories:
1732
+ ${[...this.addedDirs].map((d) => ` \u2022 ${d}`).join("\n")}
1733
+
1734
+ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path> | /add-dir remove" });
1735
+ break;
1736
+ }
1737
+ if (sub === "remove" || sub === "clear") {
1738
+ this.addedDirs.clear();
1739
+ this.send({ type: "info", message: "\u2713 All added directories removed from context." });
1740
+ break;
1741
+ }
1742
+ const dirPath = resolve(sub);
1743
+ if (!existsSync3(dirPath)) {
1744
+ this.send({ type: "error", message: `Directory not found: ${dirPath}` });
1745
+ break;
1746
+ }
1747
+ if (!statSync(dirPath).isDirectory()) {
1748
+ this.send({ type: "error", message: `Not a directory: ${dirPath}` });
1749
+ break;
1750
+ }
1751
+ this.addedDirs.add(dirPath);
1752
+ this.send({ type: "info", message: `\u2713 Added directory: ${dirPath}
1753
+ It will be included in AI context for subsequent messages.` });
1754
+ break;
1755
+ }
1756
+ // ── /commands ───────────────────────────────────────────────────
1757
+ case "commands": {
1758
+ const configDir = this.config.getConfigDir();
1759
+ const commandsDir = join2(configDir, CUSTOM_COMMANDS_DIR_NAME);
1760
+ if (!existsSync3(commandsDir)) {
1761
+ this.send({ type: "info", message: `No custom commands directory.
1762
+ Create: ${commandsDir}/ with .md files.` });
1763
+ break;
1764
+ }
1765
+ try {
1766
+ const files = readdirSync(commandsDir).filter((f) => f.endsWith(".md"));
1767
+ if (files.length === 0) {
1768
+ this.send({ type: "info", message: `No custom commands found in ${commandsDir}
1769
+ Add .md files to create commands.` });
1770
+ } else {
1771
+ const lines = [`\u{1F4CB} **Custom Commands (${files.length}):**`, `Dir: ${commandsDir}`, ""];
1772
+ for (const f of files) {
1773
+ const name2 = f.replace(/\.md$/, "");
1774
+ lines.push(` /${name2}`);
1775
+ }
1776
+ this.send({ type: "info", message: lines.join("\n") });
1777
+ }
1778
+ } catch {
1779
+ this.send({ type: "info", message: `Cannot read: ${commandsDir}` });
1780
+ }
1781
+ break;
1782
+ }
1783
+ // ── /plugins ────────────────────────────────────────────────────
1784
+ case "plugins": {
1785
+ const configDir = this.config.getConfigDir();
1786
+ const pluginsDir = join2(configDir, PLUGINS_DIR_NAME);
1787
+ const pluginTools = this.toolRegistry.listPluginTools();
1788
+ const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
1789
+ if (pluginTools.length === 0) {
1790
+ lines.push("No plugins loaded.");
1791
+ lines.push("Add .js files to the plugins directory and set allowPlugins:true in config.");
1792
+ } else {
1793
+ for (const t of pluginTools) {
1794
+ lines.push(` \u2022 ${t.name} \u2014 ${t.description}`);
1795
+ }
1796
+ }
1797
+ this.send({ type: "info", message: lines.join("\n") });
1798
+ break;
1799
+ }
1800
+ // ── /bug ────────────────────────────────────────────────────────
1801
+ case "bug": {
1802
+ const session = this.sessions.current;
1803
+ const lines = [
1804
+ "\u{1F41B} **Bug Report Template**",
1805
+ "",
1806
+ "**Environment:**",
1807
+ ` ai-cli version: ${VERSION}`,
1808
+ ` Node.js: ${process.version}`,
1809
+ ` OS: ${process.platform} ${process.arch}`,
1810
+ ` Provider: ${this.currentProvider}`,
1811
+ ` Model: ${this.currentModel}`,
1812
+ ` Session: ${session?.id.slice(0, 8) ?? "none"} (${session?.messages.length ?? 0} messages)`,
1813
+ "",
1814
+ "**Description:**",
1815
+ "(Describe the bug)",
1816
+ "",
1817
+ "**Steps to reproduce:**",
1818
+ "1. ...",
1819
+ "",
1820
+ "**Expected behavior:**",
1821
+ "...",
1822
+ "",
1823
+ "**Actual behavior:**",
1824
+ "...",
1825
+ "",
1826
+ "Report at: https://github.com/jinzhengdong/ai-cli/issues"
1827
+ ];
1828
+ this.send({ type: "info", message: lines.join("\n") });
1829
+ break;
1830
+ }
1563
1831
  default:
1564
1832
  this.send({ type: "error", message: `Unknown command: /${name}. Type /help for available commands.` });
1565
1833
  }
@@ -1759,12 +2027,27 @@ Use /context reload to load it.` });
1759
2027
  buildSystemPrompt() {
1760
2028
  const skillContent = this.skillManager?.getActivePromptContent();
1761
2029
  const activeSkill = skillContent && this.skillManager?.getActive() ? { name: this.skillManager.getActive().meta.name, content: skillContent } : void 0;
1762
- return buildSystemPrompt({
2030
+ let prompt = buildSystemPrompt({
1763
2031
  activeSystemPrompt: this.activeSystemPrompt,
1764
2032
  activeSkill,
1765
2033
  planMode: this.planMode,
1766
2034
  configDir: this.config.getConfigDir()
1767
2035
  });
2036
+ if (this.addedDirs.size > 0) {
2037
+ const MAX_DIR_CONTEXT = 4e4;
2038
+ let dirContext = "\n\n--- Added Directory Context ---\n";
2039
+ let totalLen = 0;
2040
+ for (const dir of this.addedDirs) {
2041
+ if (totalLen > MAX_DIR_CONTEXT) break;
2042
+ dirContext += `
2043
+ [Directory: ${dir}]
2044
+ `;
2045
+ dirContext += this.scanDirTree(dir, 2, 40) + "\n";
2046
+ totalLen = dirContext.length;
2047
+ }
2048
+ prompt += dirContext;
2049
+ }
2050
+ return prompt;
1768
2051
  }
1769
2052
  getModelParams() {
1770
2053
  const allParams = this.config.get("modelParams");
@@ -4,10 +4,10 @@ import {
4
4
  getDangerLevel,
5
5
  googleSearchContext,
6
6
  truncateOutput
7
- } from "./chunk-TXCLZ72H.js";
7
+ } from "./chunk-HOOMQACE.js";
8
8
  import {
9
9
  SUBAGENT_ALLOWED_TOOLS
10
- } from "./chunk-DP3M26PP.js";
10
+ } from "./chunk-KOD3C2CU.js";
11
11
 
12
12
  // src/hub/task-orchestrator.ts
13
13
  import { createInterface } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.13",
3
+ "version": "0.4.15",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",