@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.
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +40 -3
- package/dist/{tools-DiSJh1Dv.mjs → tools-V5FJ83BJ.mjs} +310 -42
- package/dist/{updateChecker-DcJC1C8S.mjs → updateChecker-Boy2GLk4.mjs} +1 -1
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-
|
|
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 {
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
6695
|
-
const
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
console.
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
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
|
-
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "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.
|
|
122
|
-
"@bike4mind/common": "2.
|
|
123
|
-
"@bike4mind/mcp": "1.
|
|
124
|
-
"@bike4mind/services": "2.
|
|
125
|
-
"@bike4mind/utils": "2.18.
|
|
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"
|