@bike4mind/cli 0.10.0 → 0.10.2

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/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CommandHistoryStore, A as formatStep, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, G as OllamaBackend, H as clearFeatureModuleTools, I as generateCliTools, J as buildSkillsPromptSection, K as getPlanModeFilePath, L as ALWAYS_DENIED_FOR_AGENTS, M as loadContextFiles, N as getApiUrl, O as McpManager, P as getEnvironmentName, Q as CheckpointStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, a as createBlockerTools, at as mergeCommands, b as createSkillTool, c as createDecisionStore, ct as warmFileCache, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as searchCommands, j as extractCompactInstructions, k as substituteArguments, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as hasFileReferences, o as formatBlockersOutput, ot as formatFileSize, p as createWriteTodosTool, q as buildSystemPrompt, r as formatReviewGatesOutput, rt as processFileReferences, s as createDecisionLogTool, st as searchFiles, t as createReviewGateStore, tt as OAuthClient, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-_X4rUM4L.mjs";
2
+ import { $ as SessionStore, A as formatStep, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, G as getPlanModeFilePath, H as clearFeatureModuleTools, I as generateCliTools, J as isReadOnlyTool, K as buildSystemPrompt, L as ALWAYS_DENIED_FOR_AGENTS, M as loadContextFiles, N as getApiUrl, O as McpManager, P as getEnvironmentName, Q as CommandHistoryStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as CustomCommandStore, Y as ReActAgent, Z as CheckpointStore, _ as createAgentDelegateTool, a as createBlockerTools, at as formatFileSize, b as createSkillTool, c as createDecisionStore, d as createFindDefinitionTool, et as OAuthClient, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as mergeCommands, j as extractCompactInstructions, k as substituteArguments, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as processFileReferences, o as formatBlockersOutput, ot as searchFiles, p as createWriteTodosTool, q as buildSkillsPromptSection, r as formatReviewGatesOutput, rt as searchCommands, s as createDecisionLogTool, st as warmFileCache, t as createReviewGateStore, tt as hasFileReferences, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-t-9dtjjl.mjs";
3
3
  import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-DV5s-qni.mjs";
4
- import { Xt as validateNotebookPath$1, Yt as validateJupyterKernelName, g as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore, v as ChatModels } from "./ConfigStore-DPWN3-0c.mjs";
5
- import { a as version, t as checkForUpdate } from "./updateChecker-C3DYG0Gn.mjs";
4
+ import { Ht as validateNotebookPath$1, Vt as validateJupyterKernelName, g as ChatModels, m as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore } from "./ConfigStore-BX6XcTa1.mjs";
5
+ import { a as version, t as checkForUpdate } from "./updateChecker-DdgysXM8.mjs";
6
6
  import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
7
7
  import { Box, Static, Text, render, useApp, useInput, usePaste, useStdout } from "ink";
8
8
  import { execSync } from "child_process";
@@ -22,6 +22,7 @@ import SelectInput from "ink-select-input";
22
22
  import jwt from "jsonwebtoken";
23
23
  import open from "open";
24
24
  import axios, { isAxiosError } from "axios";
25
+ import { OllamaBackend } from "@bike4mind/llm-adapters";
25
26
  import { get_encoding } from "tiktoken";
26
27
  import WsWebSocket from "ws";
27
28
  //#region src/components/StatusBar.tsx
@@ -3761,6 +3762,196 @@ Unlike agent_delegate (which uses pre-defined agents), this tool lets you compos
3761
3762
  };
3762
3763
  }
3763
3764
  //#endregion
3765
+ //#region src/tools/deferredToolRegistry.ts
3766
+ /**
3767
+ * Registry of tool schemas that are NOT loaded into the model's initial tool
3768
+ * list. The model sees only the names (via the system prompt directory) and
3769
+ * must call the `tool_search` meta-tool to load schemas on demand.
3770
+ *
3771
+ * This mirrors Claude Code's deferred-tool pattern. The win is large for
3772
+ * heavy MCP integrations (e.g. 41 GitHub MCP tools at ~250-350 tokens of
3773
+ * JSONSchema each = ~10-15k tokens per turn that's now ~1-1.5k of names).
3774
+ */
3775
+ var DeferredToolRegistry = class {
3776
+ constructor() {
3777
+ this.byName = /* @__PURE__ */ new Map();
3778
+ }
3779
+ /** Replace registry contents with the supplied tools. Idempotent. */
3780
+ register(tools) {
3781
+ this.byName.clear();
3782
+ for (const tool of tools) this.byName.set(tool.toolSchema.name, tool);
3783
+ logger.debug(`[DeferredToolRegistry] Registered ${tools.length} deferred tool(s)`);
3784
+ }
3785
+ clear() {
3786
+ this.byName.clear();
3787
+ }
3788
+ size() {
3789
+ return this.byName.size;
3790
+ }
3791
+ has(name) {
3792
+ return this.byName.has(name);
3793
+ }
3794
+ get(name) {
3795
+ return this.byName.get(name);
3796
+ }
3797
+ getAll() {
3798
+ return Array.from(this.byName.values());
3799
+ }
3800
+ /** Return tools whose names appear in the supplied list, in input order. */
3801
+ getByNames(names) {
3802
+ const found = [];
3803
+ for (const name of names) {
3804
+ const tool = this.byName.get(name);
3805
+ if (tool) found.push(tool);
3806
+ }
3807
+ return found;
3808
+ }
3809
+ /**
3810
+ * Rank-search deferred tools by query terms. Name matches outrank
3811
+ * description matches; exact substring on name wins ties.
3812
+ */
3813
+ searchByKeywords(query, maxResults) {
3814
+ const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
3815
+ if (terms.length === 0) return [];
3816
+ const scored = [];
3817
+ for (const tool of this.byName.values()) {
3818
+ const name = tool.toolSchema.name.toLowerCase();
3819
+ const desc = (tool.toolSchema.description || "").toLowerCase();
3820
+ let score = 0;
3821
+ for (const term of terms) {
3822
+ if (name.includes(term)) score += 10;
3823
+ if (desc.includes(term)) score += 1;
3824
+ }
3825
+ if (score > 0) scored.push({
3826
+ tool,
3827
+ score
3828
+ });
3829
+ }
3830
+ scored.sort((a, b) => b.score - a.score);
3831
+ return scored.slice(0, maxResults).map((s) => s.tool);
3832
+ }
3833
+ /** Return the directory entries used to render the system-prompt reminder. */
3834
+ getDirectoryNames() {
3835
+ return Array.from(this.byName.keys()).sort();
3836
+ }
3837
+ };
3838
+ const deferredToolRegistry = new DeferredToolRegistry();
3839
+ //#endregion
3840
+ //#region src/tools/toolSearchTool.ts
3841
+ /**
3842
+ * Default number of tools returned for a keyword search. Matches Claude
3843
+ * Code's ToolSearch convention. 5 keeps the response payload small while
3844
+ * surfacing enough alternatives for the model to refine its query.
3845
+ */
3846
+ const DEFAULT_MAX_RESULTS = 5;
3847
+ /**
3848
+ * Runtime validation for tool_search params. The LLM produces these
3849
+ * values, so we validate at this boundary rather than trusting the
3850
+ * shape. Coerces `max_results` from string→number for models that emit
3851
+ * numeric-looking strings.
3852
+ */
3853
+ const ToolSearchParamsSchema = z.object({
3854
+ query: z.string().min(1, "query must be a non-empty string"),
3855
+ max_results: z.coerce.number().int().min(1).max(20).optional()
3856
+ });
3857
+ /**
3858
+ * Parse the query string. Two forms:
3859
+ * - `select:name1,name2,...` — exact-name selection
3860
+ * - free text — keyword search across name + description
3861
+ */
3862
+ function parseQuery(query) {
3863
+ const trimmed = query.trim();
3864
+ const selectMatch = trimmed.match(/^select:(.+)$/i);
3865
+ if (selectMatch) return {
3866
+ mode: "select",
3867
+ names: selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0)
3868
+ };
3869
+ return {
3870
+ mode: "search",
3871
+ text: trimmed
3872
+ };
3873
+ }
3874
+ /**
3875
+ * Format the loaded-tools response. Mirrors Claude Code's convention:
3876
+ * one <function>{...}</function> line per matched tool. The model has
3877
+ * already seen this format in its tool-registration system messages, so
3878
+ * it parses without additional explanation.
3879
+ *
3880
+ * Note: the schemas are *also* injected into context.tools by the caller,
3881
+ * so on the next iteration the model gets them as native tool definitions.
3882
+ * The text response here is for in-turn awareness and audit trail.
3883
+ */
3884
+ function renderToolsBlock(tools) {
3885
+ if (tools.length === 0) return "";
3886
+ return `<functions>\n${tools.map((tool) => {
3887
+ const schema = {
3888
+ description: tool.toolSchema.description,
3889
+ name: tool.toolSchema.name,
3890
+ parameters: tool.toolSchema.parameters
3891
+ };
3892
+ return `<function>${JSON.stringify(schema)}</function>`;
3893
+ }).join("\n")}\n</functions>`;
3894
+ }
3895
+ /**
3896
+ * Build the tool_search meta-tool. The returned tool has a closure over
3897
+ * the supplied `toolListAccessor`, which it uses to push newly-resolved
3898
+ * tool schemas into the live agent context.
3899
+ *
3900
+ * Idempotent: re-loading a tool that's already in the context is a no-op.
3901
+ */
3902
+ function createToolSearchTool(toolListAccessor) {
3903
+ return {
3904
+ toolSchema: {
3905
+ name: "tool_search",
3906
+ description: "Fetches full schema definitions for deferred tools so they can be called. Deferred tools appear by name only in a system reminder; their parameter schemas are NOT loaded by default. Use this tool to load schemas on demand. Query forms: 'select:name1,name2' for exact selection, or free-text keywords to search by name and description. Once a tool's schema is returned, it becomes callable in subsequent turns.",
3907
+ parameters: {
3908
+ type: "object",
3909
+ properties: {
3910
+ query: {
3911
+ type: "string",
3912
+ description: "Either 'select:<comma-separated names>' to fetch specific tools, or free-text keywords (e.g. 'github pull request') to rank-search deferred tools."
3913
+ },
3914
+ max_results: {
3915
+ type: "number",
3916
+ description: `Maximum number of tools to return for keyword search. Defaults to ${DEFAULT_MAX_RESULTS}. Ignored for 'select:' queries.`
3917
+ }
3918
+ },
3919
+ required: ["query"]
3920
+ }
3921
+ },
3922
+ toolFn: async (params) => {
3923
+ const parsedParams = ToolSearchParamsSchema.safeParse(params ?? {});
3924
+ if (!parsedParams.success) {
3925
+ const issue = parsedParams.error.issues[0];
3926
+ return `tool_search: invalid parameters — ${issue.path.join(".") || "params"}: ${issue.message}`;
3927
+ }
3928
+ const { query, max_results } = parsedParams.data;
3929
+ const parsed = parseQuery(query);
3930
+ let matched;
3931
+ let unmatched = [];
3932
+ if (parsed.mode === "select") {
3933
+ matched = deferredToolRegistry.getByNames(parsed.names);
3934
+ const foundNames = new Set(matched.map((t) => t.toolSchema.name));
3935
+ unmatched = parsed.names.filter((n) => !foundNames.has(n));
3936
+ } else {
3937
+ const max = max_results ?? DEFAULT_MAX_RESULTS;
3938
+ matched = deferredToolRegistry.searchByKeywords(parsed.text, max);
3939
+ }
3940
+ if (matched.length === 0) return parsed.mode === "select" ? `tool_search: no deferred tools matched ${parsed.names.join(", ")}. Use a free-text query to search.` : `tool_search: no deferred tools matched query "${parsed.text}".`;
3941
+ const liveTools = toolListAccessor();
3942
+ const liveNames = new Set(liveTools.map((t) => t.toolSchema.name));
3943
+ let added = 0;
3944
+ for (const tool of matched) if (!liveNames.has(tool.toolSchema.name)) {
3945
+ liveTools.push(tool);
3946
+ added++;
3947
+ }
3948
+ logger.debug(`[tool_search] query="${query}" matched=${matched.length} added=${added} alreadyLoaded=${matched.length - added}`);
3949
+ const block = renderToolsBlock(matched);
3950
+ return `${`Loaded ${added} new tool schema(s)${added < matched.length ? ` (${matched.length - added} already loaded)` : ""}. These are now callable in your next message.${unmatched.length > 0 ? `\n\nNot found: ${unmatched.join(", ")}` : ""}`}\n\n${block}`;
3951
+ }
3952
+ };
3953
+ }
3954
+ //#endregion
3764
3955
  //#region src/features/FeatureModuleRegistry.ts
3765
3956
  /**
3766
3957
  * Manages the lifecycle of opt-in CLI feature modules.
@@ -5976,10 +6167,23 @@ function CliApp() {
5976
6167
  loadContextFiles(agentProjectDir)
5977
6168
  ]);
5978
6169
  const mcpTools = mcpManager.getTools();
6170
+ const deferredB4mToolNames = new Set([
6171
+ "math_evaluate",
6172
+ "dice_roll",
6173
+ "current_datetime",
6174
+ "recent_changes",
6175
+ "prompt_enhancement"
6176
+ ]);
6177
+ const deferredB4mTools = b4mTools.filter((t) => deferredB4mToolNames.has(t.toolSchema.name));
6178
+ const loadedB4mTools = b4mTools.filter((t) => !deferredB4mToolNames.has(t.toolSchema.name));
6179
+ deferredToolRegistry.register([...mcpTools, ...deferredB4mTools]);
5979
6180
  if (mcpTools.length > 0) {
5980
6181
  const serverSummaries = mcpManager.getToolCount().map((s) => `${s.serverName} (${s.count})`).join(", ");
5981
- startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M + ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
5982
- } else startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M tool(s), no MCP tools`);
6182
+ startupLog.push(`🛠️ Loaded ${loadedB4mTools.length} B4M + ${mcpTools.length} MCP tool(s, ${deferredB4mTools.length + mcpTools.length} deferred): ${serverSummaries}`);
6183
+ } else {
6184
+ const suffix = deferredB4mTools.length > 0 ? ` (${deferredB4mTools.length} deferred)` : "";
6185
+ startupLog.push(`🛠️ Loaded ${loadedB4mTools.length} B4M tool(s)${suffix}, no MCP tools`);
6186
+ }
5983
6187
  const agentSummary = agentStore.getSummary();
5984
6188
  startupLog.push(`🤖 Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`);
5985
6189
  const orchestrator = new SubagentOrchestrator({
@@ -6053,9 +6257,14 @@ function CliApp() {
6053
6257
  cliTools.push(coordinateTaskTool);
6054
6258
  }
6055
6259
  const featureTools = featureRegistry.getAllTools();
6260
+ const agentToolsRef = { current: null };
6261
+ const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool(() => {
6262
+ if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
6263
+ return agentToolsRef.current;
6264
+ }) : null;
6056
6265
  const allTools = [
6057
- ...b4mTools,
6058
- ...mcpTools,
6266
+ ...loadedB4mTools,
6267
+ ...toolSearchTool ? [toolSearchTool] : [],
6059
6268
  ...cliTools,
6060
6269
  ...featureTools
6061
6270
  ];
@@ -6070,7 +6279,7 @@ function CliApp() {
6070
6279
  const moduleNames = featureRegistry.getModuleNames().join(", ");
6071
6280
  startupLog.push(`🏰 Feature modules: ${moduleNames} (${featureTools.length} tools)`);
6072
6281
  }
6073
- logger.debug(`Total tools available to agent: ${allTools.length} (${b4mTools.length} B4M + ${mcpTools.length} MCP + ${cliTools.length} CLI + ${featureTools.length} feature)`);
6282
+ logger.debug(`Total tools available to agent: ${allTools.length} (${loadedB4mTools.length} B4M loaded + ${cliTools.length} CLI + ${featureTools.length} feature + ${toolSearchTool ? 1 : 0} tool_search, ${deferredB4mTools.length} B4M + ${mcpTools.length} MCP deferred)`);
6074
6283
  if (contextResult.globalContext) startupLog.push(`📄 Global context: ${contextResult.globalContext.filename}`);
6075
6284
  if (contextResult.projectContext) startupLog.push(`📄 Project context: ${contextResult.projectContext.filename}`);
6076
6285
  for (const error of contextResult.errors) startupLog.push(`⚠️ Context file error: ${error}`);
@@ -6084,7 +6293,8 @@ function CliApp() {
6084
6293
  enableDynamicAgentCreation: config.preferences.enableDynamicAgentCreation === true,
6085
6294
  additionalDirectories,
6086
6295
  featureModulePrompts: featureModulePrompts || void 0,
6087
- planModeFilePath: mode === "plan" ? getPlanModeFilePath(newSession.id) : void 0
6296
+ planModeFilePath: mode === "plan" ? getPlanModeFilePath(newSession.id) : void 0,
6297
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
6088
6298
  });
6089
6299
  const cliSystemPrompt = buildPromptForMode(useCliStore.getState().interactionMode);
6090
6300
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
@@ -6097,16 +6307,17 @@ function CliApp() {
6097
6307
  maxIterations,
6098
6308
  maxTokens: config.preferences.maxTokens,
6099
6309
  temperature: config.preferences.temperature,
6100
- systemPrompt: cliSystemPrompt
6310
+ systemPrompt: cliSystemPrompt,
6311
+ unknownToolResolver: async (toolName) => deferredToolRegistry.get(toolName) ?? null
6101
6312
  });
6313
+ agentToolsRef.current = agent.getTools();
6102
6314
  agentContext.currentAgent = agent;
6103
- const agentInternalContext = agent.context;
6104
6315
  let lastInteractionMode = useCliStore.getState().interactionMode;
6105
6316
  useCliStore.subscribe((s) => {
6106
6317
  if (s.interactionMode === lastInteractionMode) return;
6107
6318
  lastInteractionMode = s.interactionMode;
6108
6319
  if (agentContext.currentAgent !== agent) return;
6109
- agentInternalContext.systemPrompt = buildPromptForMode(s.interactionMode);
6320
+ agent.setSystemPrompt(buildPromptForMode(s.interactionMode));
6110
6321
  });
6111
6322
  agent.observationQueue = agentContext.observationQueue;
6112
6323
  const stepHandler = (step) => {
@@ -6562,7 +6773,8 @@ function CliApp() {
6562
6773
  customCommands: state.customCommandStore.getAllCommands(),
6563
6774
  enableSkillTool: config?.preferences.enableSkillTool !== false,
6564
6775
  additionalDirectories: state.additionalDirectories,
6565
- featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0
6776
+ featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0,
6777
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
6566
6778
  });
6567
6779
  if (tokenCounter.countSessionTokens(activeSession, systemPrompt).totalTokens >= threshold) {
6568
6780
  console.log("\n⚠️ Context window 80% full. Auto-compacting...\n");
@@ -7753,7 +7965,7 @@ Multi-line Input:
7753
7965
  const skillsTokens = skillsSection ? tokenCounter.countTokens(skillsSection) : 0;
7754
7966
  const agentDirectoryTokens = state.agentStore ? tokenCounter.countTokens(state.agentStore.getDirectoryContext()) : 0;
7755
7967
  const mcpTools = state.mcpManager?.getTools() || [];
7756
- const mcpToolsTokens = tokenCounter.countToolSchemaTokens(mcpTools);
7968
+ const deferredNames = deferredToolRegistry.getDirectoryNames();
7757
7969
  const mcpToolCount = state.mcpManager?.getToolCount() || [];
7758
7970
  const systemPrompt = buildSystemPrompt(variantForCount, {
7759
7971
  contextContent: state.contextContent,
@@ -7761,10 +7973,13 @@ Multi-line Input:
7761
7973
  customCommands: commands,
7762
7974
  enableSkillTool: state.config?.preferences.enableSkillTool !== false,
7763
7975
  additionalDirectories: state.additionalDirectories,
7764
- featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0
7976
+ featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0,
7977
+ deferredToolNames: deferredNames
7765
7978
  });
7766
7979
  const usage = tokenCounter.countSessionTokens(state.session, systemPrompt);
7767
- const totalWithTools = usage.totalTokens + mcpToolsTokens;
7980
+ const agentTools = state.agent?.getTools() ?? [];
7981
+ const agentToolsTokens = tokenCounter.countToolSchemaTokens(agentTools);
7982
+ const totalWithTools = usage.totalTokens + agentToolsTokens;
7768
7983
  const usagePercent = totalWithTools / contextWindow * 100;
7769
7984
  const BAR_WIDTH = 40;
7770
7985
  const filledWidth = Math.min(Math.round(usagePercent / 100 * BAR_WIDTH), BAR_WIDTH);
@@ -7781,10 +7996,14 @@ Multi-line Input:
7781
7996
  console.log(` Agent Directory: ${agentDirectoryTokens.toLocaleString()} tokens (${agentCount} agents)`);
7782
7997
  }
7783
7998
  if (mcpTools.length > 0) {
7784
- console.log("\nMCP Tools:");
7785
- console.log(` Total: ${mcpToolsTokens.toLocaleString()} tokens (${mcpTools.length} tools)`);
7999
+ console.log("\nMCP Tools (deferred):");
8000
+ console.log(` Schemas: load on demand via tool_search (${mcpTools.length} tools available)`);
7786
8001
  for (const { serverName, count } of mcpToolCount) console.log(` ${serverName}: ${count} tools`);
7787
8002
  }
8003
+ if (agentTools.length > 0) {
8004
+ console.log("\nLoaded Tool Schemas:");
8005
+ console.log(` Schemas: ${agentToolsTokens.toLocaleString()} tokens (${agentTools.length} tools active)`);
8006
+ }
7788
8007
  console.log("\nConversation:");
7789
8008
  console.log(` Messages: ${usage.messageTokens.toLocaleString()} tokens (${state.session.messages.length} messages)`);
7790
8009
  if (usagePercent >= 80) {
@@ -8320,14 +8539,13 @@ Multi-line Input:
8320
8539
  const newToolNames = newFeatureRegistry.getAllToolNames();
8321
8540
  if (newToolNames.length > 0) registerFeatureModuleTools(newToolNames);
8322
8541
  if (state.wsManager && newFeatureRegistry.hasModules) newFeatureRegistry.registerAllWsHandlers(state.wsManager);
8323
- const agentContext = state.agent.context;
8324
8542
  const oldFeatureToolNames = new Set(state.featureRegistry?.getAllToolNames() ?? []);
8325
- const baseTools = agentContext.tools.filter((t) => !oldFeatureToolNames.has(t.toolSchema.name));
8543
+ const baseTools = state.agent.getTools().filter((t) => !oldFeatureToolNames.has(t.toolSchema.name));
8326
8544
  const newFeatureTools = newFeatureRegistry.getAllTools();
8327
- agentContext.tools = [...baseTools, ...newFeatureTools];
8545
+ state.agent.setTools([...baseTools, ...newFeatureTools]);
8328
8546
  const newFeaturePrompts = newFeatureRegistry.getSystemPromptSections();
8329
8547
  const planFilePathForRebuild = useCliStore.getState().interactionMode === "plan" && state.session ? getPlanModeFilePath(state.session.id) : void 0;
8330
- agentContext.systemPrompt = buildSystemPrompt(updatedConfig.preferences.promptVariant ?? "current", {
8548
+ state.agent.setSystemPrompt(buildSystemPrompt(updatedConfig.preferences.promptVariant ?? "current", {
8331
8549
  contextContent: state.contextContent,
8332
8550
  agentStore: state.agentStore || void 0,
8333
8551
  customCommands: state.customCommandStore.getAllCommands(),
@@ -8335,8 +8553,9 @@ Multi-line Input:
8335
8553
  enableDynamicAgentCreation: updatedConfig.preferences.enableDynamicAgentCreation === true,
8336
8554
  additionalDirectories: state.additionalDirectories,
8337
8555
  featureModulePrompts: newFeaturePrompts || void 0,
8338
- planModeFilePath: planFilePathForRebuild
8339
- });
8556
+ planModeFilePath: planFilePathForRebuild,
8557
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
8558
+ }));
8340
8559
  const moduleNames = newFeatureRegistry.getModuleNames();
8341
8560
  if (moduleNames.length > 0) console.error(`\n\x1b[36m🏰 Feature modules hot-reloaded: ${moduleNames.join(", ")}\x1b[0m`);
8342
8561
  else console.error(`\n\x1b[36m🏰 Feature modules disabled\x1b[0m`);