@bike4mind/cli 0.4.0 → 0.5.0

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-DcJC1C8S.mjs";
2
+ import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-Boy2GLk4.mjs";
3
3
  import { execSync } from "child_process";
4
4
  import { constants, existsSync, promises } from "fs";
5
5
  import { homedir } from "os";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { B as ReActAgent, C as loadContextFiles, D as generateCliTools, E as PermissionManager, F as setWebSocketToolExecutor, H as CheckpointStore, L as buildCoreSystemPrompt, V as CustomCommandStore, W as SessionStore, _ as WebSocketLlmBackend, a as createCoordinateTaskTool, c as createAgentDelegateTool, d as createSkillTool, g as FallbackLlmBackend, h as WebSocketConnectionManager, i as createWriteTodosTool, l as AgentStore, m as WebSocketToolExecutor, n as createFindDefinitionTool, o as createBackgroundAgentTools, p as ApiClient, r as createTodoStore, s as BackgroundAgentManager, t as createGetFileStructureTool, u as SubagentOrchestrator, v as ServerLlmBackend, w as getApiUrl, y as McpManager, z as isReadOnlyTool } from "../tools-DiSJh1Dv.mjs";
2
+ import { A as getApiUrl, C as WebSocketLlmBackend, G as isReadOnlyTool, J as CheckpointStore, K as ReActAgent, M as PermissionManager, N as generateCliTools, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, X as SessionStore, _ as createSkillTool, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, p as BackgroundAgentManager, q as CustomCommandStore, s as createGetFileStructureTool, u as createWriteTodosTool, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient } from "../tools-V5FJ83BJ.mjs";
3
3
  import { n as logger, t as ConfigStore } from "../ConfigStore-DBUmvCfe.mjs";
4
4
  import { t as DEFAULT_SANDBOX_CONFIG } from "../types-DBEjF9YS.mjs";
5
5
  import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-C1B4t20N.mjs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as version, r as forceCheckForUpdate } from "../updateChecker-DcJC1C8S.mjs";
2
+ import { i as version, r as forceCheckForUpdate } from "../updateChecker-Boy2GLk4.mjs";
3
3
  import { execSync } from "child_process";
4
4
  //#region src/commands/updateCommand.ts
5
5
  /**
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-B7-LLvvx.mjs";
3
- import { A as DEFAULT_MAX_ITERATIONS, B as ReActAgent, C as loadContextFiles, D as generateCliTools, E as PermissionManager, F as setWebSocketToolExecutor, G as OAuthClient, H as CheckpointStore, I as OllamaBackend, J as searchCommands, K as hasFileReferences, L as buildCoreSystemPrompt, M as DEFAULT_THOROUGHNESS, N as clearFeatureModuleTools, O as ALWAYS_DENIED_FOR_AGENTS, P as registerFeatureModuleTools, Q as warmFileCache, R as buildSkillsPromptSection, S as extractCompactInstructions, T as getEnvironmentName, U as CommandHistoryStore, V as CustomCommandStore, W as SessionStore, X as formatFileSize, Y as mergeCommands, Z as searchFiles, _ as WebSocketLlmBackend, a as createCoordinateTaskTool, b as substituteArguments, c as createAgentDelegateTool, d as createSkillTool, f as parseAgentConfig, g as FallbackLlmBackend, h as WebSocketConnectionManager, i as createWriteTodosTool, j as DEFAULT_RETRY_CONFIG, k as DEFAULT_AGENT_MODEL, l as AgentStore, m as WebSocketToolExecutor, n as createFindDefinitionTool, o as createBackgroundAgentTools, p as ApiClient, q as processFileReferences, r as createTodoStore, s as BackgroundAgentManager, t as createGetFileStructureTool, u as SubagentOrchestrator, v as ServerLlmBackend, w as getApiUrl, x as formatStep, y as McpManager, z as isReadOnlyTool } from "./tools-DiSJh1Dv.mjs";
3
+ import { $ as processFileReferences, A as getApiUrl, B as registerFeatureModuleTools, C as WebSocketLlmBackend, D as formatStep, E as substituteArguments, F as DEFAULT_AGENT_MODEL, G as isReadOnlyTool, H as OllamaBackend, I as DEFAULT_MAX_ITERATIONS, J as CheckpointStore, K as ReActAgent, L as DEFAULT_RETRY_CONFIG, M as PermissionManager, N as generateCliTools, O as extractCompactInstructions, P as ALWAYS_DENIED_FOR_AGENTS, Q as hasFileReferences, R as DEFAULT_THOROUGHNESS, S as FallbackLlmBackend, T as McpManager, U as buildCoreSystemPrompt, V as setWebSocketToolExecutor, W as buildSkillsPromptSection, X as SessionStore, Y as CommandHistoryStore, Z as OAuthClient, _ as createSkillTool, a as createDecisionStore, b as WebSocketToolExecutor, c as createFindDefinitionTool, d as createCoordinateTaskTool, et as searchCommands, f as createBackgroundAgentTools, g as SubagentOrchestrator, h as AgentStore, i as createDecisionLogTool, it as warmFileCache, j as getEnvironmentName, k as loadContextFiles, l as createTodoStore, m as createAgentDelegateTool, n as createBlockerTools, nt as formatFileSize, o as formatDecisionsOutput, p as BackgroundAgentManager, q as CustomCommandStore, r as formatBlockersOutput, rt as searchFiles, s as createGetFileStructureTool, t as createBlockerStore, tt as mergeCommands, u as createWriteTodosTool, v as parseAgentConfig, w as ServerLlmBackend, x as WebSocketConnectionManager, y as ApiClient, z as clearFeatureModuleTools } from "./tools-V5FJ83BJ.mjs";
4
4
  import { Mt as validateNotebookPath$1, g as ChatModels, jt as validateJupyterKernelName, m as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore } from "./ConfigStore-DBUmvCfe.mjs";
5
- import { i as version, t as checkForUpdate } from "./updateChecker-DcJC1C8S.mjs";
5
+ import { i as version, t as checkForUpdate } from "./updateChecker-Boy2GLk4.mjs";
6
6
  import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
7
7
  import { Box, Static, Text, render, useApp, useInput } from "ink";
8
8
  import { execSync } from "child_process";
@@ -5036,6 +5036,8 @@ function CliApp() {
5036
5036
  const [initError, setInitError] = useState(null);
5037
5037
  const [commandHistory, setCommandHistory] = useState([]);
5038
5038
  const imageStoreInitPromise = useRef(null);
5039
+ const decisionStoreRef = useRef(createDecisionStore());
5040
+ const blockerStoreRef = useRef(createBlockerStore());
5039
5041
  const setStoreSession = useCliStore((state) => state.setSession);
5040
5042
  const enqueuePermissionPrompt = useCliStore((state) => state.enqueuePermissionPrompt);
5041
5043
  const enqueueUserQuestionPrompt = useCliStore((state) => state.enqueueUserQuestionPrompt);
@@ -5478,6 +5480,15 @@ function CliApp() {
5478
5480
  logger.warn(`⚠️ Model "${fromModel}" failed (${error.message}) — falling back to "${toModel}"`);
5479
5481
  }) : llm, backgroundManager);
5480
5482
  const writeTodosTool = createWriteTodosTool(createTodoStore());
5483
+ const decisionLogTool = createDecisionLogTool(decisionStoreRef.current);
5484
+ const blockerTools = createBlockerTools(blockerStoreRef.current);
5485
+ if (newSession.metadata.workflow) {
5486
+ decisionStoreRef.current.decisions = [...newSession.metadata.workflow.decisions];
5487
+ blockerStoreRef.current.blockers = [...newSession.metadata.workflow.blockers];
5488
+ } else {
5489
+ decisionStoreRef.current.decisions = [];
5490
+ blockerStoreRef.current.blockers = [];
5491
+ }
5481
5492
  const enableSkillTool = config.preferences.enableSkillTool !== false;
5482
5493
  const skillTool = enableSkillTool ? createSkillTool({
5483
5494
  customCommandStore: state.customCommandStore,
@@ -5495,6 +5506,8 @@ function CliApp() {
5495
5506
  agentDelegateTool,
5496
5507
  ...backgroundTools,
5497
5508
  writeTodosTool,
5509
+ decisionLogTool,
5510
+ ...blockerTools,
5498
5511
  findDefinitionTool,
5499
5512
  getFileStructureTool
5500
5513
  ];
@@ -5924,7 +5937,13 @@ function CliApp() {
5924
5937
  ...currentSession.metadata,
5925
5938
  totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
5926
5939
  totalCredits: (currentSession.metadata.totalCredits || 0) + (result.completionInfo.totalCredits || 0),
5927
- toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
5940
+ toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls,
5941
+ workflow: decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0 ? {
5942
+ decisions: decisionStoreRef.current.decisions,
5943
+ blockers: blockerStoreRef.current.blockers,
5944
+ handoff: currentSession.metadata.workflow?.handoff,
5945
+ reviewGates: currentSession.metadata.workflow?.reviewGates
5946
+ } : currentSession.metadata.workflow
5928
5947
  }
5929
5948
  };
5930
5949
  setState((prev) => ({
@@ -6540,6 +6559,12 @@ Multi-line Input:
6540
6559
  }
6541
6560
  const sessionName = args.join(" ") || state.session.name;
6542
6561
  state.session.name = sessionName;
6562
+ if (decisionStoreRef.current.decisions.length > 0 || blockerStoreRef.current.blockers.length > 0) state.session.metadata.workflow = {
6563
+ decisions: decisionStoreRef.current.decisions,
6564
+ blockers: blockerStoreRef.current.blockers,
6565
+ handoff: state.session.metadata.workflow?.handoff,
6566
+ reviewGates: state.session.metadata.workflow?.reviewGates
6567
+ };
6543
6568
  await state.sessionStore.save(state.session);
6544
6569
  console.log(`✅ Session saved as "${sessionName}"`);
6545
6570
  break;
@@ -6800,6 +6825,8 @@ Multi-line Input:
6800
6825
  };
6801
6826
  await logger.initialize(newSession.id);
6802
6827
  logger.debug("=== New Session Started via /clear ===");
6828
+ decisionStoreRef.current.decisions = [];
6829
+ blockerStoreRef.current.blockers = [];
6803
6830
  if (state.checkpointStore) state.checkpointStore.setSessionId(newSession.id);
6804
6831
  setState((prev) => ({
6805
6832
  ...prev,
@@ -7491,6 +7518,16 @@ Multi-line Input:
7491
7518
  console.log(`✅ Removed directory: ${resolvedRemovePath}`);
7492
7519
  break;
7493
7520
  }
7521
+ case "decisions":
7522
+ console.log("\n📋 Decision Log\n");
7523
+ console.log(formatDecisionsOutput(decisionStoreRef.current.decisions));
7524
+ console.log("");
7525
+ break;
7526
+ case "blockers":
7527
+ console.log("\n🚧 Blockers\n");
7528
+ console.log(formatBlockersOutput(blockerStoreRef.current.blockers));
7529
+ console.log("");
7530
+ break;
7494
7531
  case "dirs": {
7495
7532
  const additionalDirs = await state.configStore.getAdditionalDirectories();
7496
7533
  const cwd = process.cwd();
@@ -522,6 +522,14 @@ const COMMANDS = [
522
522
  {
523
523
  name: "dirs",
524
524
  description: "List all accessible directories"
525
+ },
526
+ {
527
+ name: "decisions",
528
+ description: "Show decision log for current session"
529
+ },
530
+ {
531
+ name: "blockers",
532
+ description: "Show tracked blockers for current session"
525
533
  }
526
534
  ];
527
535
  /**
@@ -3015,7 +3023,18 @@ EXAMPLES:
3015
3023
  - "what packages installed?" → ${TOOL_GLOB_FILES} "**/package.json" → ${TOOL_FILE_READ}
3016
3024
  - "find all components using React hooks" → ${TOOL_SUBAGENT_DELEGATE}(task="find all components using React hooks", type="explore")
3017
3025
 
3018
- Remember: Use context from previous messages to understand follow-up questions.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}`;
3026
+ Remember: Use context from previous messages to understand follow-up questions.
3027
+
3028
+ DURABLE WORKFLOW TRACKING:
3029
+ You have tools for tracking decisions and blockers during your work. These create an audit trail that persists across sessions, enabling anyone to understand why things were done and what's still outstanding.
3030
+
3031
+ - log_decision: When you make a significant decision (architecture choice, scope narrowing, interpretation of ambiguous requirements, trade-off between alternatives), log it with rationale. Do NOT log trivial decisions. Log decisions that would matter if someone needed to understand WHY you did something or if they needed to resume this work.
3032
+
3033
+ - track_blocker: When you encounter something blocking progress (missing information, unclear requirements, external dependencies, ambiguous specs that need human clarification), track it. This makes blockers visible so they can be addressed.
3034
+
3035
+ - resolve_blocker: When a blocker is cleared, record how it was resolved. Use the blocker ID from the track_blocker output.
3036
+
3037
+ These tools are lightweight — use them naturally as part of your work, not as a ceremony.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}`;
3019
3038
  }
3020
3039
  /**
3021
3040
  * Build the dynamic agent creation prompt section
@@ -6675,15 +6694,26 @@ const webSearchTool = {
6675
6694
  };
6676
6695
  //#endregion
6677
6696
  //#region ../../b4m-core/services/dist/llm/tools/implementation/webfetch/index.mjs
6697
+ const DEFAULT_TIMEOUT_MS = 6e4;
6698
+ const PDF_TIMEOUT_MS = 9e4;
6699
+ function isPdfUrl(url) {
6700
+ try {
6701
+ const { pathname } = new URL(url);
6702
+ return pathname.toLowerCase().endsWith(".pdf");
6703
+ } catch {
6704
+ return false;
6705
+ }
6706
+ }
6678
6707
  /**
6679
6708
  * Fetch URL content using Firecrawl (shared function, no ToolContext)
6680
6709
  * Pattern follows serpApiSearch from websearch tool
6681
6710
  *
6682
6711
  * @param adapters - Database adapters for fetching Firecrawl API key
6683
6712
  * @param url - URL to fetch
6713
+ * @param options - Optional configuration (e.g. maxTimeoutMs for Lambda-constrained callers)
6684
6714
  * @returns Markdown content and title
6685
6715
  */
6686
- async function firecrawlFetch(adapters, url) {
6716
+ async function firecrawlFetch(adapters, url, options) {
6687
6717
  if (!/^https?:\/\/.+/i.test(url)) throw new Error(`Invalid URL format: ${url}. URL must start with http:// or https://`);
6688
6718
  const apiKeySetting = await adapters.db.adminSettings.findBySettingName("FirecrawlApiKey");
6689
6719
  if (!apiKeySetting?.settingValue) {
@@ -6691,45 +6721,45 @@ async function firecrawlFetch(adapters, url) {
6691
6721
  throw new Error("Firecrawl API key not configured");
6692
6722
  }
6693
6723
  const app = new FirecrawlApp({ apiKey: apiKeySetting.settingValue });
6694
- const controller = new AbortController();
6695
- const timeoutId = setTimeout(() => controller.abort(), 6e4);
6696
- try {
6697
- console.log("📥 WebFetch Tool: Scraping URL with Firecrawl...");
6698
- let result;
6699
- try {
6700
- result = await app.scrapeUrl(url, {
6701
- formats: ["markdown"],
6702
- actions: [{
6703
- type: "wait",
6704
- milliseconds: 1e3
6705
- }]
6706
- });
6707
- } catch (scrapeError) {
6708
- if ((scrapeError instanceof Error ? scrapeError.message : "").includes("Actions are not supported")) {
6709
- console.log("📥 WebFetch Tool: Actions not supported, retrying without actions...");
6710
- result = await app.scrapeUrl(url, { formats: ["markdown"] });
6711
- } else throw scrapeError;
6712
- }
6713
- clearTimeout(timeoutId);
6714
- if (!result || result.error) {
6715
- const errorMessage = result?.error || "Unknown error";
6716
- console.error("❌ WebFetch Tool: Firecrawl error:", errorMessage);
6717
- throw new Error(`Failed to fetch content from URL: ${errorMessage}`);
6718
- }
6719
- if (!("markdown" in result) || !result.markdown) {
6720
- console.error("❌ WebFetch Tool: No markdown content returned");
6721
- throw new Error("No content could be extracted from the URL");
6722
- }
6723
- const markdown = result.markdown.slice(0, 5e4);
6724
- console.log(`📄 WebFetch Tool: Extracted ${markdown.length} characters of content`);
6725
- return {
6726
- markdown,
6727
- title: result.metadata?.title
6728
- };
6729
- } catch (error) {
6730
- clearTimeout(timeoutId);
6731
- throw error;
6732
- }
6724
+ const isPdf = isPdfUrl(url);
6725
+ const desiredTimeout = isPdf ? PDF_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
6726
+ const timeoutMs = options?.maxTimeoutMs ? Math.min(desiredTimeout, options.maxTimeoutMs) : desiredTimeout;
6727
+ console.log(`📥 WebFetch Tool: Scraping URL with Firecrawl${isPdf ? " (PDF mode, extended timeout)" : ""}...`);
6728
+ const baseParams = {
6729
+ formats: ["markdown"],
6730
+ timeout: timeoutMs
6731
+ };
6732
+ let result;
6733
+ if (isPdf) result = await app.scrapeUrl(url, baseParams);
6734
+ else try {
6735
+ result = await app.scrapeUrl(url, {
6736
+ ...baseParams,
6737
+ actions: [{
6738
+ type: "wait",
6739
+ milliseconds: 1e3
6740
+ }]
6741
+ });
6742
+ } catch (scrapeError) {
6743
+ if ((scrapeError instanceof Error ? scrapeError.message : "").includes("Actions are not supported")) {
6744
+ console.log("📥 WebFetch Tool: Actions not supported, retrying without actions...");
6745
+ result = await app.scrapeUrl(url, baseParams);
6746
+ } else throw scrapeError;
6747
+ }
6748
+ if (!result || result.error) {
6749
+ const errorMessage = result?.error || "Unknown error";
6750
+ console.error("❌ WebFetch Tool: Firecrawl error:", errorMessage);
6751
+ throw new Error(`Failed to fetch content from URL: ${errorMessage}`);
6752
+ }
6753
+ if (!("markdown" in result) || !result.markdown) {
6754
+ console.error("❌ WebFetch Tool: No markdown content returned");
6755
+ throw new Error("No content could be extracted from the URL");
6756
+ }
6757
+ const markdown = result.markdown.slice(0, 5e4);
6758
+ console.log(`📄 WebFetch Tool: Extracted ${markdown.length} characters of content`);
6759
+ return {
6760
+ markdown,
6761
+ title: result.metadata?.title
6762
+ };
6733
6763
  }
6734
6764
  const webFetchTool = {
6735
6765
  name: "web_fetch",
@@ -6785,6 +6815,14 @@ const webFetchTool = {
6785
6815
  await context.statusUpdate({}, `WebFetch: ${statusLabel}`);
6786
6816
  return userMessage;
6787
6817
  }
6818
+ if (error instanceof FirecrawlError && error.statusCode === 500 && errorMessage.includes("PDF")) {
6819
+ context.logger.warn("WebFetch PDF too large for Firecrawl", {
6820
+ url: params.url,
6821
+ error: errorMessage
6822
+ });
6823
+ await context.statusUpdate({}, "WebFetch: PDF too large");
6824
+ return `This PDF is too large to process via URL. Please download the file and upload it directly instead.`;
6825
+ }
6788
6826
  if ([
6789
6827
  "no response received",
6790
6828
  "econnrefused",
@@ -23479,4 +23517,234 @@ function createGetFileStructureTool() {
23479
23517
  };
23480
23518
  }
23481
23519
  //#endregion
23482
- export { DEFAULT_MAX_ITERATIONS as A, ReActAgent as B, loadContextFiles as C, generateCliTools as D, PermissionManager as E, setWebSocketToolExecutor as F, OAuthClient as G, CheckpointStore as H, OllamaBackend as I, searchCommands as J, hasFileReferences as K, buildCoreSystemPrompt as L, DEFAULT_THOROUGHNESS as M, clearFeatureModuleTools as N, ALWAYS_DENIED_FOR_AGENTS as O, registerFeatureModuleTools as P, warmFileCache as Q, buildSkillsPromptSection as R, extractCompactInstructions as S, getEnvironmentName as T, CommandHistoryStore as U, CustomCommandStore as V, SessionStore as W, formatFileSize$1 as X, mergeCommands as Y, searchFiles as Z, WebSocketLlmBackend as _, createCoordinateTaskTool as a, substituteArguments as b, createAgentDelegateTool as c, createSkillTool as d, parseAgentConfig as f, FallbackLlmBackend as g, WebSocketConnectionManager as h, createWriteTodosTool as i, DEFAULT_RETRY_CONFIG as j, DEFAULT_AGENT_MODEL as k, AgentStore as l, WebSocketToolExecutor as m, createFindDefinitionTool as n, createBackgroundAgentTools as o, ApiClient as p, processFileReferences as q, createTodoStore as r, BackgroundAgentManager as s, createGetFileStructureTool as t, SubagentOrchestrator as u, ServerLlmBackend as v, getApiUrl as w, formatStep as x, McpManager as y, isReadOnlyTool as z };
23520
+ //#region src/tools/decisionLogTool.ts
23521
+ /**
23522
+ * Validate log_decision parameters
23523
+ * @throws Error if validation fails
23524
+ */
23525
+ function validateParams(args) {
23526
+ const params = args;
23527
+ if (typeof params.summary !== "string" || params.summary.trim() === "") throw new Error("log_decision: summary must be a non-empty string");
23528
+ if (typeof params.rationale !== "string" || params.rationale.trim() === "") throw new Error("log_decision: rationale must be a non-empty string");
23529
+ if (params.alternatives !== void 0) {
23530
+ if (!Array.isArray(params.alternatives)) throw new Error("log_decision: alternatives must be an array of strings");
23531
+ for (const alt of params.alternatives) if (typeof alt !== "string") throw new Error("log_decision: each alternative must be a string");
23532
+ }
23533
+ if (params.context !== void 0 && typeof params.context !== "string") throw new Error("log_decision: context must be a string");
23534
+ return {
23535
+ summary: params.summary.trim(),
23536
+ rationale: params.rationale.trim(),
23537
+ alternatives: params.alternatives,
23538
+ context: typeof params.context === "string" ? params.context.trim() : void 0
23539
+ };
23540
+ }
23541
+ /**
23542
+ * Format decisions for display output
23543
+ */
23544
+ function formatDecisionsOutput(decisions) {
23545
+ if (decisions.length === 0) return "No decisions logged in this session.";
23546
+ return decisions.map((decision, index) => {
23547
+ const lines = [`${index + 1}. **${decision.summary}**`, ` Rationale: ${decision.rationale}`];
23548
+ if (decision.alternatives && decision.alternatives.length > 0) lines.push(` Alternatives considered: ${decision.alternatives.join(", ")}`);
23549
+ if (decision.context) lines.push(` Context: ${decision.context}`);
23550
+ const timestamp = new Date(decision.timestamp).toLocaleTimeString();
23551
+ lines.push(` Logged at: ${timestamp}`);
23552
+ return lines.join("\n");
23553
+ }).join("\n\n");
23554
+ }
23555
+ /**
23556
+ * Create the log_decision tool.
23557
+ *
23558
+ * Allows the AI to record significant decisions with rationale during a session.
23559
+ * Decisions are persisted in the session's workflow state for audit trail
23560
+ * and cross-session continuity.
23561
+ */
23562
+ function createDecisionLogTool(store) {
23563
+ return {
23564
+ toolFn: async (args) => {
23565
+ const params = validateParams(args);
23566
+ const decision = {
23567
+ id: v4(),
23568
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
23569
+ summary: params.summary,
23570
+ rationale: params.rationale,
23571
+ alternatives: params.alternatives,
23572
+ context: params.context
23573
+ };
23574
+ store.decisions.push(decision);
23575
+ if (store.onUpdate) store.onUpdate(store.decisions);
23576
+ return `Decision logged (#${store.decisions.length}): ${decision.summary}`;
23577
+ },
23578
+ toolSchema: {
23579
+ name: "log_decision",
23580
+ description: `Record a significant decision with its rationale for audit trail and session continuity.
23581
+
23582
+ **When to use:**
23583
+ - Architecture or design choices (e.g., "chose Zustand over Redux because...")
23584
+ - Scope narrowing or direction changes in research
23585
+ - Trade-off decisions between viable alternatives
23586
+ - Interpretation of ambiguous requirements
23587
+
23588
+ **When NOT to use:**
23589
+ - Routine operations (reading files, running tests)
23590
+ - Trivial choices that wouldn't matter to someone resuming this work
23591
+ - Implementation details that are obvious from the code
23592
+
23593
+ Log decisions that would matter if someone needed to understand WHY you did something.`,
23594
+ parameters: {
23595
+ type: "object",
23596
+ properties: {
23597
+ summary: {
23598
+ type: "string",
23599
+ description: "What was decided — a clear, concise statement"
23600
+ },
23601
+ rationale: {
23602
+ type: "string",
23603
+ description: "Why this decision was made — the reasoning behind it"
23604
+ },
23605
+ alternatives: {
23606
+ type: "array",
23607
+ items: { type: "string" },
23608
+ description: "What alternatives were considered (optional)"
23609
+ },
23610
+ context: {
23611
+ type: "string",
23612
+ description: "What triggered this decision — the situation or constraint (optional)"
23613
+ }
23614
+ },
23615
+ required: ["summary", "rationale"]
23616
+ }
23617
+ }
23618
+ };
23619
+ }
23620
+ /**
23621
+ * Create a new empty DecisionStore
23622
+ */
23623
+ function createDecisionStore(onUpdate) {
23624
+ return {
23625
+ decisions: [],
23626
+ onUpdate
23627
+ };
23628
+ }
23629
+ //#endregion
23630
+ //#region src/tools/blockerTool.ts
23631
+ /**
23632
+ * Format blockers for display output
23633
+ */
23634
+ function formatBlockersOutput(blockers) {
23635
+ if (blockers.length === 0) return "No blockers tracked in this session.";
23636
+ const open = blockers.filter((b) => b.status === "open");
23637
+ const resolved = blockers.filter((b) => b.status === "resolved");
23638
+ const lines = [];
23639
+ if (open.length > 0) {
23640
+ lines.push(`**Open blockers (${open.length}):**`);
23641
+ for (const blocker of open) lines.push(` - [${blocker.id.slice(0, 8)}] ${blocker.description}`);
23642
+ }
23643
+ if (resolved.length > 0) {
23644
+ if (open.length > 0) lines.push("");
23645
+ lines.push(`**Resolved blockers (${resolved.length}):**`);
23646
+ for (const blocker of resolved) {
23647
+ lines.push(` - [${blocker.id.slice(0, 8)}] ${blocker.description}`);
23648
+ lines.push(` Resolution: ${blocker.resolution ?? "(no resolution recorded)"}`);
23649
+ }
23650
+ }
23651
+ return lines.join("\n");
23652
+ }
23653
+ /**
23654
+ * Create the track_blocker and resolve_blocker tools.
23655
+ *
23656
+ * Allows the AI to track what's blocking progress and record resolutions.
23657
+ * Blockers are persisted in the session's workflow state for audit trail
23658
+ * and cross-session continuity.
23659
+ */
23660
+ function createBlockerTools(store) {
23661
+ return [{
23662
+ toolFn: async (args) => {
23663
+ const params = args;
23664
+ if (typeof params.description !== "string" || params.description.trim() === "") throw new Error("track_blocker: description must be a non-empty string");
23665
+ const blocker = {
23666
+ id: v4(),
23667
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
23668
+ description: params.description.trim(),
23669
+ status: "open"
23670
+ };
23671
+ store.blockers.push(blocker);
23672
+ if (store.onUpdate) store.onUpdate(store.blockers);
23673
+ const openCount = store.blockers.filter((b) => b.status === "open").length;
23674
+ return `Blocker tracked [${blocker.id.slice(0, 8)}]: ${blocker.description}\n(${openCount} open blocker${openCount === 1 ? "" : "s"})`;
23675
+ },
23676
+ toolSchema: {
23677
+ name: "track_blocker",
23678
+ description: `Track something that is blocking progress.
23679
+
23680
+ **When to use:**
23681
+ - Missing information or unclear requirements
23682
+ - External dependencies (waiting on API access, credentials, data)
23683
+ - Technical constraints discovered during work
23684
+ - Ambiguous requirements that need human clarification
23685
+
23686
+ **When NOT to use:**
23687
+ - Normal challenges that are part of the work
23688
+ - Things you can resolve immediately`,
23689
+ parameters: {
23690
+ type: "object",
23691
+ properties: { description: {
23692
+ type: "string",
23693
+ description: "What is blocking progress — be specific about what is needed to unblock"
23694
+ } },
23695
+ required: ["description"]
23696
+ }
23697
+ }
23698
+ }, {
23699
+ toolFn: async (args) => {
23700
+ const params = args;
23701
+ if (typeof params.blocker_id !== "string" || params.blocker_id.trim() === "") throw new Error("resolve_blocker: blocker_id must be a non-empty string");
23702
+ if (typeof params.resolution !== "string" || params.resolution.trim() === "") throw new Error("resolve_blocker: resolution must be a non-empty string");
23703
+ const blockerId = params.blocker_id.trim();
23704
+ const blocker = store.blockers.find((b) => b.id === blockerId || b.id.startsWith(blockerId));
23705
+ if (!blocker) {
23706
+ const openBlockers = store.blockers.filter((b) => b.status === "open");
23707
+ if (openBlockers.length === 0) return "No open blockers to resolve.";
23708
+ return `Blocker not found. Open blockers:\n${openBlockers.map((b) => ` [${b.id.slice(0, 8)}] ${b.description}`).join("\n")}`;
23709
+ }
23710
+ if (blocker.status === "resolved") return `Blocker [${blocker.id.slice(0, 8)}] is already resolved.`;
23711
+ blocker.status = "resolved";
23712
+ blocker.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
23713
+ blocker.resolution = params.resolution.trim();
23714
+ if (store.onUpdate) store.onUpdate(store.blockers);
23715
+ const openCount = store.blockers.filter((b) => b.status === "open").length;
23716
+ return `Blocker resolved [${blocker.id.slice(0, 8)}]: ${blocker.resolution}\n(${openCount} open blocker${openCount === 1 ? "" : "s"} remaining)`;
23717
+ },
23718
+ toolSchema: {
23719
+ name: "resolve_blocker",
23720
+ description: `Mark a blocker as resolved with a description of how it was resolved.
23721
+
23722
+ Use the blocker ID (or its first 8 characters) from the track_blocker output.`,
23723
+ parameters: {
23724
+ type: "object",
23725
+ properties: {
23726
+ blocker_id: {
23727
+ type: "string",
23728
+ description: "The ID of the blocker to resolve (full ID or first 8 characters)"
23729
+ },
23730
+ resolution: {
23731
+ type: "string",
23732
+ description: "How the blocker was resolved"
23733
+ }
23734
+ },
23735
+ required: ["blocker_id", "resolution"]
23736
+ }
23737
+ }
23738
+ }];
23739
+ }
23740
+ /**
23741
+ * Create a new empty BlockerStore
23742
+ */
23743
+ function createBlockerStore(onUpdate) {
23744
+ return {
23745
+ blockers: [],
23746
+ onUpdate
23747
+ };
23748
+ }
23749
+ //#endregion
23750
+ export { processFileReferences as $, getApiUrl as A, registerFeatureModuleTools as B, WebSocketLlmBackend as C, formatStep as D, substituteArguments as E, DEFAULT_AGENT_MODEL as F, isReadOnlyTool as G, OllamaBackend as H, DEFAULT_MAX_ITERATIONS as I, CheckpointStore as J, ReActAgent as K, DEFAULT_RETRY_CONFIG as L, PermissionManager as M, generateCliTools as N, extractCompactInstructions as O, ALWAYS_DENIED_FOR_AGENTS as P, hasFileReferences as Q, DEFAULT_THOROUGHNESS as R, FallbackLlmBackend as S, McpManager as T, buildCoreSystemPrompt as U, setWebSocketToolExecutor as V, buildSkillsPromptSection as W, SessionStore as X, CommandHistoryStore as Y, OAuthClient as Z, createSkillTool as _, createDecisionStore as a, WebSocketToolExecutor as b, createFindDefinitionTool as c, createCoordinateTaskTool as d, searchCommands as et, createBackgroundAgentTools as f, SubagentOrchestrator as g, AgentStore as h, createDecisionLogTool as i, warmFileCache as it, getEnvironmentName as j, loadContextFiles as k, createTodoStore as l, createAgentDelegateTool as m, createBlockerTools as n, formatFileSize$1 as nt, formatDecisionsOutput as o, BackgroundAgentManager as p, CustomCommandStore as q, formatBlockersOutput as r, searchFiles as rt, createGetFileStructureTool as s, createBlockerStore as t, mergeCommands as tt, createWriteTodosTool as u, parseAgentConfig as v, ServerLlmBackend as w, WebSocketConnectionManager as x, ApiClient as y, clearFeatureModuleTools as z };
@@ -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.4.0";
7
+ var version = "0.5.0";
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.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -118,11 +118,11 @@
118
118
  "tsx": "^4.21.0",
119
119
  "typescript": "^5.9.3",
120
120
  "vitest": "^4.1.2",
121
- "@bike4mind/agents": "0.5.2",
122
- "@bike4mind/common": "2.88.0",
123
- "@bike4mind/mcp": "1.35.11",
124
- "@bike4mind/services": "2.76.10",
125
- "@bike4mind/utils": "2.18.9"
121
+ "@bike4mind/agents": "0.5.3",
122
+ "@bike4mind/common": "2.89.0",
123
+ "@bike4mind/mcp": "1.36.0",
124
+ "@bike4mind/services": "2.77.0",
125
+ "@bike4mind/utils": "2.18.10"
126
126
  },
127
127
  "optionalDependencies": {
128
128
  "@vscode/ripgrep": "^1.17.1"