jinzd-ai-cli 0.4.277 → 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 (34) hide show
  1. package/dist/{batch-2I5TPZA2.js → batch-7Y4NAA6N.js} +2 -2
  2. package/dist/{chunk-OCOGYIRY.js → chunk-2BYQP6JX.js} +1 -1
  3. package/dist/{chunk-I3PYD2YN.js → chunk-4Z6GAYB5.js} +3 -3
  4. package/dist/{chunk-HCF442WL.js → chunk-H7QC5VMQ.js} +1 -1
  5. package/dist/{chunk-HJVKCOAG.js → chunk-JJCNNQTU.js} +183 -3
  6. package/dist/{chunk-UWQCTTP4.js → chunk-L3CPM472.js} +1 -1
  7. package/dist/{chunk-BYNNKJUC.js → chunk-LOXEYWC3.js} +1 -1
  8. package/dist/{chunk-7IPMONKZ.js → chunk-LTZFUYBT.js} +3 -3
  9. package/dist/{chunk-7YYUITED.js → chunk-LXXQTWT2.js} +1 -1
  10. package/dist/{chunk-7BO5H2QI.js → chunk-RFSKPCYC.js} +1 -1
  11. package/dist/{chunk-3VFOXEIL.js → chunk-SB4XONS4.js} +1 -1
  12. package/dist/{chunk-Z752LDYF.js → chunk-SJCOE4C7.js} +1 -1
  13. package/dist/{chunk-KIGKL3PZ.js → chunk-WOXIKQRI.js} +4 -4
  14. package/dist/{chunk-ZP3RCZKM.js → chunk-X3722RFA.js} +1 -1
  15. package/dist/{ci-JFC45LIS.js → ci-WDF2CYR4.js} +4 -4
  16. package/dist/{ci-format-P3LIZHUR.js → ci-format-7B5VAZGA.js} +2 -2
  17. package/dist/{constants-N3CHKZJY.js → constants-DC3TNLTL.js} +1 -1
  18. package/dist/{doctor-cli-Z3TGHTSX.js → doctor-cli-CDDG5XOY.js} +4 -4
  19. package/dist/electron-server.js +307 -49
  20. package/dist/{hub-NEWIFMQ3.js → hub-TO7ODOIA.js} +1 -1
  21. package/dist/index.js +150 -162
  22. package/dist/{persistent-memory-6Q5FDFJ7.js → persistent-memory-BU63CSGK.js} +2 -2
  23. package/dist/{persistent-memory-ZFR5GI7D.js → persistent-memory-JCENBMCJ.js} +2 -2
  24. package/dist/{pr-ZUGSWQ5R.js → pr-GVSZYX6I.js} +4 -4
  25. package/dist/{run-tests-USV7IJ7X.js → run-tests-2Q5MJHXB.js} +2 -2
  26. package/dist/{run-tests-76M6AC3G.js → run-tests-R4IQS6IF.js} +2 -2
  27. package/dist/{server-C523CIGN.js → server-KAURBKFI.js} +148 -54
  28. package/dist/{server-R7D5RR36.js → server-WXLE53HG.js} +5 -5
  29. package/dist/{task-orchestrator-G2MQDNGQ.js → task-orchestrator-RJ6FXYY5.js} +5 -5
  30. package/dist/{usage-EFHXXMYX.js → usage-V3R7DA7M.js} +2 -2
  31. package/dist/web/client/app.js +4 -1
  32. package/dist/web/client/app.js.br +0 -0
  33. package/dist/web/client/app.js.gz +0 -0
  34. package/package.json +1 -1
@@ -40,14 +40,14 @@ import {
40
40
  touchMemoryReferences,
41
41
  updateMemoryApproval,
42
42
  updateMemoryEntry
43
- } from "./chunk-7YYUITED.js";
43
+ } from "./chunk-LXXQTWT2.js";
44
44
  import {
45
45
  redactJson,
46
46
  scanString
47
47
  } from "./chunk-FSC6KEWU.js";
48
48
  import {
49
49
  runTestsTool
50
- } from "./chunk-BYNNKJUC.js";
50
+ } from "./chunk-LOXEYWC3.js";
51
51
  import {
52
52
  AGENTIC_BEHAVIOR_GUIDELINE,
53
53
  APP_NAME,
@@ -80,7 +80,7 @@ import {
80
80
  SUBAGENT_MAX_ROUNDS_LIMIT,
81
81
  VERSION,
82
82
  buildUserIdentityPrompt
83
- } from "./chunk-Z752LDYF.js";
83
+ } from "./chunk-SJCOE4C7.js";
84
84
  import {
85
85
  hasSemanticIndex,
86
86
  semanticSearch
@@ -15543,48 +15543,259 @@ function applyThinkCommand(sub, state2) {
15543
15543
  return stay(`Unknown /think option "${sub}" \u2014 expected on | off | low | high | max | status`);
15544
15544
  }
15545
15545
 
15546
+ // src/core/provider-command.ts
15547
+ function preservedSuffix(messageCount) {
15548
+ return messageCount > 0 ? ` (conversation preserved: ${messageCount} messages)` : "";
15549
+ }
15550
+ function resolveProviderSwitch(input) {
15551
+ const id = input.targetId;
15552
+ if (!input.configured) {
15553
+ return {
15554
+ status: "not-configured",
15555
+ message: `Provider '${id}' is not configured. Run: aicli config`
15556
+ };
15557
+ }
15558
+ if (input.availableModels.length === 0) {
15559
+ return {
15560
+ status: "no-models",
15561
+ message: `Provider '${id}' has no available models. ` + (id === "ollama" ? "Make sure Ollama is running (`ollama serve`) and has models (`ollama pull <model>`)." : "Check provider configuration.")
15562
+ };
15563
+ }
15564
+ if (id === input.currentProvider) {
15565
+ return { status: "unchanged", message: `Already using provider: ${id}` };
15566
+ }
15567
+ const model = input.configuredDefaultModel || input.providerDefaultModel;
15568
+ return {
15569
+ status: "switched",
15570
+ providerId: id,
15571
+ model,
15572
+ message: `Switched to provider: ${id} (${model})` + preservedSuffix(input.messageCount)
15573
+ };
15574
+ }
15575
+ function parseModelCommand(args, currentProvider) {
15576
+ const raw = args[0];
15577
+ if (!raw) return { kind: "select" };
15578
+ const sub = raw.toLowerCase();
15579
+ if (sub === "refresh") return { kind: "refresh", providerId: args[1] ?? currentProvider };
15580
+ if (sub === "cache") {
15581
+ if ((args[1] ?? "").toLowerCase() === "clear") return { kind: "cache-clear", providerId: args[2] };
15582
+ return { kind: "cache-show" };
15583
+ }
15584
+ return { kind: "set", modelId: raw };
15585
+ }
15586
+ function resolveModelSwitch(input) {
15587
+ if (input.targetModel === input.currentModel) {
15588
+ return { status: "unchanged", message: `Already using model: ${input.targetModel}` };
15589
+ }
15590
+ const known = input.knownModels.includes(input.targetModel);
15591
+ return {
15592
+ status: "switched",
15593
+ model: input.targetModel,
15594
+ message: `Switched to model: ${input.targetModel}` + preservedSuffix(input.messageCount),
15595
+ warning: known ? void 0 : `'${input.targetModel}' is not in the known model list for ${input.provider}. If the provider rejects it, check the spelling or run /model refresh ${input.provider}.`
15596
+ };
15597
+ }
15598
+ var PLAN_MODE_TOOL_HINT = [
15599
+ "read_file \xB7 list_dir \xB7 grep_files \xB7 glob_files",
15600
+ "web_fetch \xB7 google_search \xB7 ask_user \xB7 write_todos"
15601
+ ];
15602
+ function applyPlanCommand(sub, active) {
15603
+ const key = (sub ?? "enter").toLowerCase() === "toggle" ? active ? "exit" : "enter" : (sub ?? "enter").toLowerCase();
15604
+ if (key === "enter") {
15605
+ if (active) {
15606
+ return {
15607
+ planMode: true,
15608
+ changed: false,
15609
+ status: "already-active",
15610
+ summary: "Already in Plan Mode. Use /plan execute to start executing, or /plan exit to leave.",
15611
+ details: []
15612
+ };
15613
+ }
15614
+ return {
15615
+ planMode: true,
15616
+ changed: true,
15617
+ status: "entered",
15618
+ summary: "\u{1F4CB} Plan Mode activated",
15619
+ details: [
15620
+ "AI can now ONLY use read-only tools:",
15621
+ ...PLAN_MODE_TOOL_HINT,
15622
+ "Describe your task and the AI will analyze the codebase and create a detailed implementation plan.",
15623
+ "When ready: /plan execute | To cancel: /plan exit"
15624
+ ]
15625
+ };
15626
+ }
15627
+ if (key === "execute") {
15628
+ if (!active) {
15629
+ return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode. Enter first with /plan", details: [] };
15630
+ }
15631
+ return {
15632
+ planMode: false,
15633
+ changed: true,
15634
+ status: "executing",
15635
+ summary: "Plan Mode ended \u2014 switching to execute mode. AI now has full tool access.",
15636
+ details: []
15637
+ };
15638
+ }
15639
+ if (key === "exit" || key === "cancel") {
15640
+ if (!active) {
15641
+ return { planMode: false, changed: false, status: "not-active", summary: "Not in Plan Mode.", details: [] };
15642
+ }
15643
+ return { planMode: false, changed: true, status: "cancelled", summary: "Plan Mode cancelled. Returning to normal mode.", details: [] };
15644
+ }
15645
+ if (key === "status" || key === "show") {
15646
+ return {
15647
+ planMode: active,
15648
+ changed: false,
15649
+ status: "status",
15650
+ summary: `Plan Mode: ${active ? "ACTIVE \u{1F4CB}" : "inactive"}`,
15651
+ details: active ? ["Tools restricted to read-only set.", "Use /plan execute to start executing, or /plan exit to cancel."] : []
15652
+ };
15653
+ }
15654
+ return {
15655
+ planMode: active,
15656
+ changed: false,
15657
+ status: "unknown",
15658
+ summary: `Unknown /plan option "${sub}" \u2014 expected execute | exit | status`,
15659
+ details: []
15660
+ };
15661
+ }
15662
+ function parseRouteCommand(args) {
15663
+ const sub = (args[0] ?? "show").toLowerCase();
15664
+ if (sub === "on" || sub === "enable") return { kind: "enable" };
15665
+ if (sub === "off" || sub === "disable") return { kind: "disable" };
15666
+ if (sub === "test") {
15667
+ const message = args.slice(1).join(" ").trim();
15668
+ return message ? { kind: "test", message } : { kind: "usage", message: "Usage: /route test <message>" };
15669
+ }
15670
+ if (sub === "show" || sub === "status") return { kind: "show" };
15671
+ return { kind: "unknown", message: `Unknown subcommand: ${sub}. Usage: /route [on|off|show|test <message>]` };
15672
+ }
15673
+ function routingEmptyRulesNote(routing) {
15674
+ if (routing && routing.rules.length > 0) return null;
15675
+ return 'No rules configured yet. Add rules under `routing.rules` in ~/.aicli/config.json.\nExample: { match: { tag: "fast" }, model: "claude-haiku-4-5" }';
15676
+ }
15677
+ function describeRoutingRule(rule, index) {
15678
+ const parts = [];
15679
+ if (rule.match.tag) parts.push(`tag=#${rule.match.tag}`);
15680
+ if (rule.match.contains && rule.match.contains.length > 0) {
15681
+ const shown = rule.match.contains.slice(0, 3).join(", ");
15682
+ parts.push(`contains=[${shown}${rule.match.contains.length > 3 ? ", \u2026" : ""}]`);
15683
+ }
15684
+ if (typeof rule.match.maxLength === "number") parts.push(`maxLen=${rule.match.maxLength}`);
15685
+ if (typeof rule.match.minLength === "number") parts.push(`minLen=${rule.match.minLength}`);
15686
+ const empty = parts.length === 0;
15687
+ return {
15688
+ index,
15689
+ name: rule.name,
15690
+ condition: empty ? "(empty \u2014 never matches)" : parts.join(" & "),
15691
+ model: rule.model,
15692
+ empty
15693
+ };
15694
+ }
15695
+ function describeRouting(input) {
15696
+ const rules = input.routing?.rules ?? [];
15697
+ return {
15698
+ enabled: input.routing?.enabled === true,
15699
+ provider: input.provider,
15700
+ currentModel: input.currentModel,
15701
+ fallback: input.routing?.fallback,
15702
+ rules: rules.map((r, i) => describeRoutingRule(r, i)),
15703
+ noRulesNote: rules.length === 0 ? "(no rules configured \u2014 edit ~/.aicli/config.json `routing.rules`)" : null,
15704
+ hint: "Commands: /route on | off | test <msg> | show"
15705
+ };
15706
+ }
15707
+ function routeDecisionRows(message, currentModel, decision) {
15708
+ const rows = [
15709
+ { label: "Input", value: message },
15710
+ { label: "Current", value: currentModel },
15711
+ { label: "Decision", value: `${decision.model} ${decision.overridden ? "\u2192 ROUTED" : "(unchanged)"}` },
15712
+ { label: "Reason", value: decision.reason }
15713
+ ];
15714
+ if (typeof decision.ruleIdx === "number") rows.push({ label: "Rule", value: `#${decision.ruleIdx}` });
15715
+ return rows;
15716
+ }
15717
+
15546
15718
  // src/web/commands/provider-commands.ts
15719
+ function syncSessionProvider(ctx) {
15720
+ const session = ctx.sessions.current;
15721
+ if (!session) return;
15722
+ session.updateProvider(ctx.currentProvider, ctx.currentModel);
15723
+ ctx.saveIfNeeded();
15724
+ }
15547
15725
  async function handleProvider(args, ctx) {
15548
15726
  const id = args[0];
15549
15727
  if (!id) {
15550
15728
  ctx.send({ type: "error", message: "Usage: /provider <id>" });
15551
15729
  return;
15552
15730
  }
15553
- const p = ctx.providers.get(id);
15554
- if (!p) {
15555
- ctx.send({ type: "error", message: `Provider "${id}" not available` });
15731
+ const configured = ctx.providers.has(id);
15732
+ let availableModels = [];
15733
+ let providerDefaultModel = "";
15734
+ if (configured) {
15735
+ const target = ctx.providers.get(id);
15736
+ providerDefaultModel = target.info.defaultModel;
15737
+ availableModels = target.info.models.map((m) => m.id);
15738
+ if (availableModels.length === 0) {
15739
+ try {
15740
+ availableModels = (await target.listModels()).map((m) => m.id);
15741
+ } catch {
15742
+ availableModels = [];
15743
+ }
15744
+ }
15745
+ }
15746
+ const outcome = resolveProviderSwitch({
15747
+ targetId: id,
15748
+ currentProvider: ctx.currentProvider,
15749
+ configured,
15750
+ availableModels,
15751
+ configuredDefaultModel: ctx.config.get("defaultModels")[id],
15752
+ providerDefaultModel,
15753
+ messageCount: ctx.sessions.current?.messages.length ?? 0
15754
+ });
15755
+ if (outcome.status === "not-configured" || outcome.status === "no-models") {
15756
+ ctx.send({ type: "error", message: outcome.message });
15556
15757
  return;
15557
15758
  }
15558
- ctx.currentProvider = id;
15559
- ctx.currentModel = p.info.defaultModel;
15759
+ if (outcome.status === "unchanged") {
15760
+ ctx.send({ type: "info", message: outcome.message });
15761
+ return;
15762
+ }
15763
+ ctx.currentProvider = outcome.providerId;
15764
+ ctx.currentModel = outcome.model;
15560
15765
  ctx.updateContextWindow();
15561
- ctx.send({ type: "info", message: `Switched to provider: ${id} (${ctx.currentModel})` });
15766
+ syncSessionProvider(ctx);
15767
+ ctx.send({ type: "info", message: outcome.message });
15562
15768
  ctx.sendStatus();
15563
15769
  }
15564
15770
  async function handleModel(args, ctx) {
15565
- const sub = args[0];
15566
- if (!sub) {
15771
+ const intent = parseModelCommand(args, ctx.currentProvider);
15772
+ if (intent.kind === "select") {
15567
15773
  ctx.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
15568
15774
  return;
15569
15775
  }
15570
- if (sub === "refresh") {
15571
- const providerId = args[1] ?? ctx.currentProvider;
15776
+ if (intent.kind === "refresh") {
15777
+ if (!ctx.providers.has(intent.providerId)) {
15778
+ ctx.send({ type: "error", message: `Provider is not configured: ${intent.providerId}` });
15779
+ return;
15780
+ }
15572
15781
  try {
15573
- const models2 = await ctx.providers.refreshModels(providerId);
15574
- ctx.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
15782
+ const models2 = await ctx.providers.refreshModels(intent.providerId);
15783
+ ctx.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${intent.providerId}.` });
15575
15784
  ctx.sendStatus();
15576
15785
  } catch (err) {
15577
15786
  ctx.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
15578
15787
  }
15579
15788
  return;
15580
15789
  }
15581
- if (sub === "cache") {
15582
- if ((args[1] ?? "").toLowerCase() === "clear") {
15583
- const providerId = args[2];
15584
- ctx.providers.clearModelCache(providerId);
15585
- ctx.send({ type: "info", message: providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache." });
15586
- return;
15587
- }
15790
+ if (intent.kind === "cache-clear") {
15791
+ ctx.providers.clearModelCache(intent.providerId);
15792
+ ctx.send({
15793
+ type: "info",
15794
+ message: intent.providerId ? `Cleared model cache for ${intent.providerId}.` : "Cleared model cache."
15795
+ });
15796
+ return;
15797
+ }
15798
+ if (intent.kind === "cache-show") {
15588
15799
  const rows = ctx.providers.getModelCacheStatus();
15589
15800
  ctx.send({
15590
15801
  type: "info",
@@ -15592,16 +15803,25 @@ async function handleModel(args, ctx) {
15592
15803
  });
15593
15804
  return;
15594
15805
  }
15595
- const modelId = sub;
15596
15806
  const models = await ctx.providers.listModels(ctx.currentProvider);
15597
- const found = models.find((m) => m.id === modelId);
15598
- if (!found) {
15599
- ctx.send({ type: "error", message: `Model "${modelId}" not found` });
15807
+ const outcome = resolveModelSwitch({
15808
+ targetModel: intent.modelId,
15809
+ currentModel: ctx.currentModel,
15810
+ provider: ctx.currentProvider,
15811
+ knownModels: models.map((m) => m.id),
15812
+ messageCount: ctx.sessions.current?.messages.length ?? 0
15813
+ });
15814
+ if (outcome.status === "unchanged") {
15815
+ ctx.send({ type: "info", message: outcome.message });
15600
15816
  return;
15601
15817
  }
15602
- ctx.currentModel = modelId;
15818
+ ctx.currentModel = outcome.model;
15603
15819
  ctx.updateContextWindow();
15604
- ctx.send({ type: "info", message: `Switched to model: ${modelId}` });
15820
+ syncSessionProvider(ctx);
15821
+ ctx.send({
15822
+ type: "info",
15823
+ message: outcome.warning ? `${outcome.message} \u2014 ${outcome.warning}` : outcome.message
15824
+ });
15605
15825
  ctx.sendStatus();
15606
15826
  }
15607
15827
  async function handleThink(args, ctx) {
@@ -15611,7 +15831,7 @@ async function handleThink(args, ctx) {
15611
15831
  runtimeEffort: ctx.runtimeThinkingEffort,
15612
15832
  configuredThinking: modelParams?.thinking,
15613
15833
  configuredEffort: modelParams?.thinkingEffort,
15614
- supportsEffort: ctx.providers.get(ctx.currentProvider)?.supportsReasoningEffort ?? false,
15834
+ supportsEffort: ctx.providers.has(ctx.currentProvider) ? ctx.providers.get(ctx.currentProvider).supportsReasoningEffort : false,
15615
15835
  provider: ctx.currentProvider
15616
15836
  });
15617
15837
  if (outcome.changed) {
@@ -15625,24 +15845,59 @@ async function handleThink(args, ctx) {
15625
15845
  if (outcome.changed) ctx.sendStatus();
15626
15846
  }
15627
15847
  async function handlePlan(args, ctx) {
15628
- const sub = args[0];
15629
- if (sub === "exit" || sub === "cancel") {
15630
- ctx.planMode = false;
15631
- ctx.send({ type: "info", message: "Plan mode OFF." });
15632
- } else if (sub === "execute") {
15633
- ctx.planMode = false;
15634
- ctx.send({ type: "info", message: "Plan mode OFF. Executing with all tools enabled." });
15635
- } else if (sub === "status" || sub === "show") {
15636
- ctx.send({ type: "info", message: `Plan mode: ${ctx.planMode ? "ON (read-only tools only)" : "OFF"}` });
15848
+ const outcome = applyPlanCommand(args[0], ctx.planMode);
15849
+ if (outcome.changed) ctx.planMode = outcome.planMode;
15850
+ ctx.send({
15851
+ type: outcome.status === "unknown" ? "error" : "info",
15852
+ message: [outcome.summary, ...outcome.details].join(" \u2014 ")
15853
+ });
15854
+ if (outcome.changed) ctx.sendStatus();
15855
+ }
15856
+ async function handleRoute(args, ctx) {
15857
+ const intent = parseRouteCommand(args);
15858
+ const routing = ctx.config.get("routing");
15859
+ if (intent.kind === "enable" || intent.kind === "disable") {
15860
+ const on = intent.kind === "enable";
15861
+ ctx.config.setByPath("routing.enabled", on ? "true" : "false");
15862
+ const note = on ? routingEmptyRulesNote(routing) : null;
15863
+ ctx.send({
15864
+ type: "info",
15865
+ message: `Smart model routing ${on ? "enabled" : "disabled"}.` + (note ? `
15866
+ ${note}` : "")
15867
+ });
15868
+ ctx.sendStatus();
15869
+ return;
15870
+ }
15871
+ if (intent.kind === "usage" || intent.kind === "unknown") {
15872
+ ctx.send({ type: "error", message: intent.message });
15637
15873
  return;
15638
- } else if (sub !== void 0) {
15639
- ctx.send({ type: "error", message: `Unknown /plan option "${sub}" \u2014 expected execute | exit | status` });
15874
+ }
15875
+ if (intent.kind === "test") {
15876
+ const decision = ctx.computeRoutingDecision(intent.message);
15877
+ ctx.send({
15878
+ type: "info",
15879
+ message: routeDecisionRows(intent.message, ctx.currentModel, decision).map((row) => `${row.label}: ${row.value}`).join("\n")
15880
+ });
15640
15881
  return;
15882
+ }
15883
+ const view = describeRouting({ routing, provider: ctx.currentProvider, currentModel: ctx.currentModel });
15884
+ const lines = [
15885
+ "Smart Model Routing",
15886
+ `Status: ${view.enabled ? "enabled" : "disabled"}`,
15887
+ `Provider: ${view.provider}`,
15888
+ `Current: ${view.currentModel}`
15889
+ ];
15890
+ if (view.fallback) lines.push(`Fallback: ${view.fallback}`);
15891
+ if (view.noRulesNote) {
15892
+ lines.push(view.noRulesNote);
15641
15893
  } else {
15642
- ctx.planMode = !ctx.planMode;
15643
- ctx.send({ type: "info", message: `Plan mode: ${ctx.planMode ? "ON (read-only tools only)" : "OFF"}` });
15894
+ lines.push("Rules (evaluated top-to-bottom):");
15895
+ for (const rule of view.rules) {
15896
+ lines.push(` #${rule.index} ${rule.name ? `${rule.name} ` : ""}${rule.condition} \u2192 ${rule.model}`);
15897
+ }
15644
15898
  }
15645
- ctx.sendStatus();
15899
+ lines.push(view.hint);
15900
+ ctx.send({ type: "info", message: lines.join("\n") });
15646
15901
  }
15647
15902
 
15648
15903
  // src/core/session-cost.ts
@@ -16175,7 +16430,7 @@ Cache: write=${cacheCreate} read=${cacheRead}` : "";
16175
16430
  Cost: ${formatCost(cost)}` : "";
16176
16431
  let memoryLine = "";
16177
16432
  try {
16178
- const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-ZFR5GI7D.js");
16433
+ const { countPendingMemories: countPendingMemories2 } = await import("./persistent-memory-JCENBMCJ.js");
16179
16434
  const pending = countPendingMemories2(ctx.config.getConfigDir());
16180
16435
  if (pending > 0) memoryLine = `
16181
16436
  Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
@@ -16988,7 +17243,7 @@ async function handleTest(args, ctx) {
16988
17243
  const isCommand = argStr.includes(" ") || /^(mvn|gradle|npm|pytest|cargo|go)\b/.test(argStr);
16989
17244
  testArgs = isCommand ? { command: argStr } : { filter: argStr };
16990
17245
  }
16991
- const runTests = ctx.runTests ?? (await import("./run-tests-USV7IJ7X.js")).executeTests;
17246
+ const runTests = ctx.runTests ?? (await import("./run-tests-2Q5MJHXB.js")).executeTests;
16992
17247
  const report = await runTests(testArgs);
16993
17248
  ctx.send({ type: "info", message: report });
16994
17249
  } catch (err) {
@@ -17324,7 +17579,7 @@ async function handleMemory(args, ctx) {
17324
17579
  ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
17325
17580
  } else if (sub === "export") {
17326
17581
  if (args[1] === "md") {
17327
- const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-ZFR5GI7D.js");
17582
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-JCENBMCJ.js");
17328
17583
  ctx.send({
17329
17584
  type: "export_data",
17330
17585
  format: "md",
@@ -17766,7 +18021,8 @@ async function handleHelp(_args, ctx) {
17766
18021
  " /clear \u2014 Clear conversation & start new session",
17767
18022
  " /compact [hint] \u2014 Compress conversation history",
17768
18023
  " /think [on|off] \u2014 Toggle extended thinking mode",
17769
- " /plan [enter|exit] \u2014 Toggle read-only planning mode",
18024
+ " /plan [execute|exit|status] \u2014 Read-only planning mode (bare /plan enters it)",
18025
+ " /route [on|off|show|test <msg>] \u2014 Smart model routing",
17770
18026
  " /session new|list|load|delete <id> \u2014 Session management",
17771
18027
  " /system [prompt|clear] \u2014 Set/view/reset system prompt",
17772
18028
  " /context [status|reload] \u2014 Show/reload context layers",
@@ -17903,8 +18159,9 @@ var CLI_ONLY_WEB_COMMANDS = /* @__PURE__ */ new Map([
17903
18159
  ["snapshot", "Dev-state snapshots are written when the terminal REPL exits."],
17904
18160
  ["hooks", "Hook trust is granted per machine from the terminal."],
17905
18161
  ["hook", "Hook trust is granted per machine from the terminal."],
17906
- // 以下两条并非「终端才有意义」,而是 Web 侧尚未实现——如实说明,别假装是设计。
17907
- ["route", "Smart model routing is not applied to Web sessions yet (config.routing is REPL-only)."],
18162
+ // 以下一条并非「终端才有意义」,而是 Web 侧尚未实现——如实说明,别假装是设计。
18163
+ // (`route` 曾在这里,理由写的是「Web 尚未应用智能路由」;v0.4.269 让 routing 在 Web
18164
+ // 上真正生效之后那句话就不再成立,命令已在 Web 实现,见 commands/provider-commands.ts。)
17908
18165
  ["security", "Redaction status and toggles are terminal-only for now; /status shows the current profile."]
17909
18166
  ]);
17910
18167
  var webCommandHandlers = {
@@ -17912,6 +18169,7 @@ var webCommandHandlers = {
17912
18169
  model: handleModel,
17913
18170
  think: handleThink,
17914
18171
  plan: handlePlan,
18172
+ route: handleRoute,
17915
18173
  clear: handleClear,
17916
18174
  compact: handleCompact,
17917
18175
  status: handleStatus,
@@ -158,7 +158,7 @@ ${content}`);
158
158
  }
159
159
  }
160
160
  async function runTaskMode(config, providers, configManager, topic) {
161
- const { TaskOrchestrator } = await import("./task-orchestrator-G2MQDNGQ.js");
161
+ const { TaskOrchestrator } = await import("./task-orchestrator-RJ6FXYY5.js");
162
162
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
163
163
  let interrupted = false;
164
164
  const onSigint = () => {