jinzd-ai-cli 0.4.217 → 0.4.218

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.
Files changed (27) hide show
  1. package/README.md +4 -2
  2. package/dist/{batch-YCOVKTXD.js → batch-7V7OTMUP.js} +2 -2
  3. package/dist/{chunk-WZ3VKLF3.js → chunk-5CA2TJ5F.js} +1 -1
  4. package/dist/{chunk-QAYOI57M.js → chunk-C2Z42DI5.js} +1 -1
  5. package/dist/{chunk-MGBMNCHG.js → chunk-GX3HSGJX.js} +452 -156
  6. package/dist/{chunk-OUAZOE5U.js → chunk-H2UIHGHH.js} +23 -22
  7. package/dist/{chunk-KOPUCJXM.js → chunk-L4UREAID.js} +3 -3
  8. package/dist/{chunk-SNJAOXFT.js → chunk-MWKE2TNS.js} +1 -1
  9. package/dist/{chunk-XJGEQIYS.js → chunk-NTCB7CMT.js} +1 -1
  10. package/dist/{chunk-524WZOKS.js → chunk-OUC75QCF.js} +1 -1
  11. package/dist/{chunk-WKOQ5CYC.js → chunk-P4VBLXKS.js} +1 -1
  12. package/dist/{chunk-VTH7BLXK.js → chunk-VGFTM3XT.js} +1 -1
  13. package/dist/{ci-52RZIYWB.js → ci-L6GH2WVC.js} +4 -4
  14. package/dist/{ci-format-73UXKE65.js → ci-format-WW7454AY.js} +2 -2
  15. package/dist/{constants-DIXAD35W.js → constants-NCTFSHDU.js} +1 -1
  16. package/dist/{doctor-cli-7GOQPULZ.js → doctor-cli-EWMFBP5Q.js} +4 -4
  17. package/dist/electron-server.js +575 -231
  18. package/dist/{hub-GIGBITZN.js → hub-CDL6T7CP.js} +1 -1
  19. package/dist/index.js +148 -46
  20. package/dist/{pr-KPQ5RPKB.js → pr-D6PEKEGK.js} +4 -4
  21. package/dist/{run-tests-K7QR5QN4.js → run-tests-NXVVKAK2.js} +1 -1
  22. package/dist/{run-tests-ZDSA3QES.js → run-tests-SWU2XEV7.js} +2 -2
  23. package/dist/{server-QT3SC2KI.js → server-LHYSS6CK.js} +85 -21
  24. package/dist/{server-22YF3U34.js → server-WUT7VYTD.js} +4 -4
  25. package/dist/{task-orchestrator-BHQQCVTY.js → task-orchestrator-C5AA2BI5.js} +4 -4
  26. package/dist/{usage-WZZFSFLM.js → usage-6ZUUJBI2.js} +2 -2
  27. package/package.json +1 -1
@@ -154,7 +154,7 @@ ${content}`);
154
154
  }
155
155
  }
156
156
  async function runTaskMode(config, providers, configManager, topic) {
157
- const { TaskOrchestrator } = await import("./task-orchestrator-BHQQCVTY.js");
157
+ const { TaskOrchestrator } = await import("./task-orchestrator-C5AA2BI5.js");
158
158
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
159
159
  let interrupted = false;
160
160
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  scanProject,
16
16
  sessionHasMeaningfulContent,
17
17
  setupProxy
18
- } from "./chunk-OUAZOE5U.js";
18
+ } from "./chunk-H2UIHGHH.js";
19
19
  import {
20
20
  buildReviewPrompt,
21
21
  buildSecurityReviewPrompt
@@ -28,22 +28,29 @@ import {
28
28
  cleanupRejectedTeeFile,
29
29
  clearRecentlyDeniedAutoActions,
30
30
  deleteMemoryEntry,
31
+ describePlugin,
31
32
  estimateTokens,
32
33
  expireMemoryEntry,
33
34
  exportMemoryEntries,
34
35
  formatMemoryForPrompt,
35
36
  formatPermissionProfileWarning,
37
+ getActivePluginAssets,
38
+ getInstalledPlugin,
36
39
  getPendingHookTrust,
37
40
  getRecentlyDeniedAutoActions,
38
41
  googleSearchContext,
39
42
  initTheme,
43
+ installPlugin,
40
44
  isInterrupted,
41
45
  isMemoryExpired,
42
46
  lastResponseStore,
43
47
  listHooks,
48
+ listInstalledPlugins,
44
49
  listMemoryEntries,
45
50
  memoryMarkdownPath,
46
51
  memoryStorePath,
52
+ mergePluginHooks,
53
+ pluginRoot,
47
54
  renderDiff,
48
55
  requestInterrupt,
49
56
  resetInterrupt,
@@ -51,16 +58,18 @@ import {
51
58
  runLifecycleHooks,
52
59
  setContextWindow,
53
60
  setMaxOutputCap,
61
+ setPluginEnabled,
54
62
  spawnAgentContext,
55
63
  theme,
56
64
  trustHook,
65
+ trustPlugin,
57
66
  undoStack,
58
67
  untrustHook,
59
68
  updateMemoryApproval
60
- } from "./chunk-MGBMNCHG.js";
69
+ } from "./chunk-GX3HSGJX.js";
61
70
  import "./chunk-T2NL5ZIA.js";
62
71
  import "./chunk-BXP6YZ2P.js";
63
- import "./chunk-VTH7BLXK.js";
72
+ import "./chunk-VGFTM3XT.js";
64
73
  import {
65
74
  SessionManager,
66
75
  getContentText
@@ -77,7 +86,7 @@ import {
77
86
  getConfigDirUsage,
78
87
  listRecentCrashes,
79
88
  writeCrashLog
80
- } from "./chunk-524WZOKS.js";
89
+ } from "./chunk-OUC75QCF.js";
81
90
  import {
82
91
  ProviderRegistry
83
92
  } from "./chunk-QMXC327F.js";
@@ -86,7 +95,7 @@ import {
86
95
  getTopFailingTools,
87
96
  getTopUsedTools,
88
97
  installFlushOnExit
89
- } from "./chunk-SNJAOXFT.js";
98
+ } from "./chunk-MWKE2TNS.js";
90
99
  import {
91
100
  CONTENT_ONLY_STREAM_REMINDER,
92
101
  TEE_FINAL_USER_NUDGE,
@@ -108,7 +117,7 @@ import {
108
117
  } from "./chunk-XPBEJB27.js";
109
118
  import {
110
119
  ConfigManager
111
- } from "./chunk-WKOQ5CYC.js";
120
+ } from "./chunk-P4VBLXKS.js";
112
121
  import {
113
122
  AuthError,
114
123
  ProviderError,
@@ -133,7 +142,7 @@ import {
133
142
  SKILLS_DIR_NAME,
134
143
  VERSION,
135
144
  buildUserIdentityPrompt
136
- } from "./chunk-XJGEQIYS.js";
145
+ } from "./chunk-NTCB7CMT.js";
137
146
  import {
138
147
  formatGitContextForPrompt,
139
148
  getGitContext,
@@ -289,7 +298,7 @@ var ABOUT_FEATURES = [
289
298
  "Sub-agents: spawn_agent delegates complex subtasks to an isolated tool-call loop",
290
299
  "Agent Skills: ~/.aicli/skills/ reusable packs (system prompt + tool whitelist)",
291
300
  "MCP protocol: connect external MCP server tools; aicli mcp-serve reverses aicli into a server",
292
- "Plugin system: ~/.aicli/plugins/*.js custom tools (allowPlugins, off by default)",
301
+ "Plugin packages: skills/hooks/commands/MCP/agents via trusted .aicli-plugin/plugin.json",
293
302
  "Permission rules + hooks: auto-approve/deny/confirm + pre/post tool execution hooks",
294
303
  "User identity (/profile): persistent profile injected into every provider",
295
304
  "Symbol + semantic code index: find_symbol/get_outline/find_references/search_code (8 languages)",
@@ -916,6 +925,7 @@ function createDefaultCommands() {
916
925
  rows: [
917
926
  ["/tools", "List all AI tools available"],
918
927
  ["/plugins", "Show plugin directory and loaded plugins"],
928
+ ["/plugin [list|install|enable|disable|inspect|trust]", "Manage package plugins"],
919
929
  ["/mcp [reconnect [id]]", "MCP servers / reconnect disconnected"],
920
930
  ["/skill [name|off|list]", "Manage agent skills (reusable prompt packs)"],
921
931
  ["/context", "Show or reload hierarchical context layers"],
@@ -1376,7 +1386,7 @@ No commands match "${filter}".
1376
1386
  console.log(" PermProf : " + (ctx.config.get("defaultPermissionProfile") ?? "legacy"));
1377
1387
  console.log(" Network : " + (ctx.config.get("networkPolicy")?.enabled ?? false ? "on" : "off"));
1378
1388
  console.log(" AutoMode : " + (ctx.getToolExecutor().sessionAutoMode ? "on" : "off"));
1379
- console.log(" Hooks : " + getPendingHookTrust(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir()).length + " pending trust");
1389
+ console.log(" Hooks : " + getPendingHookTrust(mergePluginHooks(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir()), ctx.config.getConfigDir()).length + " pending trust");
1380
1390
  console.log(` Messages : ${session?.messages.length ?? 0}`);
1381
1391
  console.log(` Session : ${session?.id.slice(0, 8) ?? "none"}`);
1382
1392
  if (layers.length > 0) {
@@ -1661,6 +1671,91 @@ No tools match "${filter}".
1661
1671
  console.log();
1662
1672
  }
1663
1673
  },
1674
+ {
1675
+ name: "plugin",
1676
+ description: "Manage package plugins (.aicli-plugin/plugin.json)",
1677
+ usage: "/plugin [list|install <path>|enable <name>|disable <name>|inspect <name>|trust <name>]",
1678
+ async execute(args, ctx) {
1679
+ const sub = args[0] ?? "list";
1680
+ const configDir = ctx.config.getConfigDir();
1681
+ try {
1682
+ if (sub === "list") {
1683
+ const plugins = listInstalledPlugins(configDir);
1684
+ console.log(theme.heading("\n\u{1F50C} Package Plugins\n"));
1685
+ console.log(theme.dim(` Dir: ${pluginRoot(configDir)}`));
1686
+ if (plugins.length === 0) {
1687
+ console.log(theme.dim(" No package plugins installed."));
1688
+ console.log(theme.dim(" Use: /plugin install <path-to-plugin-dir>"));
1689
+ } else {
1690
+ for (const p of plugins) {
1691
+ const status = `${p.enabled ? theme.success("enabled") : theme.dim("disabled")} / ${p.trusted ? theme.success("trusted") : theme.warning("untrusted")}`;
1692
+ console.log(` ${theme.accent(p.name.padEnd(22))} ${p.manifest.version.padEnd(10)} ${status}`);
1693
+ if (p.manifest.description) console.log(theme.dim(` ${p.manifest.description}`));
1694
+ if (!p.valid && p.error) console.log(theme.warning(` invalid: ${p.error}`));
1695
+ }
1696
+ }
1697
+ console.log();
1698
+ return;
1699
+ }
1700
+ if (sub === "install") {
1701
+ const input2 = args[1];
1702
+ if (!input2) {
1703
+ ctx.renderer.printInfo("Usage: /plugin install <path-to-plugin-dir>");
1704
+ return;
1705
+ }
1706
+ const plugin = installPlugin(configDir, input2);
1707
+ ctx.renderer.printSuccess(`Installed plugin ${plugin.name}@${plugin.manifest.version}. Run /plugin trust ${plugin.name}, then /plugin enable ${plugin.name}.`);
1708
+ return;
1709
+ }
1710
+ if (sub === "trust") {
1711
+ const name = args[1];
1712
+ if (!name) {
1713
+ ctx.renderer.printInfo("Usage: /plugin trust <name>");
1714
+ return;
1715
+ }
1716
+ const state = trustPlugin(configDir, name);
1717
+ ctx.renderer.printSuccess(`Trusted plugin ${state.name}.`);
1718
+ return;
1719
+ }
1720
+ if (sub === "enable" || sub === "disable") {
1721
+ const name = args[1];
1722
+ if (!name) {
1723
+ ctx.renderer.printInfo(`Usage: /plugin ${sub} <name>`);
1724
+ return;
1725
+ }
1726
+ const state = setPluginEnabled(configDir, name, sub === "enable");
1727
+ ctx.renderer.printSuccess(`${sub === "enable" ? "Enabled" : "Disabled"} plugin ${state.name}. Restart aicli or reload affected managers for changes to take effect.`);
1728
+ return;
1729
+ }
1730
+ if (sub === "inspect") {
1731
+ const name = args[1];
1732
+ if (!name) {
1733
+ ctx.renderer.printInfo("Usage: /plugin inspect <name>");
1734
+ return;
1735
+ }
1736
+ const plugin = getInstalledPlugin(configDir, name);
1737
+ console.log();
1738
+ for (const line of describePlugin(plugin)) console.log(line);
1739
+ console.log();
1740
+ return;
1741
+ }
1742
+ if (sub === "active") {
1743
+ const assets = getActivePluginAssets(configDir);
1744
+ console.log(JSON.stringify({
1745
+ plugins: assets.plugins.map((p) => p.name),
1746
+ skillDirs: assets.skillDirs,
1747
+ commandDirs: assets.commandDirs,
1748
+ agentDirs: assets.agentDirs,
1749
+ mcpServers: Object.keys(assets.mcpServers)
1750
+ }, null, 2));
1751
+ return;
1752
+ }
1753
+ ctx.renderer.printInfo("Usage: /plugin [list|install|enable|disable|inspect|trust]");
1754
+ } catch (err) {
1755
+ ctx.renderer.renderError(err instanceof Error ? err.message : String(err));
1756
+ }
1757
+ }
1758
+ },
1664
1759
  {
1665
1760
  name: "mcp",
1666
1761
  description: "Show MCP server connections and tools",
@@ -1670,7 +1765,7 @@ No tools match "${filter}".
1670
1765
  const { join: join5 } = await import("path");
1671
1766
  const { existsSync: existsSync5 } = await import("fs");
1672
1767
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1673
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-DIXAD35W.js");
1768
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-NCTFSHDU.js");
1674
1769
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1675
1770
  const cwd = process.cwd();
1676
1771
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -1979,7 +2074,7 @@ No tools match "${filter}".
1979
2074
  return;
1980
2075
  }
1981
2076
  if (sub === "reload") {
1982
- const count = manager.loadSkills();
2077
+ const count = manager.loadSkills(getActivePluginAssets(ctx.config.getConfigDir()).skillDirs);
1983
2078
  ctx.renderer.printSuccess(`Reloaded: ${count} skill(s) found.`);
1984
2079
  return;
1985
2080
  }
@@ -2706,7 +2801,7 @@ ${hint}` : "")
2706
2801
  }
2707
2802
  const sub = args[0]?.toLowerCase();
2708
2803
  if (sub === "reload") {
2709
- const count = mgr.loadCommands();
2804
+ const count = mgr.loadCommands(getActivePluginAssets(ctx.config.getConfigDir()).commandDirs);
2710
2805
  console.log(theme.success(` \u2713 Reloaded ${count} custom command(s).`));
2711
2806
  return;
2712
2807
  }
@@ -2729,7 +2824,7 @@ ${hint}` : "")
2729
2824
  usage: "/test [command|filter]",
2730
2825
  async execute(args, ctx) {
2731
2826
  try {
2732
- const { executeTests } = await import("./run-tests-ZDSA3QES.js");
2827
+ const { executeTests } = await import("./run-tests-SWU2XEV7.js");
2733
2828
  const argStr = args.join(" ").trim();
2734
2829
  let testArgs = {};
2735
2830
  if (argStr) {
@@ -3377,7 +3472,7 @@ Summary: ${fileMap.size} file(s) \u2014 ${newFiles} new, ${modifiedFiles} modifi
3377
3472
  usage: "/hooks [list|inspect <id>|trust <id>|untrust <id>|disable]",
3378
3473
  execute(args, ctx) {
3379
3474
  const sub = (args[0] || "list").toLowerCase();
3380
- const hooks = listHooks(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir());
3475
+ const hooks = listHooks(mergePluginHooks(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir()), ctx.config.getConfigDir());
3381
3476
  const findHook = (id) => hooks.find((h) => h.id === id || h.hash.startsWith(id ?? ""));
3382
3477
  if (sub === "list") {
3383
3478
  console.log(theme.heading(" Hooks"));
@@ -3456,7 +3551,7 @@ Summary: ${fileMap.size} file(s) \u2014 ${newFiles} new, ${modifiedFiles} modifi
3456
3551
  console.log(" profile : " + (ctx.config.get("defaultPermissionProfile") ?? "legacy"));
3457
3552
  console.log(" network : " + (ctx.config.get("networkPolicy")?.enabled ?? false ? "on" : "off"));
3458
3553
  console.log(" autoMode : " + (ctx.getToolExecutor().sessionAutoMode ? "on" : "off"));
3459
- console.log(" hooks : " + getPendingHookTrust(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir()).length + " pending trust");
3554
+ console.log(" hooks : " + getPendingHookTrust(mergePluginHooks(ctx.config.get("hooks") ?? void 0, ctx.config.getConfigDir()), ctx.config.getConfigDir()).length + " pending trust");
3460
3555
  console.log(` patterns : ${DEFAULT_PATTERNS.length} built-in + ${security.customPatterns?.length ?? 0} custom`);
3461
3556
  console.log(` last save : ${ctx.sessions.lastRedactionHits} hit(s) redacted`);
3462
3557
  console.log();
@@ -4277,22 +4372,22 @@ var CustomCommandManager = class {
4277
4372
  this.commandsDir = commandsDir;
4278
4373
  }
4279
4374
  commands = /* @__PURE__ */ new Map();
4280
- loadCommands() {
4375
+ loadCommands(extraDirs = []) {
4281
4376
  this.commands.clear();
4282
- if (!existsSync3(this.commandsDir)) {
4283
- mkdirSync3(this.commandsDir, { recursive: true });
4284
- return 0;
4377
+ this.loadCommandDir(this.commandsDir, true);
4378
+ for (const dir of extraDirs) this.loadCommandDir(dir, false);
4379
+ return this.commands.size;
4380
+ }
4381
+ loadCommandDir(dir, createIfMissing) {
4382
+ if (!existsSync3(dir)) {
4383
+ if (createIfMissing) mkdirSync3(dir, { recursive: true });
4384
+ return;
4285
4385
  }
4286
- let count = 0;
4287
- for (const file of readdirSync(this.commandsDir)) {
4386
+ for (const file of readdirSync(dir)) {
4288
4387
  if (extname(file) !== ".md") continue;
4289
- const cmd = parseCommandFile(join3(this.commandsDir, file));
4290
- if (cmd) {
4291
- this.commands.set(cmd.meta.name, cmd);
4292
- count++;
4293
- }
4388
+ const cmd = parseCommandFile(join3(dir, file));
4389
+ if (cmd && !this.commands.has(cmd.meta.name)) this.commands.set(cmd.meta.name, cmd);
4294
4390
  }
4295
- return count;
4296
4391
  }
4297
4392
  listCommands() {
4298
4393
  return [...this.commands.values()];
@@ -4670,7 +4765,7 @@ var Repl = class {
4670
4765
  const permissionProfileName = this.config.get("defaultPermissionProfile") ?? "legacy";
4671
4766
  const permissionProfiles = this.config.get("permissionProfiles") ?? {};
4672
4767
  this.toolExecutor.setConfig({
4673
- hookConfig: this.config.get("hooks") ?? void 0,
4768
+ hookConfig: mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
4674
4769
  hookConfigDir: this.config.getConfigDir(),
4675
4770
  permissionRules: this.config.get("permissionRules") ?? [],
4676
4771
  defaultPermission: this.config.get("defaultPermission") ?? "confirm",
@@ -4680,11 +4775,12 @@ var Repl = class {
4680
4775
  workspaceRoot: process.cwd(),
4681
4776
  networkPolicy: this.config.get("networkPolicy")
4682
4777
  });
4683
- const pendingHooks = getPendingHookTrust(this.config.get("hooks") ?? void 0, this.config.getConfigDir());
4778
+ const effectiveHooks = mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir());
4779
+ const pendingHooks = getPendingHookTrust(effectiveHooks, this.config.getConfigDir());
4684
4780
  if (pendingHooks.length > 0) {
4685
4781
  process.stderr.write(theme.warning(" \u26A0 " + pendingHooks.length + " project hook(s) require trust. Run /hooks list and /hooks trust <id>.\n"));
4686
4782
  }
4687
- runLifecycleHooks(this.config.get("hooks") ?? void 0, "SessionStart", { source: "repl" }, { configDir: this.config.getConfigDir() });
4783
+ runLifecycleHooks(effectiveHooks, "SessionStart", { source: "repl" }, { configDir: this.config.getConfigDir() });
4688
4784
  if (this.config.get("alwaysYolo") === true) {
4689
4785
  this.toolExecutor.sessionAutoApprove = true;
4690
4786
  }
@@ -5145,7 +5241,7 @@ ${skillContent}`);
5145
5241
  return false;
5146
5242
  }
5147
5243
  const preDecisions = runLifecycleHooks(
5148
- this.config.get("hooks") ?? void 0,
5244
+ mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
5149
5245
  "PreCompact",
5150
5246
  { sessionId: session.id, messageCount: session.messages.length, instruction },
5151
5247
  { configDir: this.config.getConfigDir(), onSummary: (msg) => process.stdout.write(theme.dim(" [hook] " + msg) + "\n") }
@@ -5221,7 +5317,7 @@ ${response.content.trim()}
5221
5317
  const removedCount = session.compact(summaryMsg, ackMsg, COMPACT_KEEP_LAST);
5222
5318
  const afterCount = session.messages.length;
5223
5319
  runLifecycleHooks(
5224
- this.config.get("hooks") ?? void 0,
5320
+ mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
5225
5321
  "PostCompact",
5226
5322
  { sessionId: session.id, beforeCount, afterCount, removedCount, keepLast: COMPACT_KEEP_LAST, instruction },
5227
5323
  { configDir: this.config.getConfigDir(), onSummary: (msg) => process.stdout.write(theme.dim(" [hook] " + msg) + "\n") }
@@ -5423,14 +5519,15 @@ Session '${this.resumeSessionId}' not found.
5423
5519
  }
5424
5520
  const skillsDir = join4(this.config.getConfigDir(), SKILLS_DIR_NAME);
5425
5521
  this.skillManager = new SkillManager(skillsDir, this.config.get("ui").skillSizeWarn);
5426
- const skillCount = this.skillManager.loadSkills();
5522
+ const pluginAssets = getActivePluginAssets(this.config.getConfigDir());
5523
+ const skillCount = this.skillManager.loadSkills(pluginAssets.skillDirs);
5427
5524
  if (skillCount > 0) {
5428
5525
  process.stdout.write(theme.dim(` \u{1F3AF} Skills: ${skillCount} available (use /skill to manage)
5429
5526
  `));
5430
5527
  }
5431
5528
  const commandsDir = join4(this.config.getConfigDir(), CUSTOM_COMMANDS_DIR_NAME);
5432
5529
  this.customCommandManager = new CustomCommandManager(commandsDir);
5433
- const customCmdCount = this.customCommandManager.loadCommands();
5530
+ const customCmdCount = this.customCommandManager.loadCommands(pluginAssets.commandDirs);
5434
5531
  if (customCmdCount > 0) {
5435
5532
  process.stdout.write(theme.dim(` \u{1F4CB} Custom commands: ${customCmdCount} loaded (use /commands to list)
5436
5533
  `));
@@ -5486,6 +5583,7 @@ Session '${this.resumeSessionId}' not found.
5486
5583
  })();
5487
5584
  const mcpEnabled = this.config.get("mcpEnabled") !== false;
5488
5585
  const globalMcpServers = mcpEnabled ? this.config.get("mcpServers") ?? {} : {};
5586
+ const pluginMcpServers = mcpEnabled ? getActivePluginAssets(this.config.getConfigDir()).mcpServers : {};
5489
5587
  const projectMcpResult = mcpEnabled ? this.loadProjectMcpConfig() : null;
5490
5588
  if (!mcpEnabled) {
5491
5589
  process.stdout.write(theme.dim(" \u{1F50C} MCP: disabled (config.mcpEnabled=false)\n"));
@@ -5507,10 +5605,13 @@ Session '${this.resumeSessionId}' not found.
5507
5605
  );
5508
5606
  }
5509
5607
  }
5510
- const mergedMcpServers = { ...globalMcpServers, ...projectMcpServers };
5608
+ const mergedMcpServers = { ...globalMcpServers, ...pluginMcpServers, ...projectMcpServers };
5511
5609
  for (const id of Object.keys(globalMcpServers)) {
5512
5610
  this.mcpServerSources.set(id, "global");
5513
5611
  }
5612
+ for (const id of Object.keys(pluginMcpServers)) {
5613
+ this.mcpServerSources.set(id, "plugin");
5614
+ }
5514
5615
  for (const id of Object.keys(projectMcpServers)) {
5515
5616
  this.mcpServerSources.set(id, "project");
5516
5617
  }
@@ -5518,6 +5619,7 @@ Session '${this.resumeSessionId}' not found.
5518
5619
  if (Object.keys(mergedMcpServers).length > 0) {
5519
5620
  this.mcpManager = new McpManager();
5520
5621
  const mcpProjectCount = Object.keys(projectMcpServers).length;
5622
+ const mcpPluginCount = Object.keys(pluginMcpServers).length;
5521
5623
  let bannerEmitted = false;
5522
5624
  const emitBanner = (afterDeadline) => {
5523
5625
  if (bannerEmitted) return;
@@ -5663,7 +5765,7 @@ Session '${this.resumeSessionId}' not found.
5663
5765
  }
5664
5766
  runUserPromptHooks(sessionId, userInput) {
5665
5767
  const promptDecisions = runLifecycleHooks(
5666
- this.config.get("hooks") ?? void 0,
5768
+ mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
5667
5769
  "UserPromptSubmit",
5668
5770
  { sessionId, prompt: userInput },
5669
5771
  { configDir: this.config.getConfigDir(), onSummary: (msg) => process.stdout.write(theme.dim(" [hook] " + msg) + "\n") }
@@ -7152,7 +7254,7 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7152
7254
  void this.shutdownAsync();
7153
7255
  }
7154
7256
  async shutdownAsync() {
7155
- runLifecycleHooks(this.config.get("hooks") ?? void 0, "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
7257
+ runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
7156
7258
  this.running = false;
7157
7259
  const sessionId = this.sessions.current?.id;
7158
7260
  if (sessionId) {
@@ -7267,7 +7369,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7267
7369
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7268
7370
  process.exit(1);
7269
7371
  }
7270
- const { startWebServer } = await import("./server-QT3SC2KI.js");
7372
+ const { startWebServer } = await import("./server-LHYSS6CK.js");
7271
7373
  await startWebServer({ port, host: options.host });
7272
7374
  });
7273
7375
  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) => {
@@ -7434,16 +7536,16 @@ program.command("sessions").description("List recent conversation sessions").opt
7434
7536
  console.log(footer + "\n");
7435
7537
  });
7436
7538
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7437
- const { runUsageCli } = await import("./usage-WZZFSFLM.js");
7539
+ const { runUsageCli } = await import("./usage-6ZUUJBI2.js");
7438
7540
  await runUsageCli(options);
7439
7541
  });
7440
7542
  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) => {
7441
- const { runDoctorCli } = await import("./doctor-cli-7GOQPULZ.js");
7543
+ const { runDoctorCli } = await import("./doctor-cli-EWMFBP5Q.js");
7442
7544
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7443
7545
  });
7444
7546
  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) => {
7445
7547
  try {
7446
- const batch = await import("./batch-YCOVKTXD.js");
7548
+ const batch = await import("./batch-7V7OTMUP.js");
7447
7549
  switch (action) {
7448
7550
  case "submit":
7449
7551
  if (!arg) {
@@ -7486,7 +7588,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7486
7588
  }
7487
7589
  });
7488
7590
  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) => {
7489
- const { startMcpServer } = await import("./server-22YF3U34.js");
7591
+ const { startMcpServer } = await import("./server-WUT7VYTD.js");
7490
7592
  await startMcpServer({
7491
7593
  allowDestructive: !!options.allowDestructive,
7492
7594
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7499,7 +7601,7 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7499
7601
  console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
7500
7602
  process.exit(1);
7501
7603
  }
7502
- const { runPrCli } = await import("./pr-KPQ5RPKB.js");
7604
+ const { runPrCli } = await import("./pr-D6PEKEGK.js");
7503
7605
  const result = await runPrCli({
7504
7606
  action,
7505
7607
  target,
@@ -7519,8 +7621,8 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7519
7621
  process.exit(result.exitCode);
7520
7622
  });
7521
7623
  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 or URL; diff fetched via gh pr diff <value>", (v) => /^#?\d+$/.test(v) ? parseInt(v.replace(/^#/, ""), 10) : v).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)").option("--json", "Output a structured JSON report").option("--markdown", "Output Markdown report (default)").option("--sarif", "Output SARIF 2.1.0 for code scanning artifacts").option("--fail-on-security-high", "Fail when the security review contains HIGH/CRITICAL findings (default)").option("--no-fail-on-security-high", "Do not fail on security HIGH/CRITICAL findings").option("--fail-on-test-failure", "Fail when the report mentions test failures (default)").option("--no-fail-on-test-failure", "Do not fail on test failure findings").option("--fail-on-lint-failure", "Fail when the report mentions lint/typecheck failures (default)").option("--no-fail-on-lint-failure", "Do not fail on lint/typecheck failure findings").action(async (options) => {
7522
- const { runCi } = await import("./ci-52RZIYWB.js");
7523
- const { formatCiResult } = await import("./ci-format-73UXKE65.js");
7624
+ const { runCi } = await import("./ci-L6GH2WVC.js");
7625
+ const { formatCiResult } = await import("./ci-format-WW7454AY.js");
7524
7626
  const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
7525
7627
  if (formats > 1) {
7526
7628
  console.error("Choose only one output format: --json, --markdown, or --sarif.");
@@ -7677,7 +7779,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7677
7779
  }),
7678
7780
  config.get("customProviders")
7679
7781
  );
7680
- const { startHub } = await import("./hub-GIGBITZN.js");
7782
+ const { startHub } = await import("./hub-CDL6T7CP.js");
7681
7783
  await startHub(
7682
7784
  {
7683
7785
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  countSeverity,
4
4
  runCi
5
- } from "./chunk-KOPUCJXM.js";
6
- import "./chunk-WZ3VKLF3.js";
5
+ } from "./chunk-L4UREAID.js";
6
+ import "./chunk-5CA2TJ5F.js";
7
7
  import {
8
8
  buildReviewPrompt,
9
9
  buildSecurityReviewPrompt,
@@ -15,11 +15,11 @@ import {
15
15
  import "./chunk-XPBEJB27.js";
16
16
  import {
17
17
  ConfigManager
18
- } from "./chunk-WKOQ5CYC.js";
18
+ } from "./chunk-P4VBLXKS.js";
19
19
  import "./chunk-TZQHYZKT.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-XJGEQIYS.js";
22
+ } from "./chunk-NTCB7CMT.js";
23
23
  import "./chunk-IW3Q7AE5.js";
24
24
 
25
25
  // src/cli/pr.ts
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-QAYOI57M.js";
4
+ } from "./chunk-C2Z42DI5.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-VTH7BLXK.js";
6
- import "./chunk-XJGEQIYS.js";
5
+ } from "./chunk-VGFTM3XT.js";
6
+ import "./chunk-NTCB7CMT.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool