jinzd-ai-cli 0.4.276 → 0.4.278

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 (39) hide show
  1. package/dist/{batch-F7SKIBI3.js → batch-7Y4NAA6N.js} +2 -2
  2. package/dist/{chunk-VF7DMSKF.js → chunk-2BYQP6JX.js} +1 -1
  3. package/dist/{chunk-GLSR2NR3.js → chunk-4DEMGCK4.js} +4 -3
  4. package/dist/{chunk-MXPUOF5K.js → chunk-4Z6GAYB5.js} +4 -4
  5. package/dist/{chunk-NXAHXWPF.js → chunk-H7QC5VMQ.js} +1 -1
  6. package/dist/{chunk-MCKUGWOY.js → chunk-JJCNNQTU.js} +183 -3
  7. package/dist/{chunk-AOISQVXS.js → chunk-L3CPM472.js} +1 -1
  8. package/dist/{chunk-ATD2Y3PB.js → chunk-LOXEYWC3.js} +1 -1
  9. package/dist/{chunk-WYDP2WEQ.js → chunk-LTZFUYBT.js} +4 -4
  10. package/dist/{chunk-4NH4Q7QK.js → chunk-LXXQTWT2.js} +1 -1
  11. package/dist/{chunk-QCG6UU2S.js → chunk-PPIISCFN.js} +4 -3
  12. package/dist/{chunk-KWSGSSWN.js → chunk-RFSKPCYC.js} +1 -1
  13. package/dist/{chunk-OMBKLWLJ.js → chunk-SB4XONS4.js} +1 -1
  14. package/dist/{chunk-UNL7NR55.js → chunk-SJCOE4C7.js} +1 -1
  15. package/dist/{chunk-4KTK2OYU.js → chunk-UGY5SVNX.js} +46 -20
  16. package/dist/{chunk-NXNXWM6H.js → chunk-WOXIKQRI.js} +16 -7
  17. package/dist/{chunk-FLDYAXJR.js → chunk-X3722RFA.js} +1 -1
  18. package/dist/{ci-VURFRMWQ.js → ci-WDF2CYR4.js} +5 -5
  19. package/dist/{ci-format-AFJJIURF.js → ci-format-7B5VAZGA.js} +2 -2
  20. package/dist/{constants-WO7ERENL.js → constants-DC3TNLTL.js} +1 -1
  21. package/dist/{doctor-cli-BGGILTHE.js → doctor-cli-CDDG5XOY.js} +5 -5
  22. package/dist/electron-server.js +378 -78
  23. package/dist/{hub-Z73RLNJI.js → hub-TO7ODOIA.js} +1 -1
  24. package/dist/index.js +175 -188
  25. package/dist/{indexer-M2LJGTY2.js → indexer-RBEKMFT4.js} +1 -1
  26. package/dist/{indexer-VZETNE4H.js → indexer-TNDWGFRI.js} +1 -1
  27. package/dist/{persistent-memory-5QMARQJF.js → persistent-memory-BU63CSGK.js} +2 -2
  28. package/dist/{persistent-memory-CI544RLT.js → persistent-memory-JCENBMCJ.js} +2 -2
  29. package/dist/{pr-24REDMGV.js → pr-GVSZYX6I.js} +5 -5
  30. package/dist/{run-tests-72VNZXNT.js → run-tests-2Q5MJHXB.js} +2 -2
  31. package/dist/{run-tests-JJ3KOY5E.js → run-tests-R4IQS6IF.js} +2 -2
  32. package/dist/{server-JMXC2N3F.js → server-KAURBKFI.js} +167 -62
  33. package/dist/{server-QJHCJDA7.js → server-WXLE53HG.js} +6 -6
  34. package/dist/{task-orchestrator-DJN36HP5.js → task-orchestrator-RJ6FXYY5.js} +8 -8
  35. package/dist/{usage-HXJVDETY.js → usage-V3R7DA7M.js} +2 -2
  36. package/dist/web/client/app.js +4 -1
  37. package/dist/web/client/app.js.br +0 -0
  38. package/dist/web/client/app.js.gz +0 -0
  39. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  McpManager,
5
5
  SNAPSHOT_PROMPT,
6
6
  SkillManager,
7
+ applyPlanCommand,
7
8
  applyProfileCommand,
8
9
  applyThinkCommand,
9
10
  armDeferredSave,
@@ -14,21 +15,28 @@ import {
14
15
  costSourceNote,
15
16
  decideDeferredSave,
16
17
  decideModel,
18
+ describeRouting,
17
19
  estimateConversationTokens,
18
20
  evaluateContextPressure,
19
21
  filterMemoryEntries,
20
22
  loadDevState,
21
23
  parseMemoryShowFilters,
24
+ parseModelCommand,
25
+ parseRouteCommand,
22
26
  parseSimpleYaml,
23
27
  persistToolRound,
24
28
  readGitDiff,
29
+ resolveModelSwitch,
30
+ resolveProviderSwitch,
31
+ routeDecisionRows,
32
+ routingEmptyRulesNote,
25
33
  saveDevState,
26
34
  scanProject,
27
35
  sessionHasMeaningfulContent,
28
36
  setupProxy,
29
37
  snapshotFile,
30
38
  stripRoutingTags
31
- } from "./chunk-MCKUGWOY.js";
39
+ } from "./chunk-JJCNNQTU.js";
32
40
  import {
33
41
  CostTracker,
34
42
  computeCost,
@@ -48,6 +56,7 @@ import {
48
56
  getRecentlyDeniedAutoActions,
49
57
  googleSearchContext,
50
58
  isInterrupted,
59
+ isToolCapableProvider,
51
60
  lastResponseStore,
52
61
  renderDiff,
53
62
  requestInterrupt,
@@ -57,7 +66,7 @@ import {
57
66
  setMaxOutputCap,
58
67
  spawnAgentContext,
59
68
  undoStack
60
- } from "./chunk-NXNXWM6H.js";
69
+ } from "./chunk-WOXIKQRI.js";
61
70
  import {
62
71
  buildReviewPrompt,
63
72
  buildSecurityReviewPrompt
@@ -98,8 +107,8 @@ import {
98
107
  touchMemoryReferences,
99
108
  updateMemoryApproval,
100
109
  updateMemoryEntry
101
- } from "./chunk-KWSGSSWN.js";
102
- import "./chunk-GLSR2NR3.js";
110
+ } from "./chunk-RFSKPCYC.js";
111
+ import "./chunk-4DEMGCK4.js";
103
112
  import "./chunk-UUSRWSSX.js";
104
113
  import "./chunk-CKH4KQ4E.js";
105
114
  import "./chunk-BXP6YZ2P.js";
@@ -122,7 +131,7 @@ import {
122
131
  formatDoctorReport,
123
132
  loadContextFiles,
124
133
  writeCrashLog
125
- } from "./chunk-WYDP2WEQ.js";
134
+ } from "./chunk-LTZFUYBT.js";
126
135
  import {
127
136
  describePlugin,
128
137
  getActivePluginAssets,
@@ -139,14 +148,14 @@ import {
139
148
  trustHook,
140
149
  trustPlugin,
141
150
  untrustHook
142
- } from "./chunk-AOISQVXS.js";
151
+ } from "./chunk-L3CPM472.js";
143
152
  import {
144
153
  ProviderRegistry,
145
154
  listBuiltInProviderMetadata
146
- } from "./chunk-4KTK2OYU.js";
155
+ } from "./chunk-UGY5SVNX.js";
147
156
  import {
148
157
  ConfigManager
149
- } from "./chunk-VF7DMSKF.js";
158
+ } from "./chunk-2BYQP6JX.js";
150
159
  import {
151
160
  CONTENT_ONLY_STREAM_REMINDER,
152
161
  TEE_FINAL_USER_NUDGE,
@@ -184,7 +193,7 @@ import {
184
193
  import {
185
194
  fileCheckpoints
186
195
  } from "./chunk-4BKXL7SM.js";
187
- import "./chunk-OMBKLWLJ.js";
196
+ import "./chunk-SB4XONS4.js";
188
197
  import {
189
198
  AGENTIC_BEHAVIOR_GUIDELINE,
190
199
  AUTHOR,
@@ -202,7 +211,7 @@ import {
202
211
  SKILLS_DIR_NAME,
203
212
  VERSION,
204
213
  buildUserIdentityPrompt
205
- } from "./chunk-FLDYAXJR.js";
214
+ } from "./chunk-X3722RFA.js";
206
215
 
207
216
  // src/index.ts
208
217
  import { program } from "commander";
@@ -978,34 +987,41 @@ function createProviderCommands() {
978
987
  } else {
979
988
  targetId = args[0];
980
989
  }
981
- if (!ctx.providers.has(targetId)) {
982
- ctx.renderer.renderError(`Provider '${targetId}' is not configured. Run: aicli config`);
983
- return;
984
- }
985
- const targetProvider = ctx.providers.get(targetId);
986
- if (targetProvider.info.models.length === 0) {
987
- const models = await targetProvider.listModels();
988
- if (models.length === 0) {
989
- ctx.renderer.renderError(
990
- `Provider '${targetId}' has no available models. ` + (targetId === "ollama" ? "Make sure Ollama is running (`ollama serve`) and has models (`ollama pull <model>`)." : "Check provider configuration.")
991
- );
992
- return;
990
+ const configured = ctx.providers.has(targetId);
991
+ let availableModels = [];
992
+ let providerDefaultModel = "";
993
+ if (configured) {
994
+ const target = ctx.providers.get(targetId);
995
+ providerDefaultModel = target.info.defaultModel;
996
+ availableModels = target.info.models.map((m) => m.id);
997
+ if (availableModels.length === 0) {
998
+ availableModels = (await target.listModels()).map((m) => m.id);
993
999
  }
994
1000
  }
995
- if (targetId === ctx.getCurrentProvider()) {
996
- ctx.renderer.printInfo(`Already using provider: ${targetId}`);
1001
+ const session = ctx.sessions.current;
1002
+ const outcome = resolveProviderSwitch({
1003
+ targetId,
1004
+ currentProvider: ctx.getCurrentProvider(),
1005
+ configured,
1006
+ availableModels,
1007
+ configuredDefaultModel: ctx.config.get("defaultModels")[targetId],
1008
+ providerDefaultModel,
1009
+ messageCount: session?.messages.length ?? 0
1010
+ });
1011
+ if (outcome.status === "not-configured" || outcome.status === "no-models") {
1012
+ ctx.renderer.renderError(outcome.message);
997
1013
  return;
998
1014
  }
999
- const session = ctx.sessions.current;
1000
- const msgCount = session?.messages.length ?? 0;
1001
- ctx.setProvider(targetId);
1015
+ if (outcome.status === "unchanged") {
1016
+ ctx.renderer.printInfo(outcome.message);
1017
+ return;
1018
+ }
1019
+ ctx.setProvider(outcome.providerId, outcome.model);
1002
1020
  if (session) {
1003
- session.updateProvider(targetId, ctx.getCurrentModel());
1021
+ session.updateProvider(outcome.providerId, outcome.model);
1004
1022
  await ctx.sessions.save();
1005
1023
  }
1006
- ctx.renderer.printSuccess(
1007
- `Switched to provider: ${targetId}` + (msgCount > 0 ? ` (conversation preserved: ${msgCount} messages)` : "")
1008
- );
1024
+ ctx.renderer.printSuccess(outcome.message);
1009
1025
  }
1010
1026
  },
1011
1027
  {
@@ -1013,29 +1029,29 @@ function createProviderCommands() {
1013
1029
  description: "Switch model, refresh live model list, or inspect model cache",
1014
1030
  usage: "/model [name|refresh [provider]|cache [clear [provider]]]",
1015
1031
  async execute(args, ctx) {
1016
- const sub = (args[0] ?? "").toLowerCase();
1017
- if (sub === "refresh") {
1018
- const providerId = args[1] ?? ctx.getCurrentProvider();
1019
- if (!ctx.providers.has(providerId)) {
1020
- ctx.renderer.printInfo(`Provider is not configured: ${providerId}`);
1032
+ const intent = parseModelCommand(args, ctx.getCurrentProvider());
1033
+ if (intent.kind === "refresh") {
1034
+ if (!ctx.providers.has(intent.providerId)) {
1035
+ ctx.renderer.printInfo(`Provider is not configured: ${intent.providerId}`);
1021
1036
  return;
1022
1037
  }
1023
- ctx.renderer.printInfo(`Refreshing models for ${providerId}...`);
1038
+ ctx.renderer.printInfo(`Refreshing models for ${intent.providerId}...`);
1024
1039
  try {
1025
- const models = await ctx.providers.refreshModels(providerId);
1026
- ctx.renderer.printSuccess(`Refreshed ${models.length} model(s) for ${providerId}.`);
1040
+ const models = await ctx.providers.refreshModels(intent.providerId);
1041
+ ctx.renderer.printSuccess(`Refreshed ${models.length} model(s) for ${intent.providerId}.`);
1027
1042
  } catch (err) {
1028
1043
  ctx.renderer.printInfo(`Model refresh failed: ${err instanceof Error ? err.message : String(err)}`);
1029
1044
  }
1030
1045
  return;
1031
1046
  }
1032
- if (sub === "cache") {
1033
- if ((args[1] ?? "").toLowerCase() === "clear") {
1034
- const providerId = args[2];
1035
- ctx.providers.clearModelCache(providerId);
1036
- ctx.renderer.printSuccess(providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache.");
1037
- return;
1038
- }
1047
+ if (intent.kind === "cache-clear") {
1048
+ ctx.providers.clearModelCache(intent.providerId);
1049
+ ctx.renderer.printSuccess(
1050
+ intent.providerId ? `Cleared model cache for ${intent.providerId}.` : "Cleared model cache."
1051
+ );
1052
+ return;
1053
+ }
1054
+ if (intent.kind === "cache-show") {
1039
1055
  const rows = ctx.providers.getModelCacheStatus();
1040
1056
  if (rows.length === 0) {
1041
1057
  ctx.renderer.printInfo("Model cache is empty. Use /model refresh to populate it.");
@@ -1047,9 +1063,11 @@ function createProviderCommands() {
1047
1063
  );
1048
1064
  return;
1049
1065
  }
1066
+ let knownModels = ctx.providers.has(ctx.getCurrentProvider()) ? ctx.providers.get(ctx.getCurrentProvider()).info.models.map((m) => m.id) : [];
1050
1067
  let targetModel;
1051
- if (!args[0]) {
1068
+ if (intent.kind === "select") {
1052
1069
  const models = await ctx.providers.listModels(ctx.getCurrentProvider());
1070
+ knownModels = models.map((m) => m.id);
1053
1071
  const currentModel = ctx.getCurrentModel();
1054
1072
  const items = models.map((m) => ({
1055
1073
  value: m.id,
@@ -1068,22 +1086,27 @@ function createProviderCommands() {
1068
1086
  if (!chosen) return;
1069
1087
  targetModel = chosen;
1070
1088
  } else {
1071
- targetModel = args[0];
1089
+ targetModel = intent.modelId;
1072
1090
  }
1073
- if (targetModel === ctx.getCurrentModel()) {
1074
- ctx.renderer.printInfo(`Already using model: ${targetModel}`);
1091
+ const session = ctx.sessions.current;
1092
+ const outcome = resolveModelSwitch({
1093
+ targetModel,
1094
+ currentModel: ctx.getCurrentModel(),
1095
+ provider: ctx.getCurrentProvider(),
1096
+ knownModels,
1097
+ messageCount: session?.messages.length ?? 0
1098
+ });
1099
+ if (outcome.status === "unchanged") {
1100
+ ctx.renderer.printInfo(outcome.message);
1075
1101
  return;
1076
1102
  }
1077
- const session = ctx.sessions.current;
1078
- const msgCount = session?.messages.length ?? 0;
1079
- ctx.setProvider(ctx.getCurrentProvider(), targetModel);
1103
+ ctx.setProvider(ctx.getCurrentProvider(), outcome.model);
1080
1104
  if (session) {
1081
- session.updateProvider(ctx.getCurrentProvider(), targetModel);
1105
+ session.updateProvider(ctx.getCurrentProvider(), outcome.model);
1082
1106
  await ctx.sessions.save();
1083
1107
  }
1084
- ctx.renderer.printSuccess(
1085
- `Switched to model: ${targetModel}` + (msgCount > 0 ? ` (conversation preserved: ${msgCount} messages)` : "")
1086
- );
1108
+ ctx.renderer.printSuccess(outcome.message);
1109
+ if (outcome.warning) console.log(theme.warning(` ${outcome.warning}`));
1087
1110
  }
1088
1111
  },
1089
1112
  {
@@ -1091,72 +1114,61 @@ function createProviderCommands() {
1091
1114
  description: "Smart model routing \u2014 enable/disable or inspect routing rules",
1092
1115
  usage: "/route [on|off|show|test <message>]",
1093
1116
  async execute(args, ctx) {
1094
- const sub = (args[0] ?? "show").toLowerCase();
1117
+ const intent = parseRouteCommand(args);
1095
1118
  const routing = ctx.config.get("routing");
1096
- if (sub === "on" || sub === "enable") {
1119
+ if (intent.kind === "enable") {
1097
1120
  ctx.config.setByPath("routing.enabled", "true");
1098
1121
  ctx.renderer.printSuccess("Smart model routing enabled.");
1099
- if (!routing || routing.rules.length === 0) {
1100
- ctx.renderer.printInfo(
1101
- 'No rules configured yet. Add rules under `routing.rules` in ~/.aicli/config.json.\nExample: { match: { tag: "fast" }, model: "claude-haiku-4-5" }'
1102
- );
1103
- }
1122
+ const note = routingEmptyRulesNote(routing);
1123
+ if (note) ctx.renderer.printInfo(note);
1104
1124
  return;
1105
1125
  }
1106
- if (sub === "off" || sub === "disable") {
1126
+ if (intent.kind === "disable") {
1107
1127
  ctx.config.setByPath("routing.enabled", "false");
1108
1128
  ctx.renderer.printSuccess("Smart model routing disabled.");
1109
1129
  return;
1110
1130
  }
1111
- if (sub === "test") {
1112
- const msg = args.slice(1).join(" ").trim();
1113
- if (!msg) {
1114
- console.log(theme.warning(" Usage: /route test <message>"));
1115
- return;
1116
- }
1117
- const decision = ctx.computeRoutingDecision(msg);
1118
- const marker = decision.overridden ? theme.accent("\u2192 ROUTED") : theme.dim("(unchanged)");
1119
- console.log();
1120
- console.log(` Input: ${theme.dim(msg)}`);
1121
- console.log(` Current: ${theme.info(ctx.getCurrentModel())}`);
1122
- console.log(` Decision: ${theme.info(decision.model)} ${marker}`);
1123
- console.log(` Reason: ${theme.dim(decision.reason)}`);
1124
- if (typeof decision.ruleIdx === "number") {
1125
- console.log(` Rule: #${decision.ruleIdx}`);
1126
- }
1127
- console.log();
1131
+ if (intent.kind === "usage" || intent.kind === "unknown") {
1132
+ console.log(theme.warning(` ${intent.message}`));
1128
1133
  return;
1129
1134
  }
1130
- if (sub === "show" || sub === "status") {
1131
- console.log();
1132
- console.log(` ${theme.heading("Smart Model Routing")}`);
1133
- console.log(` Status: ${routing?.enabled ? theme.success("enabled") : theme.dim("disabled")}`);
1134
- console.log(` Provider: ${theme.info(ctx.getCurrentProvider())}`);
1135
- console.log(` Current: ${theme.info(ctx.getCurrentModel())}`);
1136
- if (routing?.fallback) {
1137
- console.log(` Fallback: ${theme.info(routing.fallback)}`);
1138
- }
1135
+ if (intent.kind === "test") {
1136
+ const decision = ctx.computeRoutingDecision(intent.message);
1139
1137
  console.log();
1140
- if (!routing || routing.rules.length === 0) {
1141
- console.log(` ${theme.dim("(no rules configured \u2014 edit ~/.aicli/config.json `routing.rules`)")}`);
1142
- } else {
1143
- console.log(` ${theme.heading("Rules")} ${theme.dim(`(evaluated top-to-bottom)`)}:`);
1144
- routing.rules.forEach((r, i) => {
1145
- const parts = [];
1146
- if (r.match.tag) parts.push(`tag=#${r.match.tag}`);
1147
- if (r.match.contains && r.match.contains.length > 0) parts.push(`contains=[${r.match.contains.slice(0, 3).join(", ")}${r.match.contains.length > 3 ? ", \u2026" : ""}]`);
1148
- if (typeof r.match.maxLength === "number") parts.push(`maxLen=${r.match.maxLength}`);
1149
- if (typeof r.match.minLength === "number") parts.push(`minLen=${r.match.minLength}`);
1150
- const cond = parts.length > 0 ? parts.join(" & ") : theme.warning("(empty \u2014 never matches)");
1151
- console.log(` ${theme.dim(`#${i}`)} ${r.name ? theme.accent(r.name) + " " : ""}${cond} ${theme.dim("\u2192")} ${theme.info(r.model)}`);
1152
- });
1138
+ for (const row of routeDecisionRows(intent.message, ctx.getCurrentModel(), decision)) {
1139
+ const pad = `${row.label}:`.padEnd(10);
1140
+ const value = row.label === "Input" || row.label === "Reason" || row.label === "Rule" ? theme.dim(row.value) : theme.info(row.value);
1141
+ console.log(` ${pad}${value}`);
1153
1142
  }
1154
1143
  console.log();
1155
- console.log(` ${theme.dim("Commands: /route on | off | test <msg> | show")}`);
1156
- console.log();
1157
1144
  return;
1158
1145
  }
1159
- console.log(theme.warning(` Unknown subcommand: ${sub}. Usage: /route [on|off|show|test <message>]`));
1146
+ const view = describeRouting({
1147
+ routing,
1148
+ provider: ctx.getCurrentProvider(),
1149
+ currentModel: ctx.getCurrentModel()
1150
+ });
1151
+ console.log();
1152
+ console.log(` ${theme.heading("Smart Model Routing")}`);
1153
+ console.log(` Status: ${view.enabled ? theme.success("enabled") : theme.dim("disabled")}`);
1154
+ console.log(` Provider: ${theme.info(view.provider)}`);
1155
+ console.log(` Current: ${theme.info(view.currentModel)}`);
1156
+ if (view.fallback) console.log(` Fallback: ${theme.info(view.fallback)}`);
1157
+ console.log();
1158
+ if (view.noRulesNote) {
1159
+ console.log(` ${theme.dim(view.noRulesNote)}`);
1160
+ } else {
1161
+ console.log(` ${theme.heading("Rules")} ${theme.dim(`(evaluated top-to-bottom)`)}:`);
1162
+ for (const rule of view.rules) {
1163
+ const cond = rule.empty ? theme.warning(rule.condition) : rule.condition;
1164
+ console.log(
1165
+ ` ${theme.dim(`#${rule.index}`)} ${rule.name ? theme.accent(rule.name) + " " : ""}${cond} ${theme.dim("\u2192")} ${theme.info(rule.model)}`
1166
+ );
1167
+ }
1168
+ }
1169
+ console.log();
1170
+ console.log(` ${theme.dim(view.hint)}`);
1171
+ console.log();
1160
1172
  }
1161
1173
  },
1162
1174
  {
@@ -1190,56 +1202,33 @@ function createProviderCommands() {
1190
1202
  description: "Enter Plan Mode (read-only): AI can only read files and plan, not modify anything",
1191
1203
  usage: "/plan [execute|exit|status]",
1192
1204
  execute(args, ctx) {
1193
- const sub = args[0]?.toLowerCase();
1194
- if (!sub || sub === "enter") {
1195
- if (ctx.getPlanMode()) {
1196
- ctx.renderer.printInfo("Already in Plan Mode. Use /plan execute to start executing, or /plan exit to leave.");
1205
+ const outcome = applyPlanCommand(args[0], ctx.getPlanMode());
1206
+ if (outcome.changed) ctx.setPlanMode(outcome.planMode);
1207
+ switch (outcome.status) {
1208
+ case "entered":
1209
+ console.log();
1210
+ console.log(theme.warning(` ${outcome.summary}`));
1211
+ console.log(theme.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1212
+ for (const line of outcome.details) console.log(theme.dim(` ${line}`));
1213
+ console.log();
1197
1214
  return;
1198
- }
1199
- ctx.setPlanMode(true);
1200
- console.log();
1201
- console.log(theme.warning(" \u{1F4CB} Plan Mode activated"));
1202
- console.log(theme.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1203
- console.log(theme.dim(" AI can now ONLY use read-only tools:"));
1204
- console.log(theme.dim(" read_file \xB7 list_dir \xB7 grep_files \xB7 glob_files"));
1205
- console.log(theme.dim(" web_fetch \xB7 google_search \xB7 ask_user \xB7 write_todos"));
1206
- console.log(theme.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1207
- console.log(theme.dim(" Describe your task and the AI will analyze the codebase"));
1208
- console.log(theme.dim(" and create a detailed implementation plan."));
1209
- console.log(theme.dim(" When ready: /plan execute | To cancel: /plan exit"));
1210
- console.log();
1211
- return;
1212
- }
1213
- if (sub === "execute") {
1214
- if (!ctx.getPlanMode()) {
1215
- ctx.renderer.printInfo("Not in Plan Mode. Enter first with /plan");
1215
+ case "executing":
1216
+ ctx.renderer.printSuccess(outcome.summary);
1216
1217
  return;
1217
- }
1218
- ctx.setPlanMode(false);
1219
- ctx.renderer.printSuccess("Plan Mode ended \u2014 switching to execute mode. AI now has full tool access.");
1220
- return;
1221
- }
1222
- if (sub === "exit" || sub === "cancel") {
1223
- if (!ctx.getPlanMode()) {
1224
- ctx.renderer.printInfo("Not in Plan Mode.");
1218
+ case "already-active":
1219
+ case "not-active":
1220
+ case "cancelled":
1221
+ ctx.renderer.printInfo(outcome.summary);
1225
1222
  return;
1226
- }
1227
- ctx.setPlanMode(false);
1228
- ctx.renderer.printInfo("Plan Mode cancelled. Returning to normal mode.");
1229
- return;
1230
- }
1231
- if (sub === "status") {
1232
- const active = ctx.getPlanMode();
1233
- console.log();
1234
- console.log(` Plan Mode: ${active ? theme.warning("ACTIVE \u{1F4CB}") : theme.dim("inactive")}`);
1235
- if (active) {
1236
- console.log(theme.dim(" Tools restricted to read-only set."));
1237
- console.log(theme.dim(" Use /plan execute to start executing, or /plan exit to cancel."));
1238
- }
1239
- console.log();
1240
- return;
1223
+ case "status":
1224
+ console.log();
1225
+ console.log(` ${outcome.planMode ? theme.warning(outcome.summary) : theme.dim(outcome.summary)}`);
1226
+ for (const line of outcome.details) console.log(theme.dim(` ${line}`));
1227
+ console.log();
1228
+ return;
1229
+ default:
1230
+ ctx.renderer.renderError(outcome.summary);
1241
1231
  }
1242
- ctx.renderer.renderError("Usage: /plan [execute|exit|status]");
1243
1232
  }
1244
1233
  }
1245
1234
  ];
@@ -1408,7 +1397,7 @@ No tools match "${filter}".
1408
1397
  const { join: join6 } = await import("path");
1409
1398
  const { existsSync: existsSync6 } = await import("fs");
1410
1399
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1411
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-WO7ERENL.js");
1400
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-DC3TNLTL.js");
1412
1401
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1413
1402
  const cwd = process.cwd();
1414
1403
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2328,7 +2317,7 @@ function createReviewCommands() {
2328
2317
  usage: "/test [command|filter]",
2329
2318
  async execute(args, ctx) {
2330
2319
  try {
2331
- const { executeTests } = await import("./run-tests-JJ3KOY5E.js");
2320
+ const { executeTests } = await import("./run-tests-R4IQS6IF.js");
2332
2321
  const argStr = args.join(" ").trim();
2333
2322
  let testArgs = {};
2334
2323
  if (argStr) {
@@ -2590,7 +2579,7 @@ function createMemoryCommands() {
2590
2579
  const sub = (args[0] ?? "status").toLowerCase();
2591
2580
  const root = process.cwd();
2592
2581
  const { loadIndex, clearIndex } = await import("./store-MWNHVGJT.js");
2593
- const { indexProject } = await import("./indexer-VZETNE4H.js");
2582
+ const { indexProject } = await import("./indexer-TNDWGFRI.js");
2594
2583
  const { loadVectorStore, clearVectorStore } = await import("./vector-store-BBDXB5IQ.js");
2595
2584
  if (sub === "status") {
2596
2585
  const idx = loadIndex(root);
@@ -4765,7 +4754,7 @@ function removePreviousGrapheme(line, cursor) {
4765
4754
  };
4766
4755
  }
4767
4756
  function installUnicodeBackspaceRepair(input, rl, shouldHandle = () => true) {
4768
- const editable = rl;
4757
+ const editable = rlInternal(rl);
4769
4758
  let previous = { line: editable.line, cursor: editable.cursor };
4770
4759
  const onKeypress = (_character, key) => {
4771
4760
  const current = { line: editable.line, cursor: editable.cursor };
@@ -5543,8 +5532,7 @@ ${ctxLine}` : "")
5543
5532
  showPrompt() {
5544
5533
  this.firstPromptShown = true;
5545
5534
  this.refreshPrompt();
5546
- const rlInternal2 = this.rl;
5547
- rlInternal2.prevRows = 0;
5535
+ rlInternal(this.rl).prevRows = 0;
5548
5536
  this.rl.prompt();
5549
5537
  }
5550
5538
  async start() {
@@ -5690,7 +5678,7 @@ Session '${this.resumeSessionId}' not found.
5690
5678
  }
5691
5679
  void (async () => {
5692
5680
  try {
5693
- const { indexProject } = await import("./indexer-VZETNE4H.js");
5681
+ const { indexProject } = await import("./indexer-TNDWGFRI.js");
5694
5682
  const { loadIndex } = await import("./store-MWNHVGJT.js");
5695
5683
  const root = process.cwd();
5696
5684
  const existed = loadIndex(root);
@@ -5884,10 +5872,10 @@ Session '${this.resumeSessionId}' not found.
5884
5872
  processing = false;
5885
5873
  if (this.running) {
5886
5874
  rlAny.output = savedOutput;
5887
- const rlInternal2 = this.rl;
5888
- rlInternal2.line = "";
5889
- rlInternal2.cursor = 0;
5890
- rlInternal2.paused = false;
5875
+ const internal = rlInternal(this.rl);
5876
+ internal.line = "";
5877
+ internal.cursor = 0;
5878
+ internal.paused = false;
5891
5879
  process.stdin.resume();
5892
5880
  this.showPrompt();
5893
5881
  } else {
@@ -6012,8 +6000,7 @@ Session '${this.resumeSessionId}' not found.
6012
6000
  const t0 = Date.now();
6013
6001
  try {
6014
6002
  const provider = this.providers.get(this.currentProvider);
6015
- const supportsTools = "chatWithTools" in provider;
6016
- if (supportsTools) {
6003
+ if (isToolCapableProvider(provider)) {
6017
6004
  await this.handleChatWithTools(
6018
6005
  provider,
6019
6006
  session.messages,
@@ -6675,11 +6662,10 @@ ${mcpBudgetNote}` : "");
6675
6662
  this.providers,
6676
6663
  fallbackOpts,
6677
6664
  (p, m) => {
6678
- const tc = p;
6679
- if (typeof tc.chatWithToolsStream !== "function") {
6665
+ if (typeof p.chatWithToolsStream !== "function") {
6680
6666
  throw new Error(`provider ${p.info.id} does not support streaming tool calls`);
6681
6667
  }
6682
- return tc.chatWithToolsStream({ ...chatRequest, model: m, signal: streamAc.signal }, toolDefs);
6668
+ return p.chatWithToolsStream({ ...chatRequest, model: m, signal: streamAc.signal }, toolDefs);
6683
6669
  }
6684
6670
  );
6685
6671
  const streamResult = await this.consumeToolStream(streamGen, spinner);
@@ -6707,10 +6693,12 @@ ${mcpBudgetNote}` : "");
6707
6693
  effectiveModel,
6708
6694
  this.providers,
6709
6695
  fallbackOpts,
6710
- (p, m) => p.chatWithTools(
6711
- { ...chatRequest, model: m, signal },
6712
- toolDefs
6713
- )
6696
+ (p, m) => {
6697
+ if (!isToolCapableProvider(p)) {
6698
+ throw new Error(`provider ${p.info.id} does not support tool calls`);
6699
+ }
6700
+ return p.chatWithTools({ ...chatRequest, model: m, signal }, toolDefs);
6701
+ }
6714
6702
  ),
6715
6703
  // 中断:以 host-stop 正常收尾,不为一次主动取消喷 provider 报错
6716
6704
  () => ({ toolCalls: [], content: "", stopLoop: true })
@@ -7278,9 +7266,9 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7278
7266
  select: (prompt, items, initialIndex) => {
7279
7267
  this.selecting = true;
7280
7268
  return selectFromList(prompt, items, initialIndex).finally(() => {
7281
- const rlInternal2 = this.rl;
7282
- rlInternal2.line = "";
7283
- rlInternal2.cursor = 0;
7269
+ const internal = rlInternal(this.rl);
7270
+ internal.line = "";
7271
+ internal.cursor = 0;
7284
7272
  process.stdin.pause();
7285
7273
  setImmediate(() => {
7286
7274
  this.selecting = false;
@@ -7400,9 +7388,8 @@ Tip: You can continue the conversation by asking the AI to proceed.`
7400
7388
  if (!session) return;
7401
7389
  session.addMessage({ role: "user", content: message, timestamp: /* @__PURE__ */ new Date() });
7402
7390
  const provider = this.providers.get(this.currentProvider);
7403
- const toolCapable = provider;
7404
- if (typeof toolCapable.chatWithTools === "function") {
7405
- await this.handleChatWithTools(toolCapable, session.messages);
7391
+ if (isToolCapableProvider(provider)) {
7392
+ await this.handleChatWithTools(provider, session.messages);
7406
7393
  } else {
7407
7394
  await this.handleChatSimple(provider, session.messages);
7408
7395
  }
@@ -7560,7 +7547,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7560
7547
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7561
7548
  process.exit(1);
7562
7549
  }
7563
- const { startWebServer } = await import("./server-JMXC2N3F.js");
7550
+ const { startWebServer } = await import("./server-KAURBKFI.js");
7564
7551
  await startWebServer({ port, host: options.host });
7565
7552
  });
7566
7553
  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) => {
@@ -7727,11 +7714,11 @@ program.command("sessions").description("List recent conversation sessions").opt
7727
7714
  console.log(footer + "\n");
7728
7715
  });
7729
7716
  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) => {
7730
- const { runUsageCli } = await import("./usage-HXJVDETY.js");
7717
+ const { runUsageCli } = await import("./usage-V3R7DA7M.js");
7731
7718
  await runUsageCli(options);
7732
7719
  });
7733
7720
  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) => {
7734
- const { runDoctorCli } = await import("./doctor-cli-BGGILTHE.js");
7721
+ const { runDoctorCli } = await import("./doctor-cli-CDDG5XOY.js");
7735
7722
  const argv = process.argv.slice(2);
7736
7723
  await runDoctorCli({
7737
7724
  json: !!options.json || argv.includes("--json"),
@@ -7740,7 +7727,7 @@ program.command("doctor").description("Health check: API keys, config, MCP, rece
7740
7727
  });
7741
7728
  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) => {
7742
7729
  try {
7743
- const batch = await import("./batch-F7SKIBI3.js");
7730
+ const batch = await import("./batch-7Y4NAA6N.js");
7744
7731
  switch (action) {
7745
7732
  case "submit":
7746
7733
  if (!arg) {
@@ -7783,7 +7770,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7783
7770
  }
7784
7771
  });
7785
7772
  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) => {
7786
- const { startMcpServer } = await import("./server-QJHCJDA7.js");
7773
+ const { startMcpServer } = await import("./server-WXLE53HG.js");
7787
7774
  await startMcpServer({
7788
7775
  allowDestructive: !!options.allowDestructive,
7789
7776
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7796,7 +7783,7 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7796
7783
  console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
7797
7784
  process.exit(1);
7798
7785
  }
7799
- const { runPrCli } = await import("./pr-24REDMGV.js");
7786
+ const { runPrCli } = await import("./pr-GVSZYX6I.js");
7800
7787
  const result = await runPrCli({
7801
7788
  action,
7802
7789
  target,
@@ -7816,8 +7803,8 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7816
7803
  process.exit(result.exitCode);
7817
7804
  });
7818
7805
  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) => {
7819
- const { runCi } = await import("./ci-VURFRMWQ.js");
7820
- const { formatCiResult } = await import("./ci-format-AFJJIURF.js");
7806
+ const { runCi } = await import("./ci-WDF2CYR4.js");
7807
+ const { formatCiResult } = await import("./ci-format-7B5VAZGA.js");
7821
7808
  const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
7822
7809
  if (formats > 1) {
7823
7810
  console.error("Choose only one output format: --json, --markdown, or --sarif.");
@@ -7975,7 +7962,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7975
7962
  config.get("customProviders"),
7976
7963
  config.getConfigDir()
7977
7964
  );
7978
- const { startHub } = await import("./hub-Z73RLNJI.js");
7965
+ const { startHub } = await import("./hub-TO7ODOIA.js");
7979
7966
  await startHub(
7980
7967
  {
7981
7968
  topic: topic ?? "",