@bike4mind/cli 0.9.0 → 0.9.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.
Files changed (27) hide show
  1. package/dist/{BubblewrapRuntime-BHbtqvLx.mjs → BubblewrapRuntime-CUD3bsgG.mjs} +1 -1
  2. package/dist/ProxyManager-DIAAw902.mjs +3 -0
  3. package/dist/{SandboxOrchestrator-CHZgSR3P.mjs → SandboxOrchestrator-C4oDqltp.mjs} +1 -1
  4. package/dist/{SandboxRuntimeAdapter-C1B4t20N.mjs → SandboxRuntimeAdapter-D1RUReNL.mjs} +2 -2
  5. package/dist/{SandboxRuntimeAdapter-D7UAG13n.mjs → SandboxRuntimeAdapter-DXa3nFOw.mjs} +1 -1
  6. package/dist/{SeatbeltRuntime-D4m0VOcD.mjs → SeatbeltRuntime-CTElMR9Q.mjs} +1 -1
  7. package/dist/commands/doctorCommand.mjs +13 -1
  8. package/dist/commands/headlessCommand.mjs +4 -4
  9. package/dist/commands/updateCommand.mjs +33 -15
  10. package/dist/{grepSearch-DMuEcUSq-D3RsGLCg.mjs → grepSearch-BxucZWO8-lPRv6R6F.mjs} +15 -26
  11. package/dist/index.mjs +87 -80
  12. package/dist/ripgrepCheck-DIu4apVE.mjs +39 -0
  13. package/dist/{tools-B0Y_zziv.mjs → tools-AWSBVqG5.mjs} +251 -20
  14. package/dist/{updateChecker-CtczXQeW.mjs → updateChecker-Bml_XTCM.mjs} +1 -1
  15. package/package.json +6 -6
  16. package/dist/ProxyManager-kiOD1X8-.mjs +0 -3
  17. /package/dist/{ImageStore-DaKT_Ew8.mjs → ImageStore-BFp_d12J.mjs} +0 -0
  18. /package/dist/{ProxyManager-Dl2nFk-A.mjs → ProxyManager-BsCoxpns.mjs} +0 -0
  19. /package/dist/{SandboxOrchestrator-BEW3rqYi.mjs → SandboxOrchestrator-B4GcZdBc.mjs} +0 -0
  20. /package/dist/{StderrViolationParser-D0afQ3-1.mjs → StderrViolationParser-BFP4bo7I.mjs} +0 -0
  21. /package/dist/{ViolationLogStore-CZl35HcA.mjs → ViolationLogStore-Dp6HF0nz.mjs} +0 -0
  22. /package/dist/{bashExecute-pYljpfPn-Bsh-jb3S.mjs → bashExecute-pYljpfPn-BZXHMQEl.mjs} +0 -0
  23. /package/dist/{createFile-C1JoeuYh-LvIRJtxM.mjs → createFile-C1JoeuYh-metInFKd.mjs} +0 -0
  24. /package/dist/{deleteFile-BTberNGj-BpBvrcoC.mjs → deleteFile-BTberNGj-CW922hRM.mjs} +0 -0
  25. /package/dist/{globFiles-Bez8QCbS-DyZsKEJB.mjs → globFiles-Bez8QCbS-DZb6McbJ.mjs} +0 -0
  26. /package/dist/{terminalSetup-rmr1P8KF.mjs → terminalSetup-DxloCowq.mjs} +0 -0
  27. /package/dist/{treeSitterEngine-BFTHnDwH.mjs → treeSitterEngine-Cw2LbVZT.mjs} +0 -0
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as isBinaryAvailable, r as expandPath } from "./SandboxRuntimeAdapter-C1B4t20N.mjs";
2
+ import { i as isBinaryAvailable, r as expandPath } from "./SandboxRuntimeAdapter-D1RUReNL.mjs";
3
3
  import os from "os";
4
4
  //#region src/sandbox/runtime/BubblewrapRuntime.ts
5
5
  /**
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { t as ProxyManager } from "./ProxyManager-BsCoxpns.mjs";
3
+ export { ProxyManager };
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { t as SandboxOrchestrator } from "./SandboxOrchestrator-BEW3rqYi.mjs";
2
+ import { t as SandboxOrchestrator } from "./SandboxOrchestrator-B4GcZdBc.mjs";
3
3
  export { SandboxOrchestrator };
@@ -42,12 +42,12 @@ async function createSandboxRuntime() {
42
42
  const platform = detectPlatform();
43
43
  if (!platform) return null;
44
44
  if (platform === "darwin") {
45
- const { SeatbeltRuntime } = await import("./SeatbeltRuntime-D4m0VOcD.mjs");
45
+ const { SeatbeltRuntime } = await import("./SeatbeltRuntime-CTElMR9Q.mjs");
46
46
  const runtime = new SeatbeltRuntime();
47
47
  return runtime.isAvailable() ? runtime : null;
48
48
  }
49
49
  if (platform === "linux") {
50
- const { BubblewrapRuntime } = await import("./BubblewrapRuntime-BHbtqvLx.mjs");
50
+ const { BubblewrapRuntime } = await import("./BubblewrapRuntime-CUD3bsgG.mjs");
51
51
  const runtime = new BubblewrapRuntime();
52
52
  return runtime.isAvailable() ? runtime : null;
53
53
  }
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { i as isBinaryAvailable, n as detectPlatform, r as expandPath, t as createSandboxRuntime } from "./SandboxRuntimeAdapter-C1B4t20N.mjs";
2
+ import { i as isBinaryAvailable, n as detectPlatform, r as expandPath, t as createSandboxRuntime } from "./SandboxRuntimeAdapter-D1RUReNL.mjs";
3
3
  export { createSandboxRuntime, detectPlatform, expandPath, isBinaryAvailable };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as isBinaryAvailable, r as expandPath } from "./SandboxRuntimeAdapter-C1B4t20N.mjs";
2
+ import { i as isBinaryAvailable, r as expandPath } from "./SandboxRuntimeAdapter-D1RUReNL.mjs";
3
3
  import { mkdtempSync, writeFileSync } from "fs";
4
4
  import os from "os";
5
5
  import path from "path";
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-CtczXQeW.mjs";
2
+ import { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-Bml_XTCM.mjs";
3
+ import { t as checkRipgrep } from "../ripgrepCheck-DIu4apVE.mjs";
3
4
  import { execSync } from "child_process";
4
5
  import { constants, existsSync, promises } from "fs";
5
6
  import { homedir } from "os";
@@ -74,6 +75,17 @@ async function handleDoctorCommand() {
74
75
  message: "Could not determine npm prefix"
75
76
  });
76
77
  }
78
+ const rg = await checkRipgrep();
79
+ if (rg.available) results.push({
80
+ name: "ripgrep (grep_search)",
81
+ status: "pass",
82
+ message: rg.path
83
+ });
84
+ else results.push({
85
+ name: "ripgrep (grep_search)",
86
+ status: "warn",
87
+ message: `${rg.error ?? "not found"} — run: b4m update`
88
+ });
77
89
  const configFile = path.join(homedir(), ".bike4mind", "config.json");
78
90
  if (existsSync(configFile)) results.push({
79
91
  name: "Config file",
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, I as generateCliTools, M as loadContextFiles, N as getApiUrl, O as McpManager, Q as CheckpointStore, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, m as createCoordinateTaskTool, p as createWriteTodosTool, q as buildSystemPrompt, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-B0Y_zziv.mjs";
2
+ import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, I as generateCliTools, M as loadContextFiles, N as getApiUrl, O as McpManager, Q as CheckpointStore, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, m as createCoordinateTaskTool, p as createWriteTodosTool, q as buildSystemPrompt, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-AWSBVqG5.mjs";
3
3
  import { n as logger, t as ConfigStore } from "../ConfigStore-CAKSUXCi.mjs";
4
4
  import { t as DEFAULT_SANDBOX_CONFIG } from "../types-DBEjF9YS.mjs";
5
- import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-C1B4t20N.mjs";
6
- import { t as SandboxOrchestrator } from "../SandboxOrchestrator-BEW3rqYi.mjs";
7
- import { t as ProxyManager } from "../ProxyManager-Dl2nFk-A.mjs";
5
+ import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-D1RUReNL.mjs";
6
+ import { t as SandboxOrchestrator } from "../SandboxOrchestrator-B4GcZdBc.mjs";
7
+ import { t as ProxyManager } from "../ProxyManager-BsCoxpns.mjs";
8
8
  import { randomBytes } from "crypto";
9
9
  import { v4 } from "uuid";
10
10
  //#region src/commands/headlessCommand.ts
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as version, i as forceCheckForUpdate } from "../updateChecker-CtczXQeW.mjs";
2
+ import { a as version, i as forceCheckForUpdate } from "../updateChecker-Bml_XTCM.mjs";
3
+ import { t as checkRipgrep } from "../ripgrepCheck-DIu4apVE.mjs";
3
4
  import { execSync } from "child_process";
4
5
  //#region src/commands/updateCommand.ts
5
6
  /**
@@ -7,6 +8,22 @@ import { execSync } from "child_process";
7
8
  * Checks for and installs CLI updates.
8
9
  * Runs outside the interactive CLI session.
9
10
  */
11
+ const INSTALL_CMD = "npm install -g @bike4mind/cli@latest";
12
+ function runGlobalInstall() {
13
+ try {
14
+ execSync(INSTALL_CMD, {
15
+ stdio: "inherit",
16
+ timeout: 12e4
17
+ });
18
+ return true;
19
+ } catch {
20
+ console.error("\nInstall failed. Try running manually:");
21
+ console.error(` ${INSTALL_CMD}`);
22
+ console.error("\nIf you get permission errors, try:");
23
+ console.error(` sudo ${INSTALL_CMD}`);
24
+ return false;
25
+ }
26
+ }
10
27
  async function handleUpdateCommand() {
11
28
  const currentVersion = version;
12
29
  console.log(`Current version: v${currentVersion}`);
@@ -16,25 +33,26 @@ async function handleUpdateCommand() {
16
33
  console.error("Failed to check for updates. Check your internet connection.");
17
34
  process.exit(1);
18
35
  }
19
- if (!result.updateAvailable) {
36
+ const rgBefore = await checkRipgrep();
37
+ const needsRepair = !rgBefore.available;
38
+ if (!result.updateAvailable && !needsRepair) {
20
39
  console.log(`Already on the latest version (v${currentVersion}).`);
21
40
  return;
22
41
  }
23
- console.log(`Update available: v${currentVersion} → v${result.latestVersion}\n`);
24
- console.log("Installing update...\n");
25
- try {
26
- execSync("npm install -g @bike4mind/cli@latest", {
27
- stdio: "inherit",
28
- timeout: 12e4
29
- });
30
- console.log(`\nSuccessfully updated to v${result.latestVersion}.`);
31
- } catch {
32
- console.error("\nUpdate failed. Try running manually:");
33
- console.error(" npm install -g @bike4mind/cli@latest");
34
- console.error("\nIf you get permission errors, try:");
35
- console.error(" sudo npm install -g @bike4mind/cli@latest");
42
+ if (result.updateAvailable) console.log(`Update available: v${currentVersion} → v${result.latestVersion}`);
43
+ if (needsRepair) console.log(`Repairing missing ripgrep binary (${rgBefore.error ?? "unknown reason"})`);
44
+ console.log("\nInstalling...\n");
45
+ if (!runGlobalInstall()) process.exit(1);
46
+ const rgAfter = await checkRipgrep();
47
+ if (!rgAfter.available) {
48
+ console.error("\nWarning: ripgrep is still unavailable after install.");
49
+ console.error(` ${rgAfter.error ?? "unknown reason"}`);
50
+ console.error("The grep_search tool will not work. Try a forced reinstall:");
51
+ console.error(` ${INSTALL_CMD} --force`);
36
52
  process.exit(1);
37
53
  }
54
+ if (result.updateAvailable) console.log(`\nSuccessfully updated to v${result.latestVersion}.`);
55
+ else console.log("\nRipgrep restored.");
38
56
  }
39
57
  //#endregion
40
58
  export { handleUpdateCommand };
@@ -1,42 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as isPathAllowed } from "./pathValidation-CIytuhr3-Dt5dntLx.mjs";
3
- import { execFile, execFileSync } from "child_process";
3
+ import { execFile } from "child_process";
4
4
  import { existsSync } from "fs";
5
5
  import path from "path";
6
6
  import { stat } from "fs/promises";
7
7
  import { promisify } from "util";
8
- import { createRequire } from "module";
9
- //#region ../../b4m-core/services/dist/grepSearch-DMuEcUSq.mjs
8
+ //#region ../../b4m-core/services/dist/grepSearch-BxucZWO8.mjs
10
9
  const execFileAsync = promisify(execFile);
11
- const require = createRequire(import.meta.url);
12
10
  /** Cached ripgrep binary path after first resolution */
13
11
  let cachedRgPath = null;
14
12
  /**
15
- * Get ripgrep binary path
16
- * This is an optional dependency (CLI-only), so we load it lazily.
17
- * If the binary is missing (postinstall was skipped), auto-downloads it.
13
+ * Resolve ripgrep binary path via the package's exported `rgPath`.
14
+ * `@vscode/ripgrep` is an optional dependency, so we load it lazily; in 1.18+
15
+ * the binary lives in a platform-specific sibling package and is resolved by
16
+ * the package itself — we just have to ask for it.
18
17
  */
19
- function getRipgrepPath() {
18
+ async function getRipgrepPath() {
20
19
  if (cachedRgPath) return cachedRgPath;
20
+ let rgPath;
21
21
  try {
22
- const ripgrepPath = require.resolve("@vscode/ripgrep");
23
- const ripgrepDir = path.dirname(ripgrepPath);
24
- const rgBinary = path.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
25
- if (!existsSync(rgBinary)) {
26
- const postinstallScript = path.join(ripgrepDir, "..", "lib", "postinstall.js");
27
- if (existsSync(postinstallScript)) execFileSync(process.execPath, [postinstallScript], {
28
- cwd: path.join(ripgrepDir, ".."),
29
- stdio: "pipe",
30
- timeout: 3e4
31
- });
32
- if (!existsSync(rgBinary)) throw new Error(`ripgrep binary not found at ${rgBinary}. The postinstall download may have failed. Try running: cd ${path.join(ripgrepDir, "..")} && node lib/postinstall.js`);
33
- }
34
- cachedRgPath = rgBinary;
35
- return rgBinary;
36
- } catch (error) {
37
- if (error instanceof Error && error.message.includes("ripgrep binary not found")) throw error;
38
- throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
22
+ ({rgPath} = await import("@vscode/ripgrep"));
23
+ } catch {
24
+ throw new Error("ripgrep is not available. Install the optional dependency: pnpm add @vscode/ripgrep --filter @bike4mind/services");
39
25
  }
26
+ if (!rgPath || !existsSync(rgPath)) throw new Error(`ripgrep binary not found at ${rgPath ?? "<unresolved>"}. Ensure @vscode/ripgrep platform optional dependencies are installed for ${process.platform}-${process.arch}.`);
27
+ cachedRgPath = rgPath;
28
+ return rgPath;
40
29
  }
41
30
  /**
42
31
  * Converts a glob pattern to ripgrep glob patterns
@@ -62,7 +51,7 @@ async function searchFiles(params, allowedDirectories) {
62
51
  if (error.code === "ENOENT") throw new Error(`Path does not exist: ${dir_path}`);
63
52
  throw error;
64
53
  }
65
- const rgPath = getRipgrepPath();
54
+ const rgPath = await getRipgrepPath();
66
55
  const rgArgs = [
67
56
  "--json",
68
57
  "--max-count",
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-DLduYYGR.mjs";
3
- 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-B0Y_zziv.mjs";
3
+ 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-AWSBVqG5.mjs";
4
4
  import { Nt as validateJupyterKernelName, Pt as validateNotebookPath$1, g as ChatModels, m as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore } from "./ConfigStore-CAKSUXCi.mjs";
5
- import { a as version, t as checkForUpdate } from "./updateChecker-CtczXQeW.mjs";
5
+ import { a as version, t as checkForUpdate } from "./updateChecker-Bml_XTCM.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";
@@ -1022,7 +1022,7 @@ function renderDiffPreview(preview) {
1022
1022
  }, line);
1023
1023
  });
1024
1024
  }
1025
- const TOOLS_WITH_HIDDEN_ARGS$1 = new Set(["edit_local_file"]);
1025
+ const TOOLS_WITH_HIDDEN_ARGS$1 = new Set(["edit_local_file", "bash_execute"]);
1026
1026
  /**
1027
1027
  * Permission prompt component
1028
1028
  *
@@ -2075,11 +2075,10 @@ const MessageItem = React.memo(function MessageItem({ message, showThoughts = tr
2075
2075
  const { stdout } = useStdout();
2076
2076
  const terminalCols = stdout?.columns ?? 80;
2077
2077
  const userPromptText = `❯ ${message.content}`;
2078
- const paddedUserPromptText = userPromptText.length >= terminalCols - 2 ? userPromptText : userPromptText.padEnd(terminalCols - 2);
2078
+ const paddedUserPromptText = userPromptText.length >= terminalCols ? userPromptText : userPromptText.padEnd(terminalCols);
2079
2079
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, isUser && message.content && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, {
2080
2080
  backgroundColor: "whiteBright",
2081
- color: "black",
2082
- wrap: "truncate-end"
2081
+ color: "black"
2083
2082
  }, paddedUserPromptText)), !isUser && message.metadata?.steps && message.metadata.steps.filter((s) => showThoughts && s.type === "thought" || s.type === "action").length > 0 && /* @__PURE__ */ React.createElement(Box, {
2084
2083
  paddingLeft: 2,
2085
2084
  flexDirection: "column",
@@ -2209,15 +2208,13 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
2209
2208
  onClose: () => setShowMcpViewer(false)
2210
2209
  })) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Static, { items: messages }, (message) => /* @__PURE__ */ React.createElement(Box, {
2211
2210
  key: message.id,
2212
- flexDirection: "column",
2213
- paddingX: 1
2211
+ flexDirection: "column"
2214
2212
  }, /* @__PURE__ */ React.createElement(MessageItem, {
2215
2213
  message,
2216
2214
  showThoughts
2217
2215
  }))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, pendingMessages.map((message) => /* @__PURE__ */ React.createElement(Box, {
2218
2216
  key: message.id,
2219
- flexDirection: "column",
2220
- paddingX: 1
2217
+ flexDirection: "column"
2221
2218
  }, /* @__PURE__ */ React.createElement(MessageItem, {
2222
2219
  message,
2223
2220
  showThoughts
@@ -2281,7 +2278,9 @@ function App({ onMessage, onBackgroundCompletion, onCommand, onBashCommand, onPe
2281
2278
  borderStyle: "single",
2282
2279
  borderColor: isBashMode ? "yellow" : "cyan",
2283
2280
  borderTop: true,
2284
- borderBottom: true
2281
+ borderBottom: true,
2282
+ borderLeft: false,
2283
+ borderRight: false
2285
2284
  }, /* @__PURE__ */ React.createElement(InputPrompt, {
2286
2285
  onSubmit: handleSubmit,
2287
2286
  onBashCommand,
@@ -5442,6 +5441,67 @@ function summarizeUserQuestion(payload) {
5442
5441
  const first = payload.questions?.[0]?.question;
5443
5442
  return first ? first.slice(0, 240) : void 0;
5444
5443
  }
5444
+ /**
5445
+ * Render the B4M ASCII banner with an optional startup log column to the right.
5446
+ * Used on startup and after /clear so the user always sees the banner at the
5447
+ * top of a fresh session.
5448
+ */
5449
+ function renderBanner(startupLog = []) {
5450
+ const bannerLines = [
5451
+ {
5452
+ text: "██████╗ ██╗ ██╗███╗ ███╗",
5453
+ ansi: "\x1B[36m\x1B[1m"
5454
+ },
5455
+ {
5456
+ text: "██╔══██╗██║ ██║████╗ ████║",
5457
+ ansi: "\x1B[36m\x1B[1m"
5458
+ },
5459
+ {
5460
+ text: "██████╔╝███████║██╔████╔██║",
5461
+ ansi: "\x1B[36m\x1B[1m"
5462
+ },
5463
+ {
5464
+ text: "██╔══██╗╚════██║██║╚██╔╝██║",
5465
+ ansi: "\x1B[36m\x1B[1m"
5466
+ },
5467
+ {
5468
+ text: "██████╔╝ ██║██║ ╚═╝ ██║",
5469
+ ansi: "\x1B[36m\x1B[1m"
5470
+ },
5471
+ {
5472
+ text: "╚═════╝ ╚═╝╚═╝ ╚═╝",
5473
+ ansi: "\x1B[36m\x1B[1m"
5474
+ },
5475
+ {
5476
+ text: "",
5477
+ ansi: ""
5478
+ },
5479
+ {
5480
+ text: `v${version} - AI-Powered CLI`,
5481
+ ansi: "\x1B[2m"
5482
+ },
5483
+ {
5484
+ text: "/help for more information",
5485
+ ansi: "\x1B[2m"
5486
+ }
5487
+ ];
5488
+ const bannerWidth = 30;
5489
+ const rightColWidth = (process.stdout.columns || 80) - bannerWidth - 2;
5490
+ const truncate = (str, max) => {
5491
+ if (str.length > max) return str.slice(0, max - 1) + "…";
5492
+ return str;
5493
+ };
5494
+ const totalLines = Math.max(bannerLines.length, startupLog.length);
5495
+ for (let i = 0; i < totalLines; i++) {
5496
+ const banner = bannerLines[i];
5497
+ const leftText = banner?.text || "";
5498
+ const leftAnsi = banner?.ansi || "";
5499
+ const right = startupLog[i] || "";
5500
+ const coloredLeft = leftText ? `${leftAnsi}${leftText}\x1b[0m` : "";
5501
+ const gap = " ".repeat(bannerWidth - leftText.length + 2);
5502
+ console.log(coloredLeft + gap + truncate(right, rightColWidth));
5503
+ }
5504
+ }
5445
5505
  let exitTimestamp = null;
5446
5506
  let exitInProgress = false;
5447
5507
  const EXIT_TIMEOUT_MS = 2e3;
@@ -5582,8 +5642,6 @@ function CliApp() {
5582
5642
  setCommandHistory(await state.commandHistoryStore.load());
5583
5643
  try {
5584
5644
  await state.customCommandStore.loadCommands();
5585
- const customCommandCount = state.customCommandStore.getCommandCount();
5586
- if (customCommandCount > 0) startupLog.push(`📝 Loaded ${customCommandCount} custom command${customCommandCount !== 1 ? "s" : ""}`);
5587
5645
  } catch (error) {
5588
5646
  console.warn("Failed to load custom commands:", error instanceof Error ? error.message : String(error));
5589
5647
  }
@@ -5612,7 +5670,7 @@ function CliApp() {
5612
5670
  startupLog.push(`✅ Authenticated (expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? "s" : ""})`);
5613
5671
  const apiBaseURL = getApiUrl(config.apiConfig);
5614
5672
  const envName = getEnvironmentName(config.apiConfig);
5615
- if (import.meta.url.includes("/src/") || process.env.NODE_ENV === "development" || envName !== "Bike4Mind") console.log(`🌍 API Environment: ${envName} (${apiBaseURL})`);
5673
+ if (import.meta.url.includes("/src/") || process.env.NODE_ENV === "development" || envName !== "Bike4Mind") startupLog.unshift(`🌍 API Environment: ${envName} (${apiBaseURL})`);
5616
5674
  const apiClient = new ApiClient(apiBaseURL, state.configStore);
5617
5675
  const tokenGetter = async () => {
5618
5676
  return (await state.configStore.getAuthTokens())?.accessToken ?? null;
@@ -5796,11 +5854,11 @@ function CliApp() {
5796
5854
  };
5797
5855
  const permissionManager = new PermissionManager(config.trustedTools || [], void 0, config.tools.disabled || []);
5798
5856
  const [{ createSandboxRuntime }, { SandboxOrchestrator }, { DEFAULT_SANDBOX_CONFIG }, { ProxyManager }, { ViolationLogStore }] = await Promise.all([
5799
- import("./SandboxRuntimeAdapter-D7UAG13n.mjs"),
5800
- import("./SandboxOrchestrator-CHZgSR3P.mjs"),
5857
+ import("./SandboxRuntimeAdapter-DXa3nFOw.mjs"),
5858
+ import("./SandboxOrchestrator-C4oDqltp.mjs"),
5801
5859
  import("./types-DK3P88Px.mjs"),
5802
- import("./ProxyManager-kiOD1X8-.mjs"),
5803
- import("./ViolationLogStore-CZl35HcA.mjs")
5860
+ import("./ProxyManager-DIAAw902.mjs"),
5861
+ import("./ViolationLogStore-Dp6HF0nz.mjs")
5804
5862
  ]);
5805
5863
  const sandboxConfig = config.sandbox ?? DEFAULT_SANDBOX_CONFIG;
5806
5864
  const checkpointStore = new CheckpointStore(state.configStore.getProjectConfigDir() || process.cwd());
@@ -5906,7 +5964,6 @@ function CliApp() {
5906
5964
  observationQueue: []
5907
5965
  };
5908
5966
  const { tools: b4mTools } = await generateCliTools(config.userId, llm, modelInfo.id, permissionManager, promptFn, agentContext, state.configStore, apiClient, void 0, userQuestionFn, checkpointStore, sandboxOrchestrator, additionalDirectories);
5909
- startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M tool(s)`);
5910
5967
  const mcpManager = new McpManager(config);
5911
5968
  const builtinAgentsDir = new URL("./agents/defaults/", import.meta.url).pathname;
5912
5969
  const agentProjectDir = state.configStore.getProjectConfigDir();
@@ -5919,8 +5976,8 @@ function CliApp() {
5919
5976
  const mcpTools = mcpManager.getTools();
5920
5977
  if (mcpTools.length > 0) {
5921
5978
  const serverSummaries = mcpManager.getToolCount().map((s) => `${s.serverName} (${s.count})`).join(", ");
5922
- startupLog.push(`📡 Loaded ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
5923
- } else startupLog.push(`📡 No MCP tools loaded`);
5979
+ startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M + ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
5980
+ } else startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M tool(s), no MCP tools`);
5924
5981
  const agentSummary = agentStore.getSummary();
5925
5982
  startupLog.push(`🤖 Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`);
5926
5983
  const orchestrator = new SubagentOrchestrator({
@@ -6002,11 +6059,9 @@ function CliApp() {
6002
6059
  ];
6003
6060
  startupLog.push(`📂 Working directory: ${process.cwd()}`);
6004
6061
  if (additionalDirectories.length > 0) startupLog.push(`📁 Additional directories: ${additionalDirectories.length}`);
6005
- const agentNamesList = agentStore.getAgentNames().join(", ");
6006
- startupLog.push(`🤖 Subagent delegation enabled (${agentNamesList}) + background execution`);
6007
6062
  if (skillTool) {
6008
6063
  const skillCount = state.customCommandStore.getCommandCount();
6009
- if (skillCount > 0) startupLog.push(`🛠️ Skill tool enabled (${skillCount} skills available)`);
6064
+ if (skillCount > 0) startupLog.push(`🛠️ Skill tool enabled (${skillCount} skills available)`);
6010
6065
  }
6011
6066
  if (dynamicAgentTool) startupLog.push(`🧪 Dynamic agent creation enabled (experimental)`);
6012
6067
  if (featureRegistry.hasModules) {
@@ -6175,60 +6230,7 @@ function CliApp() {
6175
6230
  featureRegistry
6176
6231
  }));
6177
6232
  setStoreSession(newSession);
6178
- const bannerLines = [
6179
- {
6180
- text: "██████╗ ██╗ ██╗███╗ ███╗",
6181
- ansi: "\x1B[36m\x1B[1m"
6182
- },
6183
- {
6184
- text: "██╔══██╗██║ ██║████╗ ████║",
6185
- ansi: "\x1B[36m\x1B[1m"
6186
- },
6187
- {
6188
- text: "██████╔╝███████║██╔████╔██║",
6189
- ansi: "\x1B[36m\x1B[1m"
6190
- },
6191
- {
6192
- text: "██╔══██╗╚════██║██║╚██╔╝██║",
6193
- ansi: "\x1B[36m\x1B[1m"
6194
- },
6195
- {
6196
- text: "██████╔╝ ██║██║ ╚═╝ ██║",
6197
- ansi: "\x1B[36m\x1B[1m"
6198
- },
6199
- {
6200
- text: "╚═════╝ ╚═╝╚═╝ ╚═╝",
6201
- ansi: "\x1B[36m\x1B[1m"
6202
- },
6203
- {
6204
- text: "",
6205
- ansi: ""
6206
- },
6207
- {
6208
- text: `v${version} - AI-Powered CLI`,
6209
- ansi: "\x1B[2m"
6210
- },
6211
- {
6212
- text: "/help for more information",
6213
- ansi: "\x1B[2m"
6214
- }
6215
- ];
6216
- const bannerWidth = 30;
6217
- const rightColWidth = (process.stdout.columns || 80) - bannerWidth - 2;
6218
- const truncate = (str, max) => {
6219
- if (str.length > max) return str.slice(0, max - 1) + "…";
6220
- return str;
6221
- };
6222
- const totalLines = Math.max(bannerLines.length, startupLog.length);
6223
- for (let i = 0; i < totalLines; i++) {
6224
- const banner = bannerLines[i];
6225
- const leftText = banner?.text || "";
6226
- const leftAnsi = banner?.ansi || "";
6227
- const right = startupLog[i] || "";
6228
- const coloredLeft = leftText ? `${leftAnsi}${leftText}\x1b[0m` : "";
6229
- const gap = " ".repeat(bannerWidth - leftText.length + 2);
6230
- console.log(coloredLeft + gap + truncate(right, rightColWidth));
6231
- }
6233
+ renderBanner(startupLog);
6232
6234
  setIsInitialized(true);
6233
6235
  console.log("");
6234
6236
  } catch (error) {
@@ -6873,7 +6875,7 @@ function CliApp() {
6873
6875
  let imageStore = state.imageStore;
6874
6876
  if (!imageStore) {
6875
6877
  if (!imageStoreInitPromise.current) imageStoreInitPromise.current = (async () => {
6876
- const { ImageStore: ImageStoreClass } = await import("./ImageStore-DaKT_Ew8.mjs");
6878
+ const { ImageStore: ImageStoreClass } = await import("./ImageStore-BFp_d12J.mjs");
6877
6879
  const newImageStore = new ImageStoreClass();
6878
6880
  setState((prev) => ({
6879
6881
  ...prev,
@@ -7480,6 +7482,7 @@ Multi-line Input:
7480
7482
  case "clear":
7481
7483
  case "new": {
7482
7484
  console.clear();
7485
+ renderBanner();
7483
7486
  const model = state.session?.model || state.config?.defaultModel || ChatModels.CLAUDE_4_5_SONNET;
7484
7487
  const newSession = {
7485
7488
  id: v4(),
@@ -8151,7 +8154,7 @@ Multi-line Input:
8151
8154
  break;
8152
8155
  }
8153
8156
  case "terminal-setup": {
8154
- const { runTerminalSetup } = await import("./terminalSetup-rmr1P8KF.mjs");
8157
+ const { runTerminalSetup } = await import("./terminalSetup-DxloCowq.mjs");
8155
8158
  await runTerminalSetup();
8156
8159
  break;
8157
8160
  }
@@ -8465,6 +8468,10 @@ Multi-line Input:
8465
8468
  allow: response !== "deny",
8466
8469
  resolvedBy: "user"
8467
8470
  });
8471
+ if (response === "deny") {
8472
+ abortControllerRef.current?.abort();
8473
+ return;
8474
+ }
8468
8475
  emitNextAwaitingStatus();
8469
8476
  },
8470
8477
  onUserQuestionResponse: (response, promptId) => {
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { access, constants } from "fs/promises";
3
+ //#region src/utils/ripgrepCheck.ts
4
+ /**
5
+ * Probe `@vscode/ripgrep` the same way `grep_search` does: dynamic-import the
6
+ * package, take its exported `rgPath`, and verify it's executable on disk.
7
+ * The package is an optional dependency, so any failure mode (missing package,
8
+ * missing platform sibling, missing binary) is reported as `available: false`.
9
+ */
10
+ async function checkRipgrep() {
11
+ let rgPath;
12
+ try {
13
+ ({rgPath} = await import("@vscode/ripgrep"));
14
+ } catch (err) {
15
+ return {
16
+ available: false,
17
+ error: err instanceof Error ? err.message : String(err)
18
+ };
19
+ }
20
+ if (!rgPath) return {
21
+ available: false,
22
+ error: "@vscode/ripgrep did not export rgPath"
23
+ };
24
+ try {
25
+ await access(rgPath, constants.X_OK);
26
+ return {
27
+ available: true,
28
+ path: rgPath
29
+ };
30
+ } catch {
31
+ return {
32
+ available: false,
33
+ path: rgPath,
34
+ error: `binary not executable at ${rgPath}`
35
+ };
36
+ }
37
+ }
38
+ //#endregion
39
+ export { checkRipgrep as t };
@@ -83,7 +83,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
83
83
  import { fileURLToPath } from "url";
84
84
  import WsWebSocket from "ws";
85
85
  import { createParser } from "eventsource-parser";
86
- import { createRequire } from "module";
87
86
  //#region src/utils/fileSearch.ts
88
87
  /**
89
88
  * Load gitignore rules from project root
@@ -2286,6 +2285,31 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
2286
2285
  return this.iterations;
2287
2286
  }
2288
2287
  /**
2288
+ * Surgically replace the observation content of the most recent tool_result
2289
+ * message matching `toolCallId`. Used to inject a Lambda-dispatched
2290
+ * subagent's terminal answer into the parent's message history without
2291
+ * appending a new message (Anthropic rejects consecutive user-role messages,
2292
+ * so we replace the placeholder observation in place).
2293
+ *
2294
+ * Throws if the underlying backend doesn't support this surgery or if no
2295
+ * matching message is found.
2296
+ */
2297
+ replaceLastToolResultObservation(toolCallId, newObservation) {
2298
+ if (!this.context.llm.replaceLastToolResultObservation) throw new Error(`ReActAgent.replaceLastToolResultObservation: backend (${this.context.llm.currentModel}) does not implement replaceLastToolResultObservation — cannot resume after subagent handoff`);
2299
+ this.context.llm.replaceLastToolResultObservation(this.messages, toolCallId, newObservation);
2300
+ }
2301
+ /**
2302
+ * Locate the LLM-assigned id of the most recent tool call with the given name.
2303
+ * Delegates to the backend so each provider's message shape (Anthropic
2304
+ * content-block `tool_use` vs OpenAI `assistant.tool_calls`) is handled
2305
+ * correctly. Returns `undefined` if the backend doesn't implement the lookup
2306
+ * or no matching call exists.
2307
+ */
2308
+ getLatestToolCallId(toolName) {
2309
+ if (!this.context.llm.getLatestToolCallId) return void 0;
2310
+ return this.context.llm.getLatestToolCallId(this.messages, toolName);
2311
+ }
2312
+ /**
2289
2313
  * Serialize the agent's current execution state for persistence.
2290
2314
  *
2291
2315
  * Call this after each iteration to create a durable checkpoint that can
@@ -2884,6 +2908,153 @@ function trimConversationHistory(messages, maxIterations, protectedPrefixCount)
2884
2908
  const keepFrom = dynamicStart + nudgeIndices[nudgeIndices.length - maxIterations];
2885
2909
  messages.splice(dynamicStart, keepFrom - dynamicStart);
2886
2910
  }
2911
+ String.raw`
2912
+ const { parentPort } = require('node:worker_threads');
2913
+ const vm = require('node:vm');
2914
+
2915
+ const STDOUT_HEAD_BYTES = ${5e3};
2916
+ const STDOUT_TAIL_BYTES = ${2e3};
2917
+ const HARD_PER_LINE_BYTES = ${5e4};
2918
+
2919
+ let stdoutChunks = [];
2920
+ let stdoutBytes = 0;
2921
+ let truncated = false;
2922
+ function captureLine(args) {
2923
+ const line = args.map(a => {
2924
+ if (typeof a === 'string') return a;
2925
+ if (a === undefined) return 'undefined';
2926
+ if (a === null) return 'null';
2927
+ try { return JSON.stringify(a, jsonReplacer, 2); } catch { return String(a); }
2928
+ }).join(' ');
2929
+ const capped = line.length > HARD_PER_LINE_BYTES
2930
+ ? line.slice(0, HARD_PER_LINE_BYTES) + ' [...line truncated]'
2931
+ : line;
2932
+ stdoutChunks.push(capped);
2933
+ stdoutBytes += capped.length + 1;
2934
+ }
2935
+ function jsonReplacer(_k, v) {
2936
+ if (v instanceof Error) return { name: v.name, message: v.message };
2937
+ if (typeof v === 'bigint') return v.toString() + 'n';
2938
+ return v;
2939
+ }
2940
+ function collectStdout() {
2941
+ const joined = stdoutChunks.join('\n');
2942
+ if (joined.length <= STDOUT_HEAD_BYTES + STDOUT_TAIL_BYTES) return joined;
2943
+ truncated = true;
2944
+ const head = joined.slice(0, STDOUT_HEAD_BYTES);
2945
+ const tail = joined.slice(joined.length - STDOUT_TAIL_BYTES);
2946
+ const elidedBytes = joined.length - STDOUT_HEAD_BYTES - STDOUT_TAIL_BYTES;
2947
+ return head + '\n[...' + elidedBytes + ' bytes truncated...]\n' + tail;
2948
+ }
2949
+
2950
+ const sandbox = {
2951
+ console: {
2952
+ log: (...a) => captureLine(a),
2953
+ warn: (...a) => captureLine(a),
2954
+ error: (...a) => captureLine(a),
2955
+ info: (...a) => captureLine(a),
2956
+ },
2957
+ Math, JSON, Date, RegExp, Error, TypeError, RangeError, Promise,
2958
+ Array, Object, String, Number, Boolean, Map, Set, WeakMap, WeakSet, Symbol,
2959
+ // Number coercion / validation builtins — kept in sync with ReplContext
2960
+ // sandbox so worker and in-process backends behave identically.
2961
+ parseInt, parseFloat, isNaN, isFinite,
2962
+ structuredClone: globalThis.structuredClone,
2963
+ };
2964
+ // strings: false disables eval / new Function inside the worker context.
2965
+ // Same posture as the in-process ReplContext (Ken's P2 #2).
2966
+ const ctx = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
2967
+
2968
+ // Tool stubs: each in-REPL call posts a toolCall and awaits matching toolResult.
2969
+ let nextToolCallId = 0;
2970
+ const pendingToolCalls = new Map();
2971
+ function makeToolStub(name) {
2972
+ return (...args) => {
2973
+ const id = nextToolCallId++;
2974
+ return new Promise((resolve, reject) => {
2975
+ pendingToolCalls.set(id, { resolve, reject });
2976
+ // Args go through structured cloning. Functions, classes, etc. don't
2977
+ // survive the boundary — that's fine for our tool surface.
2978
+ try {
2979
+ parentPort.postMessage({ type: 'toolCall', id, name, args });
2980
+ } catch (e) {
2981
+ pendingToolCalls.delete(id);
2982
+ reject(new Error('postMessage failed for tool ' + name + ': ' + (e && e.message)));
2983
+ }
2984
+ });
2985
+ };
2986
+ }
2987
+ function setToolStubs(toolNames) {
2988
+ for (const name of toolNames) {
2989
+ sandbox[name] = makeToolStub(name);
2990
+ }
2991
+ }
2992
+
2993
+ function serializeError(e) {
2994
+ if (e instanceof Error) {
2995
+ const stack = e.stack ? '\n' + e.stack.split('\n').slice(0, 6).join('\n') : '';
2996
+ return e.name + ': ' + e.message + stack;
2997
+ }
2998
+ const t = typeof e;
2999
+ if (t === 'object' && e !== null) {
3000
+ let s = '';
3001
+ try { s = JSON.stringify(e); } catch { s = '[unserializable]'; }
3002
+ if (s === '{}' || s === '[]') {
3003
+ const ctor = (e && e.constructor && e.constructor.name) || 'Object';
3004
+ return '[non-Error throw: empty ' + ctor + ' — likely \`throw {}\` or thrown DOM exception]';
3005
+ }
3006
+ return '[non-Error throw: ' + s.slice(0, 500) + ']';
3007
+ }
3008
+ return '[' + t + ' throw: ' + String(e).slice(0, 200) + ']';
3009
+ }
3010
+
3011
+ let timeoutMsDefault = 30000;
3012
+
3013
+ parentPort.on('message', async (msg) => {
3014
+ if (msg.type === 'init') {
3015
+ timeoutMsDefault = msg.timeoutMs || 30000;
3016
+ setToolStubs(msg.toolNames || []);
3017
+ parentPort.postMessage({ type: 'ready' });
3018
+ return;
3019
+ }
3020
+ if (msg.type === 'setTools') {
3021
+ setToolStubs(msg.toolNames || []);
3022
+ return;
3023
+ }
3024
+ if (msg.type === 'toolResult') {
3025
+ const pending = pendingToolCalls.get(msg.id);
3026
+ if (!pending) return;
3027
+ pendingToolCalls.delete(msg.id);
3028
+ if (msg.ok) pending.resolve(msg.value);
3029
+ else pending.reject(new Error(msg.error || 'tool call failed'));
3030
+ return;
3031
+ }
3032
+ if (msg.type === 'runCode') {
3033
+ const t0 = Date.now();
3034
+ stdoutChunks = []; stdoutBytes = 0; truncated = false;
3035
+ let error = null;
3036
+ const wrapped = '(async () => {\n' + msg.code + '\n})()';
3037
+ try {
3038
+ const promise = vm.runInContext(wrapped, ctx, {
3039
+ timeout: msg.timeoutMs || timeoutMsDefault,
3040
+ displayErrors: true,
3041
+ });
3042
+ await promise;
3043
+ } catch (e) {
3044
+ error = serializeError(e);
3045
+ }
3046
+ parentPort.postMessage({
3047
+ type: 'runResult',
3048
+ id: msg.id,
3049
+ stdout: collectStdout(),
3050
+ error,
3051
+ truncated,
3052
+ durationMs: Date.now() - t0,
3053
+ });
3054
+ return;
3055
+ }
3056
+ });
3057
+ `;
2887
3058
  //#endregion
2888
3059
  //#region src/config/toolSafety.ts
2889
3060
  /**
@@ -3055,7 +3226,7 @@ function buildPlanModePromptSection(planModeFilePath) {
3055
3226
 
3056
3227
  The user has cycled into plan mode (Shift+Tab). Plan mode restricts WRITING, not READING. You still have a complete read toolkit — use it.
3057
3228
 
3058
- **Tools available in plan mode (use these aggressively):**
3229
+ **Tools available in plan mode:**
3059
3230
  - \`grep_search\` — find symbols, strings, patterns across files
3060
3231
  - \`glob_files\` — list files by pattern (use this instead of \`ls\` / \`find\`)
3061
3232
  - \`file_read\` — read file contents
@@ -3080,8 +3251,8 @@ Ground every claim in files you have actually read. Do not write pseudocode in c
3080
3251
  Follow this phased workflow. Do not skip phases.
3081
3252
 
3082
3253
  ### Phase 1 — Understand
3083
- - Read the relevant code before forming opinions. Use grep_search / glob_files / file_read directly for narrow lookups.
3084
- - For broad or uncertain scope, delegate to the 'explore' subagent via agent_delegate so the main context stays focused.
3254
+ - **Default to delegation.** For any task touching more than 2-3 files, delegate to the 'explore' subagent via agent_delegate *first*. Read directly only for narrow single-file lookups or to verify a specific line after the subagent reports back.
3255
+ - **Read budget: each file at most once.** If you've used ~5 read tools and still feel uncertain about scope, the spec is ambiguous call ask_user_question instead of reading more. Additional reads will not resolve ambiguity; they will just bloat context.
3085
3256
  - Identify what already exists vs. what needs to be built. Reuse existing functions and patterns rather than proposing new ones.
3086
3257
 
3087
3258
  ### Phase 2 — Clarify (REQUIRED if anything is ambiguous)
@@ -3262,7 +3433,11 @@ You have tools for tracking decisions, blockers, and human review gates during y
3262
3433
 
3263
3434
  - request_review_gate: Pause for explicit human approval before crossing a significant decision point — one that affects interpretation, evidence, cost, credentials, platform, or public commitment (e.g., narrowing research scope after synthesis, hard-to-reverse refactors, architectural pivots, dependency swaps). Do NOT use for routine operations or actions already covered by the standard permission system (file edits, bash commands). Treat a rejection as a hard stop — re-plan, do not retry.
3264
3435
 
3265
- These tools are lightweight — use them naturally as part of your work, not as a ceremony.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3436
+ These tools are lightweight — use them naturally as part of your work, not as a ceremony.
3437
+
3438
+ ## Working Directory
3439
+
3440
+ The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3266
3441
  }
3267
3442
  /**
3268
3443
  * Build the minimal-variant system prompt. Reuses the project context,
@@ -3286,7 +3461,11 @@ function buildMinimalSystemPrompt(config = {}) {
3286
3461
  Guidelines:
3287
3462
  - Be concise in your responses.
3288
3463
  - Show file paths clearly when working with files.
3289
- - When the task is done, give the user a direct answer — no recap of steps already visible in the tool history.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3464
+ - When the task is done, give the user a direct answer — no recap of steps already visible in the tool history.
3465
+
3466
+ ## Working Directory
3467
+
3468
+ The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3290
3469
  }
3291
3470
  /**
3292
3471
  * Pick a system prompt by variant. The dispatch point for the
@@ -3381,6 +3560,42 @@ function isWriteTargetingPlanFile(toolName, args, cwd = process.cwd()) {
3381
3560
  return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
3382
3561
  }
3383
3562
  //#endregion
3563
+ //#region ../../b4m-core/utils/dist/llm/backend.mjs
3564
+ /**
3565
+ * Shared implementation for OpenAI-style backends that store separate
3566
+ * `role: 'tool'` messages with `tool_call_id`. Mutates in place.
3567
+ */
3568
+ function replaceLastToolResultObservationOpenAI(messages, toolCallId, newObservation) {
3569
+ for (let i = messages.length - 1; i >= 0; i--) {
3570
+ const msg = messages[i];
3571
+ if (msg.role === "tool" && msg.tool_call_id === toolCallId) {
3572
+ msg.content = newObservation;
3573
+ return;
3574
+ }
3575
+ }
3576
+ throw new Error(`replaceLastToolResultObservation: no role=tool message with tool_call_id="${toolCallId}" found in messages`);
3577
+ }
3578
+ /**
3579
+ * Shared implementation for `getLatestToolCallId` on OpenAI-style backends.
3580
+ * Scans assistant messages backwards for a `tool_calls` array (set by
3581
+ * `pushToolMessages`) containing a function call with matching `name`.
3582
+ *
3583
+ * Tie-break: when a single assistant message contains multiple matching calls
3584
+ * (parallel tool use), this returns the LAST entry in `tool_calls` array order.
3585
+ * Callers that need to disambiguate between parallel calls must track the id
3586
+ * at dispatch time rather than recover it from history.
3587
+ */
3588
+ function getLatestToolCallIdOpenAI(messages, toolName) {
3589
+ for (let i = messages.length - 1; i >= 0; i--) {
3590
+ const msg = messages[i];
3591
+ if (msg.role !== "assistant" || !Array.isArray(msg.tool_calls)) continue;
3592
+ for (let j = msg.tool_calls.length - 1; j >= 0; j--) {
3593
+ const call = msg.tool_calls[j];
3594
+ if (call.function?.name === toolName && call.id) return call.id;
3595
+ }
3596
+ }
3597
+ }
3598
+ //#endregion
3384
3599
  //#region ../../b4m-core/utils/dist/imageGeneration/AIImageService.mjs
3385
3600
  /**
3386
3601
  * Abstract class for AI Image Service
@@ -5526,6 +5741,12 @@ var OpenAIBackend = class {
5526
5741
  tool_call_id: tool.id
5527
5742
  });
5528
5743
  }
5744
+ replaceLastToolResultObservation(messages, toolCallId, newObservation) {
5745
+ replaceLastToolResultObservationOpenAI(messages, toolCallId, newObservation);
5746
+ }
5747
+ getLatestToolCallId(messages, toolName) {
5748
+ return getLatestToolCallIdOpenAI(messages, toolName);
5749
+ }
5529
5750
  formatTools(tools = []) {
5530
5751
  return tools.map((tool) => ({
5531
5752
  type: "function",
@@ -18421,11 +18642,11 @@ function parseQuery(query) {
18421
18642
  */
18422
18643
  const getCliOnlyTools = async () => {
18423
18644
  const [{ createFileTool }, { globFilesTool }, { grepSearchTool }, { deleteFileTool }, { bashExecuteTool }] = await Promise.all([
18424
- import("./createFile-C1JoeuYh-LvIRJtxM.mjs"),
18425
- import("./globFiles-Bez8QCbS-DyZsKEJB.mjs"),
18426
- import("./grepSearch-DMuEcUSq-D3RsGLCg.mjs"),
18427
- import("./deleteFile-BTberNGj-BpBvrcoC.mjs"),
18428
- import("./bashExecute-pYljpfPn-Bsh-jb3S.mjs")
18645
+ import("./createFile-C1JoeuYh-metInFKd.mjs"),
18646
+ import("./globFiles-Bez8QCbS-DZb6McbJ.mjs"),
18647
+ import("./grepSearch-BxucZWO8-lPRv6R6F.mjs"),
18648
+ import("./deleteFile-BTberNGj-CW922hRM.mjs"),
18649
+ import("./bashExecute-pYljpfPn-BZXHMQEl.mjs")
18429
18650
  ]);
18430
18651
  return {
18431
18652
  file_read: fileReadTool,
@@ -19081,7 +19302,7 @@ function cleanupSandboxFiles(paths) {
19081
19302
  */
19082
19303
  async function captureViolations(isSandboxed, result, command, orchestrator) {
19083
19304
  if (!isSandboxed || !orchestrator || !command) return;
19084
- const { parseSandboxStderr, toSandboxViolations } = await import("./StderrViolationParser-D0afQ3-1.mjs");
19305
+ const { parseSandboxStderr, toSandboxViolations } = await import("./StderrViolationParser-BFP4bo7I.mjs");
19085
19306
  const parsed = parseSandboxStderr(result);
19086
19307
  if (parsed.length > 0) {
19087
19308
  const violations = toSandboxViolations(parsed, command);
@@ -23514,7 +23735,8 @@ function createTodoStore(onUpdate) {
23514
23735
  //#endregion
23515
23736
  //#region src/tools/findDefinitionTool.ts
23516
23737
  const execFileAsync = promisify(execFile);
23517
- const require = createRequire(import.meta.url);
23738
+ /** Cached ripgrep binary path after first resolution */
23739
+ let cachedRgPath = null;
23518
23740
  const VALID_KINDS = [
23519
23741
  "class",
23520
23742
  "function",
@@ -23558,14 +23780,23 @@ const KIND_KEYWORDS = {
23558
23780
  ]
23559
23781
  };
23560
23782
  const ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
23561
- function getRipgrepPath() {
23783
+ /**
23784
+ * Resolve ripgrep binary path via the package's exported `rgPath`.
23785
+ * `@vscode/ripgrep` is an optional dependency, so we load it lazily; in 1.18+
23786
+ * the binary lives in a platform-specific sibling package and is resolved by
23787
+ * the package itself — we just have to ask for it.
23788
+ */
23789
+ async function getRipgrepPath() {
23790
+ if (cachedRgPath) return cachedRgPath;
23791
+ let rgPath;
23562
23792
  try {
23563
- const ripgrepPath = require.resolve("@vscode/ripgrep");
23564
- const ripgrepDir = path.dirname(ripgrepPath);
23565
- return path.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
23793
+ ({rgPath} = await import("@vscode/ripgrep"));
23566
23794
  } catch {
23567
- throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
23795
+ throw new Error("ripgrep is not available. Install the optional dependency: pnpm add @vscode/ripgrep --filter @bike4mind/cli");
23568
23796
  }
23797
+ if (!rgPath || !existsSync(rgPath)) throw new Error(`ripgrep binary not found at ${rgPath ?? "<unresolved>"}. Ensure @vscode/ripgrep platform optional dependencies are installed for ${process.platform}-${process.arch}.`);
23798
+ cachedRgPath = rgPath;
23799
+ return rgPath;
23569
23800
  }
23570
23801
  function isPathWithinWorkspace(targetPath, baseCwd) {
23571
23802
  const resolvedTarget = path.resolve(targetPath);
@@ -23619,7 +23850,7 @@ async function findDefinitions(params) {
23619
23850
  if (error.code === "ENOENT") throw new Error(`Path does not exist: ${search_path}`);
23620
23851
  throw error;
23621
23852
  }
23622
- const rgPath = getRipgrepPath();
23853
+ const rgPath = await getRipgrepPath();
23623
23854
  const rgArgs = [
23624
23855
  "--json",
23625
23856
  "--max-count",
@@ -23796,7 +24027,7 @@ function createGetFileStructureTool() {
23796
24027
  if (stats.isDirectory()) return `Error: Path is a directory, not a file: ${params.path}`;
23797
24028
  if (stats.size > MAX_FILE_SIZE) return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE / 1024 / 1024}MB`;
23798
24029
  const ext = path.extname(resolvedPath).toLowerCase();
23799
- const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-BFTHnDwH.mjs");
24030
+ const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-Cw2LbVZT.mjs");
23800
24031
  const languageId = getLanguageForExtension(ext);
23801
24032
  if (!languageId) return `Error: Unsupported file type "${ext}". Supported languages: ${getSupportedLanguages().join(", ")}`;
23802
24033
  const sourceCode = await promises.readFile(resolvedPath, "utf-8");
@@ -4,7 +4,7 @@ import { homedir } from "os";
4
4
  import path from "path";
5
5
  import axios from "axios";
6
6
  //#region package.json
7
- var version = "0.9.0";
7
+ var version = "0.9.2";
8
8
  //#endregion
9
9
  //#region src/utils/updateChecker.ts
10
10
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -121,11 +121,11 @@
121
121
  "tsx": "^4.21.0",
122
122
  "typescript": "^5.9.3",
123
123
  "vitest": "^4.1.2",
124
- "@bike4mind/agents": "0.8.3",
125
- "@bike4mind/common": "2.94.0",
126
- "@bike4mind/mcp": "1.37.7",
127
- "@bike4mind/services": "2.82.0",
128
- "@bike4mind/utils": "2.20.0"
124
+ "@bike4mind/agents": "0.9.0",
125
+ "@bike4mind/common": "2.95.0",
126
+ "@bike4mind/mcp": "1.37.8",
127
+ "@bike4mind/services": "2.83.0",
128
+ "@bike4mind/utils": "2.21.0"
129
129
  },
130
130
  "optionalDependencies": {
131
131
  "@vscode/ripgrep": "^1.17.1"
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as ProxyManager } from "./ProxyManager-Dl2nFk-A.mjs";
3
- export { ProxyManager };