@bike4mind/cli 0.2.8-cli-bash-tools.17156 → 0.2.8-feat-cli-context-file-loading.17149

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/README.md CHANGED
@@ -10,6 +10,7 @@ Interactive command-line interface for Bike4Mind with ReAct agents.
10
10
  - 🛠️ B4M tools + MCP integration
11
11
  - 🎨 Rich terminal UI with Ink
12
12
  - 🐛 Debug logging with `--verbose` flag
13
+ - 📄 Context file loading (CLAUDE.md, AGENTS.md, AI.md)
13
14
 
14
15
  ## Installation
15
16
 
@@ -160,7 +161,6 @@ export SERPER_API_KEY="your-key-here"
160
161
  - ✅ `math_evaluate` - No API key needed
161
162
  - ✅ `current_datetime` - No API key needed
162
163
  - ✅ `prompt_enhancement` - No API key needed
163
- - ✅ `bash_execute` - No API key needed
164
164
  - 🔑 `weather_info` - Requires `toolApiKeys.openweather`
165
165
  - 🔑 `web_search` - Requires `toolApiKeys.serper`
166
166
  - 🔑 `deep_research` - Requires `toolApiKeys.serper`
@@ -242,6 +242,51 @@ You can also run any MCP server via npx or custom executables:
242
242
 
243
243
  **Note:** Internal MCP servers must be built and available in the `b4m-core/packages/mcp/dist/src/` directory. The CLI will automatically find them if you're running from the monorepo. For Docker-based servers, ensure Docker is installed and the image is accessible.
244
244
 
245
+ ## Context Files
246
+
247
+ The CLI supports loading project-specific instructions from context files, similar to CLAUDE.md in Claude Code. These files provide persistent instructions that are automatically included in the agent's system prompt.
248
+
249
+ ### Supported Files
250
+
251
+ **Project-level** (in your project directory, checked in priority order):
252
+ 1. `CLAUDE.local.md` - Personal/gitignored instructions (highest priority)
253
+ 2. `CLAUDE.md` - Project instructions
254
+ 3. `AGENTS.md` - Cross-tool standard
255
+ 4. `AI.local.md` - Personal/gitignored instructions
256
+ 5. `AI.md` - Project instructions
257
+ 6. `INSTRUCTIONS.md` - Project instructions (lowest priority)
258
+
259
+ **Global** (in `~/.bike4mind/`):
260
+ 1. `AI.local.md` - Personal global instructions
261
+ 2. `AI.md` - Global instructions
262
+
263
+ ### How It Works
264
+
265
+ - The CLI loads the **first matching file** from each layer (global and project)
266
+ - Project files take precedence over global files
267
+ - `.local.md` variants are intended to be gitignored for personal preferences
268
+ - Files must be under 100KB
269
+ - Symlinks are rejected for security
270
+
271
+ ### Example
272
+
273
+ Create a `CLAUDE.md` in your project:
274
+
275
+ ```markdown
276
+ # Project Instructions
277
+
278
+ - Always use TypeScript strict mode
279
+ - Prefer functional components with hooks
280
+ - Run tests before committing
281
+ ```
282
+
283
+ On startup, you'll see:
284
+ ```
285
+ 📄 Project context: CLAUDE.md
286
+ ```
287
+
288
+ The agent will follow these instructions for all interactions in that project.
289
+
245
290
  ## Session Storage
246
291
 
247
292
  Sessions are saved to `~/.bike4mind/sessions/`
@@ -339,6 +384,7 @@ Current implementation includes:
339
384
  ✅ B4M tools integration
340
385
  ✅ MCP tools support
341
386
  ✅ Debug logging with --verbose flag
387
+ ✅ Context file loading (CLAUDE.md, AGENTS.md, AI.md)
342
388
 
343
389
  Coming soon:
344
390
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-HD4QQGS6.js";
4
+ } from "./chunk-H6SHASMZ.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/artifactExtractor.js
@@ -6,12 +6,12 @@ import {
6
6
  getSettingsByNames,
7
7
  obfuscateApiKey,
8
8
  secureParameters
9
- } from "./chunk-SE2T5GED.js";
9
+ } from "./chunk-DNSBC3LO.js";
10
10
  import {
11
11
  ApiKeyType,
12
12
  MementoTier,
13
13
  isSupportedEmbeddingModel
14
- } from "./chunk-HD4QQGS6.js";
14
+ } from "./chunk-H6SHASMZ.js";
15
15
 
16
16
  // ../../b4m-core/packages/services/dist/src/apiKeyService/get.js
17
17
  import { z } from "zod";
@@ -16,7 +16,7 @@ import {
16
16
  dayjsConfig_default,
17
17
  extractSnippetMeta,
18
18
  settingsMap
19
- } from "./chunk-HD4QQGS6.js";
19
+ } from "./chunk-H6SHASMZ.js";
20
20
 
21
21
  // ../../b4m-core/packages/utils/dist/src/storage/S3Storage.js
22
22
  import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand } from "@aws-sdk/client-s3";
@@ -230,9 +230,7 @@ var b4mLLMTools = z3.enum([
230
230
  "create_file",
231
231
  "glob_files",
232
232
  "grep_search",
233
- "delete_file",
234
- // Shell execution
235
- "bash_execute"
233
+ "delete_file"
236
234
  ]);
237
235
  var B4MLLMToolsList = b4mLLMTools.options.map((tool) => tool);
238
236
  var RechartsChartTypeSchema = z3.enum([
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BadRequestError,
4
4
  secureParameters
5
- } from "./chunk-SE2T5GED.js";
5
+ } from "./chunk-DNSBC3LO.js";
6
6
  import {
7
7
  GenericCreditDeductTransaction,
8
8
  ImageEditUsageTransaction,
@@ -10,7 +10,7 @@ import {
10
10
  RealtimeVoiceUsageTransaction,
11
11
  TextGenerationUsageTransaction,
12
12
  TransferCreditTransaction
13
- } from "./chunk-HD4QQGS6.js";
13
+ } from "./chunk-H6SHASMZ.js";
14
14
 
15
15
  // ../../b4m-core/packages/services/dist/src/creditService/subtractCredits.js
16
16
  import { z } from "zod";
@@ -7,11 +7,11 @@ import {
7
7
  getSettingsMap,
8
8
  getSettingsValue,
9
9
  secureParameters
10
- } from "./chunk-SE2T5GED.js";
10
+ } from "./chunk-DNSBC3LO.js";
11
11
  import {
12
12
  KnowledgeType,
13
13
  SupportedFabFileMimeTypes
14
- } from "./chunk-HD4QQGS6.js";
14
+ } from "./chunk-H6SHASMZ.js";
15
15
 
16
16
  // ../../b4m-core/packages/services/dist/src/fabFileService/create.js
17
17
  import { z } from "zod";
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  createFabFile,
4
4
  createFabFileSchema
5
- } from "./chunk-RJYG7BUV.js";
6
- import "./chunk-SE2T5GED.js";
5
+ } from "./chunk-YXHQN7SK.js";
6
+ import "./chunk-DNSBC3LO.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-HD4QQGS6.js";
8
+ import "./chunk-H6SHASMZ.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  createFabFile,
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-6S7HV2GF.js";
8
- import "./chunk-F2K7OCDJ.js";
9
- import "./chunk-RJYG7BUV.js";
7
+ } from "./chunk-3CCBDIFR.js";
8
+ import "./chunk-LIRY24M5.js";
9
+ import "./chunk-YXHQN7SK.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-SE2T5GED.js";
18
+ } from "./chunk-DNSBC3LO.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-AMDXHL6S.js";
@@ -73,7 +73,7 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-HD4QQGS6.js";
76
+ } from "./chunk-H6SHASMZ.js";
77
77
  import {
78
78
  __require
79
79
  } from "./chunk-PDX44BCA.js";
@@ -85,7 +85,7 @@ import { v4 as uuidv49 } from "uuid";
85
85
 
86
86
  // src/components/App.tsx
87
87
  import React10 from "react";
88
- import { Box as Box9, Text as Text9 } from "ink";
88
+ import { Box as Box9, Text as Text9, Static } from "ink";
89
89
 
90
90
  // src/components/StatusBar.tsx
91
91
  import React from "react";
@@ -742,9 +742,11 @@ function InputPrompt({
742
742
  const [selectedIndex, setSelectedIndex] = useState2(0);
743
743
  const [historyIndex, setHistoryIndex] = useState2(-1);
744
744
  const [tempInput, setTempInput] = useState2("");
745
+ const [inputKey, setInputKey] = useState2(0);
745
746
  useEffect(() => {
746
747
  if (prefillInput) {
747
748
  setValue(prefillInput);
749
+ setInputKey((prev) => prev + 1);
748
750
  onPrefillConsumed?.();
749
751
  }
750
752
  }, [prefillInput, onPrefillConsumed]);
@@ -889,10 +891,11 @@ function InputPrompt({
889
891
  return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(
890
892
  CustomTextInput,
891
893
  {
894
+ key: inputKey,
892
895
  value,
893
896
  onChange: handleChange,
894
897
  onSubmit: handleSubmit,
895
- placeholder: disabled ? "" : "Type your message, /help for commands, or @file to reference",
898
+ placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
896
899
  showCursor: !disabled
897
900
  }
898
901
  )), shouldShowCommandAutocomplete && /* @__PURE__ */ React5.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React5.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
@@ -923,7 +926,6 @@ var useCliStore = create((set) => ({
923
926
  addAgentStep: (step) => set((state) => ({
924
927
  agentSteps: [...state.agentSteps, step]
925
928
  })),
926
- setAgentSteps: (steps) => set({ agentSteps: steps }),
927
929
  clearAgentSteps: () => set({ agentSteps: [] }),
928
930
  // Permission prompt
929
931
  permissionPrompt: null,
@@ -943,7 +945,7 @@ var AgentThinking = React7.memo(function AgentThinking2() {
943
945
  if (!isThinking) {
944
946
  return null;
945
947
  }
946
- return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
948
+ return /* @__PURE__ */ React7.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { steps: agentSteps }));
947
949
  });
948
950
 
949
951
  // src/components/PermissionPrompt.tsx
@@ -1398,7 +1400,7 @@ ${errorBlock}`;
1398
1400
  canBeTrusted: permissionPrompt.canBeTrusted,
1399
1401
  onResponse: onPermissionResponse
1400
1402
  }
1401
- )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column" }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: `${message.timestamp}-${message.role}-${index}`, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message })))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1403
+ )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React10.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React10.createElement(MessageItem, { message }))), /* @__PURE__ */ React10.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React10.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React10.createElement(
1402
1404
  InputPrompt,
1403
1405
  {
1404
1406
  onSubmit: handleSubmit,
@@ -1411,8 +1413,7 @@ ${errorBlock}`;
1411
1413
  ))));
1412
1414
  }
1413
1415
  function MessageItem({ message }) {
1414
- const isUser = message.role === "user";
1415
- return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: isUser ? "cyan" : "green" }, isUser ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1416
+ return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
1416
1417
  if (step.type === "thought") {
1417
1418
  return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
1418
1419
  }
@@ -1424,7 +1425,7 @@ function MessageItem({ message }) {
1424
1425
  return /* @__PURE__ */ React10.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
1425
1426
  }
1426
1427
  return null;
1427
- }).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React10.createElement(Text9, null, message.content)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1428
+ }).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
1428
1429
  }
1429
1430
 
1430
1431
  // src/components/MessageList.tsx
@@ -2627,9 +2628,10 @@ ${options.context}` : this.getSystemPrompt()
2627
2628
  content: query
2628
2629
  }
2629
2630
  ];
2631
+ console.log("[DEBUG ReActAgent] Messages array being sent to LLM:");
2630
2632
  messages.forEach((msg, i) => {
2631
2633
  const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
2632
- this.context.logger.debug(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2634
+ console.log(` [${i}] ${msg.role}: ${content.substring(0, 150)}...`);
2633
2635
  });
2634
2636
  let finalAnswer = "";
2635
2637
  let reachedMaxIterations = false;
@@ -8648,381 +8650,6 @@ var deleteFileTool = {
8648
8650
  })
8649
8651
  };
8650
8652
 
8651
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
8652
- import { spawn } from "child_process";
8653
- import path11 from "path";
8654
- var DEFAULT_TIMEOUT_MS = 6e4;
8655
- var MAX_OUTPUT_SIZE = 100 * 1024;
8656
- var DANGEROUS_PATTERNS = [
8657
- // Destructive file operations
8658
- {
8659
- pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
8660
- reason: "Recursive delete with path",
8661
- block: true
8662
- },
8663
- {
8664
- pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
8665
- reason: "Force delete without preserve root",
8666
- block: true
8667
- },
8668
- {
8669
- pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
8670
- reason: "Recursive force delete on root paths",
8671
- block: true
8672
- },
8673
- {
8674
- pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
8675
- reason: "Force recursive delete on root paths",
8676
- block: true
8677
- },
8678
- // System-level dangerous commands
8679
- { pattern: /\bsudo\b/i, reason: "Elevated privileges (sudo)", block: true },
8680
- { pattern: /\bsu\s+(-|root)/i, reason: "Switch to root user", block: true },
8681
- { pattern: /\bchmod\s+777\b/i, reason: "Overly permissive chmod", block: false },
8682
- { pattern: /\bchown\s+-R\s+root/i, reason: "Recursive chown to root", block: true },
8683
- // Disk/partition operations
8684
- { pattern: /\bmkfs\b/i, reason: "Filesystem creation", block: true },
8685
- { pattern: /\bfdisk\b/i, reason: "Disk partitioning", block: true },
8686
- { pattern: /\bdd\s+.*of=\/dev\//i, reason: "Direct disk write", block: true },
8687
- // Network attacks
8688
- { pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i, reason: "Reverse shell attempt", block: true },
8689
- { pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8690
- { pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Piping remote script to shell", block: true },
8691
- // Fork bombs and resource exhaustion
8692
- { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i, reason: "Fork bomb", block: true },
8693
- { pattern: /\bwhile\s+true.*do.*done.*&/i, reason: "Infinite loop in background", block: false },
8694
- // Credential/sensitive data access
8695
- { pattern: /\/etc\/shadow/i, reason: "Access to shadow file", block: true },
8696
- { pattern: /\/etc\/passwd.*>/i, reason: "Modifying passwd file", block: true },
8697
- { pattern: /\baws\s+.*--profile\s+/i, reason: "AWS profile access", block: false },
8698
- // History/log manipulation
8699
- { pattern: /\bhistory\s+-c\b/i, reason: "Clearing shell history", block: false },
8700
- { pattern: />\s*\/var\/log\//i, reason: "Overwriting system logs", block: true },
8701
- // Dangerous redirects
8702
- { pattern: />\s*\/dev\/sda/i, reason: "Writing to block device", block: true },
8703
- { pattern: />\s*\/dev\/null.*2>&1.*</i, reason: "Potentially hiding output", block: false },
8704
- // Environment manipulation
8705
- { pattern: /\bexport\s+PATH\s*=\s*[^$]/i, reason: "Overwriting PATH", block: false },
8706
- { pattern: /\bexport\s+LD_PRELOAD/i, reason: "LD_PRELOAD manipulation", block: true },
8707
- // Process/system control
8708
- { pattern: /\bkill\s+-9\s+(-1|1)\b/i, reason: "Killing all processes", block: true },
8709
- { pattern: /\bkillall\s+-9\b/i, reason: "Force killing processes", block: false },
8710
- { pattern: /\bshutdown\b/i, reason: "System shutdown", block: true },
8711
- { pattern: /\breboot\b/i, reason: "System reboot", block: true },
8712
- { pattern: /\binit\s+[06]\b/i, reason: "System runlevel change", block: true }
8713
- ];
8714
- var SAFE_COMMAND_PREFIXES = [
8715
- "ls",
8716
- "cat",
8717
- "head",
8718
- "tail",
8719
- "grep",
8720
- "find",
8721
- "echo",
8722
- "pwd",
8723
- "whoami",
8724
- "date",
8725
- "cal",
8726
- "wc",
8727
- "sort",
8728
- "uniq",
8729
- "cut",
8730
- "tr",
8731
- "sed",
8732
- "awk",
8733
- "git",
8734
- "npm",
8735
- "pnpm",
8736
- "yarn",
8737
- "node",
8738
- "npx",
8739
- "tsx",
8740
- "ts-node",
8741
- "python",
8742
- "python3",
8743
- "pip",
8744
- "pip3",
8745
- "docker",
8746
- "docker-compose",
8747
- "curl",
8748
- "wget",
8749
- // These are safe for fetching, blocked when piped to shell
8750
- "mkdir",
8751
- "touch",
8752
- "cp",
8753
- "mv",
8754
- // File operations (still require permission)
8755
- "which",
8756
- "whereis",
8757
- "type",
8758
- "file",
8759
- "stat",
8760
- "env",
8761
- "printenv",
8762
- "set",
8763
- "man",
8764
- "help",
8765
- "info",
8766
- "diff",
8767
- "comm",
8768
- "cmp",
8769
- "tar",
8770
- "zip",
8771
- "unzip",
8772
- "gzip",
8773
- "gunzip",
8774
- "ssh",
8775
- "scp",
8776
- "rsync",
8777
- // Network tools
8778
- "make",
8779
- "cmake",
8780
- "cargo",
8781
- "go",
8782
- "rustc",
8783
- "gcc",
8784
- "g++",
8785
- // Build tools
8786
- "jest",
8787
- "vitest",
8788
- "mocha",
8789
- "pytest",
8790
- // Test runners
8791
- "eslint",
8792
- "prettier",
8793
- "tsc"
8794
- // Linters/formatters
8795
- ];
8796
- function checkDangerousPatterns(command) {
8797
- for (const { pattern, reason, block } of DANGEROUS_PATTERNS) {
8798
- if (pattern.test(command)) {
8799
- return { blocked: block, reason };
8800
- }
8801
- }
8802
- return { blocked: false };
8803
- }
8804
- function getBaseCommand(command) {
8805
- const trimmed = command.trim();
8806
- const match = trimmed.match(/^(\S+)/);
8807
- return match ? match[1] : "";
8808
- }
8809
- function isSafeCommandPrefix(command) {
8810
- const baseCommand = getBaseCommand(command);
8811
- return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
8812
- }
8813
- async function executeBashCommand(params) {
8814
- const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
8815
- if (!command || command.trim().length === 0) {
8816
- return {
8817
- stdout: "",
8818
- stderr: "Error: Command cannot be empty",
8819
- exitCode: 1,
8820
- timedOut: false,
8821
- blocked: true,
8822
- blockedReason: "Empty command"
8823
- };
8824
- }
8825
- const dangerCheck = checkDangerousPatterns(command);
8826
- if (dangerCheck.blocked) {
8827
- return {
8828
- stdout: "",
8829
- stderr: `Command blocked for safety: ${dangerCheck.reason}`,
8830
- exitCode: 1,
8831
- timedOut: false,
8832
- blocked: true,
8833
- blockedReason: dangerCheck.reason
8834
- };
8835
- }
8836
- const baseCwd = process.cwd();
8837
- const targetCwd = relativeCwd ? path11.resolve(baseCwd, relativeCwd) : baseCwd;
8838
- const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
8839
- return new Promise((resolve3) => {
8840
- let stdout = "";
8841
- let stderr = "";
8842
- let timedOut = false;
8843
- const proc = spawn("bash", ["-c", command], {
8844
- cwd: targetCwd,
8845
- env: {
8846
- ...process.env,
8847
- // Prevent color codes that might be hard to read
8848
- NO_COLOR: "1",
8849
- FORCE_COLOR: "0"
8850
- },
8851
- stdio: ["ignore", "pipe", "pipe"]
8852
- });
8853
- const timeoutId = setTimeout(() => {
8854
- timedOut = true;
8855
- proc.kill("SIGTERM");
8856
- setTimeout(() => {
8857
- if (!proc.killed) {
8858
- proc.kill("SIGKILL");
8859
- }
8860
- }, 5e3);
8861
- }, effectiveTimeout);
8862
- proc.stdout.on("data", (data) => {
8863
- if (stdout.length < MAX_OUTPUT_SIZE) {
8864
- stdout += data.toString();
8865
- if (stdout.length > MAX_OUTPUT_SIZE) {
8866
- stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8867
- }
8868
- }
8869
- });
8870
- proc.stderr.on("data", (data) => {
8871
- if (stderr.length < MAX_OUTPUT_SIZE) {
8872
- stderr += data.toString();
8873
- if (stderr.length > MAX_OUTPUT_SIZE) {
8874
- stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
8875
- }
8876
- }
8877
- });
8878
- proc.on("close", (exitCode) => {
8879
- clearTimeout(timeoutId);
8880
- resolve3({
8881
- stdout: stdout.trim(),
8882
- stderr: stderr.trim(),
8883
- exitCode,
8884
- timedOut,
8885
- blocked: false
8886
- });
8887
- });
8888
- proc.on("error", (error) => {
8889
- clearTimeout(timeoutId);
8890
- resolve3({
8891
- stdout: "",
8892
- stderr: `Failed to execute command: ${error.message}`,
8893
- exitCode: 1,
8894
- timedOut: false,
8895
- blocked: false
8896
- });
8897
- });
8898
- });
8899
- }
8900
- function formatResult(result, command) {
8901
- const parts = [];
8902
- parts.push(`$ ${command}`);
8903
- parts.push("");
8904
- if (result.blocked) {
8905
- parts.push(`BLOCKED: ${result.blockedReason}`);
8906
- parts.push("");
8907
- parts.push("This command was blocked for safety reasons.");
8908
- parts.push("If you believe this is a false positive, please run the command manually.");
8909
- return parts.join("\n");
8910
- }
8911
- if (result.timedOut) {
8912
- parts.push("WARNING: Command timed out and was terminated.");
8913
- parts.push("");
8914
- }
8915
- if (result.stdout) {
8916
- parts.push(result.stdout);
8917
- }
8918
- if (result.stderr) {
8919
- if (result.stdout) {
8920
- parts.push("");
8921
- }
8922
- parts.push("STDERR:");
8923
- parts.push(result.stderr);
8924
- }
8925
- if (result.exitCode !== 0 && result.exitCode !== null) {
8926
- parts.push("");
8927
- parts.push(`Exit code: ${result.exitCode}`);
8928
- }
8929
- if (!result.stdout && !result.stderr && !result.timedOut) {
8930
- parts.push("(command completed with no output)");
8931
- }
8932
- return parts.join("\n");
8933
- }
8934
- var bashExecuteTool = {
8935
- name: "bash_execute",
8936
- implementation: (context) => ({
8937
- toolFn: async (value) => {
8938
- const params = value;
8939
- const isSafe = isSafeCommandPrefix(params.command);
8940
- context.logger.info("Bash: Executing command", {
8941
- command: params.command,
8942
- cwd: params.cwd || ".",
8943
- timeout: params.timeout || DEFAULT_TIMEOUT_MS,
8944
- isSafeCommand: isSafe
8945
- });
8946
- if (context.onStart) {
8947
- await context.onStart("bash_execute", {
8948
- command: params.command,
8949
- cwd: params.cwd
8950
- });
8951
- }
8952
- try {
8953
- const result = await executeBashCommand(params);
8954
- const formattedResult = formatResult(result, params.command);
8955
- context.logger.info("Bash: Command completed", {
8956
- exitCode: result.exitCode,
8957
- timedOut: result.timedOut,
8958
- blocked: result.blocked
8959
- });
8960
- if (context.onFinish) {
8961
- await context.onFinish("bash_execute", {
8962
- command: params.command,
8963
- exitCode: result.exitCode,
8964
- blocked: result.blocked
8965
- });
8966
- }
8967
- return formattedResult;
8968
- } catch (error) {
8969
- context.logger.error("Bash: Command failed", error);
8970
- if (context.onFinish) {
8971
- await context.onFinish("bash_execute", {
8972
- command: params.command,
8973
- error: error instanceof Error ? error.message : "Unknown error"
8974
- });
8975
- }
8976
- throw error;
8977
- }
8978
- },
8979
- toolSchema: {
8980
- name: "bash_execute",
8981
- description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
8982
-
8983
- SAFETY NOTES:
8984
- - Commands are executed in the current working directory (or specified cwd)
8985
- - Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
8986
- - Commands have a default timeout of 60 seconds (max 5 minutes)
8987
- - Output is limited to prevent overwhelming responses
8988
- - This tool ALWAYS requires user permission before execution
8989
-
8990
- COMMON USE CASES:
8991
- - Running build commands: npm run build, make, cargo build
8992
- - Git operations: git status, git log, git diff
8993
- - Viewing system info: ls, pwd, cat, head, tail
8994
- - Running tests: npm test, pytest, cargo test
8995
- - Package management: npm install, pip install
8996
- - File operations: mkdir, cp, mv (with permission)
8997
-
8998
- BLOCKED OPERATIONS:
8999
- - sudo and privilege escalation
9000
- - Recursive deletes on system paths
9001
- - Direct disk operations
9002
- - Fork bombs and resource exhaustion
9003
- - Piping remote scripts to shell`,
9004
- parameters: {
9005
- type: "object",
9006
- properties: {
9007
- command: {
9008
- type: "string",
9009
- description: "The bash command to execute. Can include pipes, redirects, and chained commands."
9010
- },
9011
- cwd: {
9012
- type: "string",
9013
- description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
9014
- },
9015
- timeout: {
9016
- type: "number",
9017
- description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
9018
- }
9019
- },
9020
- required: ["command"]
9021
- }
9022
- }
9023
- })
9024
- };
9025
-
9026
8653
  // ../../b4m-core/packages/services/dist/src/llm/tools/index.js
9027
8654
  var tools = {
9028
8655
  dice_roll: diceRollTool,
@@ -9050,9 +8677,7 @@ var tools = {
9050
8677
  create_file: createFileTool,
9051
8678
  glob_files: globFilesTool,
9052
8679
  grep_search: grepSearchTool,
9053
- delete_file: deleteFileTool,
9054
- // Shell execution
9055
- bash_execute: bashExecuteTool
8680
+ delete_file: deleteFileTool
9056
8681
  };
9057
8682
  var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
9058
8683
  const context = {
@@ -9327,7 +8952,7 @@ async function generateFileDeletePreview(args) {
9327
8952
  if (!existsSync7(args.path)) {
9328
8953
  return `[File does not exist: ${args.path}]`;
9329
8954
  }
9330
- const stats = await import("fs/promises").then((fs11) => fs11.stat(args.path));
8955
+ const stats = await import("fs/promises").then((fs12) => fs12.stat(args.path));
9331
8956
  return `[File will be deleted]
9332
8957
 
9333
8958
  Path: ${args.path}
@@ -9340,7 +8965,7 @@ Last modified: ${stats.mtime.toLocaleString()}`;
9340
8965
 
9341
8966
  // src/utils/Logger.ts
9342
8967
  import fs10 from "fs/promises";
9343
- import path12 from "path";
8968
+ import path11 from "path";
9344
8969
  import os from "os";
9345
8970
  var Logger2 = class _Logger {
9346
8971
  constructor() {
@@ -9363,9 +8988,9 @@ var Logger2 = class _Logger {
9363
8988
  */
9364
8989
  async initialize(sessionId) {
9365
8990
  this.sessionId = sessionId;
9366
- const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
8991
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9367
8992
  await fs10.mkdir(debugDir, { recursive: true });
9368
- this.logFilePath = path12.join(debugDir, `${sessionId}.txt`);
8993
+ this.logFilePath = path11.join(debugDir, `${sessionId}.txt`);
9369
8994
  await this.writeToFile("INFO", "=== CLI SESSION START ===");
9370
8995
  }
9371
8996
  /**
@@ -9575,12 +9200,12 @@ var Logger2 = class _Logger {
9575
9200
  async cleanupOldLogs() {
9576
9201
  if (!this.fileLoggingEnabled) return;
9577
9202
  try {
9578
- const debugDir = path12.join(os.homedir(), ".bike4mind", "debug");
9203
+ const debugDir = path11.join(os.homedir(), ".bike4mind", "debug");
9579
9204
  const files = await fs10.readdir(debugDir);
9580
9205
  const now = Date.now();
9581
9206
  const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
9582
9207
  for (const file of files) {
9583
- const filePath = path12.join(debugDir, file);
9208
+ const filePath = path11.join(debugDir, file);
9584
9209
  const stats = await fs10.stat(filePath);
9585
9210
  if (stats.mtime.getTime() < thirtyDaysAgo) {
9586
9211
  await fs10.unlink(filePath);
@@ -9645,8 +9270,7 @@ var LOCAL_TOOLS = [
9645
9270
  "edit_file",
9646
9271
  "dice_roll",
9647
9272
  "math_evaluate",
9648
- "current_datetime",
9649
- "bash_execute"
9273
+ "current_datetime"
9650
9274
  ];
9651
9275
  function isServerTool(toolName) {
9652
9276
  return SERVER_TOOLS.includes(toolName);
@@ -9748,10 +9372,6 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
9748
9372
  } catch (error) {
9749
9373
  preview = `[Could not generate preview: ${error instanceof Error ? error.message : "Unknown error"}]`;
9750
9374
  }
9751
- } else if (toolName === "bash_execute" && args?.command) {
9752
- const cwd = args.cwd ? ` (in ${args.cwd})` : "";
9753
- const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
9754
- preview = `$ ${args.command}${cwd}${timeout}`;
9755
9375
  }
9756
9376
  const response = await showPermissionPrompt(toolName, args, preview);
9757
9377
  if (response.action === "deny") {
@@ -9822,8 +9442,6 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
9822
9442
  glob_files: {},
9823
9443
  grep_search: {},
9824
9444
  delete_file: {},
9825
- // Shell execution (always requires permission)
9826
- bash_execute: {},
9827
9445
  // Server-side tools (executed via /api/ai/v1/tools Lambda)
9828
9446
  // Routed by ToolRouter to use B4M company API keys
9829
9447
  weather_info: {},
@@ -9889,7 +9507,6 @@ var DEFAULT_TOOL_CATEGORIES = {
9889
9507
  create_file: "prompt_always",
9890
9508
  delete_file: "prompt_always",
9891
9509
  shell_execute: "prompt_always",
9892
- bash_execute: "prompt_always",
9893
9510
  git_commit: "prompt_always",
9894
9511
  git_push: "prompt_always",
9895
9512
  // ===== PROMPT DEFAULT (Repository read tools) =====
@@ -10051,6 +9668,118 @@ function getEnvironmentName(configApiConfig) {
10051
9668
  return "Bike4Mind";
10052
9669
  }
10053
9670
 
9671
+ // src/utils/contextLoader.ts
9672
+ import * as fs11 from "fs";
9673
+ import * as path12 from "path";
9674
+ import { homedir as homedir4 } from "os";
9675
+ var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
9676
+ var PROJECT_CONTEXT_FILES = [
9677
+ "CLAUDE.local.md",
9678
+ // gitignored, highest priority
9679
+ "CLAUDE.md",
9680
+ "AGENTS.md",
9681
+ // cross-tool standard
9682
+ "AI.local.md",
9683
+ // gitignored
9684
+ "AI.md",
9685
+ "INSTRUCTIONS.md"
9686
+ ];
9687
+ var GLOBAL_CONTEXT_FILES = ["AI.local.md", "AI.md"];
9688
+ function formatFileSize2(bytes) {
9689
+ if (bytes < 1024) return `${bytes}B`;
9690
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
9691
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
9692
+ }
9693
+ function tryReadContextFile(dir, filename, source) {
9694
+ const filePath = path12.join(dir, filename);
9695
+ try {
9696
+ const stats = fs11.lstatSync(filePath);
9697
+ if (stats.isDirectory()) {
9698
+ return null;
9699
+ }
9700
+ if (stats.isSymbolicLink()) {
9701
+ return {
9702
+ error: `${source === "global" ? "Global" : "Project"} ${filename} is a symlink (not allowed for security)`
9703
+ };
9704
+ }
9705
+ if (stats.size > CONTEXT_FILE_SIZE_LIMIT) {
9706
+ return {
9707
+ error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
9708
+ };
9709
+ }
9710
+ const content = fs11.readFileSync(filePath, "utf-8");
9711
+ return {
9712
+ filename,
9713
+ content,
9714
+ source,
9715
+ path: filePath
9716
+ };
9717
+ } catch (err) {
9718
+ if (err.code === "ENOENT") {
9719
+ return null;
9720
+ }
9721
+ if (err.code === "EACCES") {
9722
+ return {
9723
+ error: `Cannot read ${source} ${filename}: permission denied`
9724
+ };
9725
+ }
9726
+ return {
9727
+ error: `Cannot read ${source} ${filename}: ${err instanceof Error ? err.message : "Unknown error"}`
9728
+ };
9729
+ }
9730
+ }
9731
+ function findContextFile(dir, candidates, source) {
9732
+ for (const filename of candidates) {
9733
+ const result = tryReadContextFile(dir, filename, source);
9734
+ if (result === null) {
9735
+ continue;
9736
+ }
9737
+ if ("error" in result) {
9738
+ return { result: null, error: result.error };
9739
+ }
9740
+ return { result, error: null };
9741
+ }
9742
+ return { result: null, error: null };
9743
+ }
9744
+ function mergeContextContent(global, project) {
9745
+ if (global && project) {
9746
+ return `${global.content}
9747
+
9748
+ ---
9749
+
9750
+ ${project.content}`;
9751
+ }
9752
+ if (global) {
9753
+ return global.content;
9754
+ }
9755
+ if (project) {
9756
+ return project.content;
9757
+ }
9758
+ return "";
9759
+ }
9760
+ async function loadContextFiles(projectDir) {
9761
+ const errors = [];
9762
+ const globalDir = path12.join(homedir4(), ".bike4mind");
9763
+ const projectDirectory = projectDir || process.cwd();
9764
+ const [globalResult, projectResult] = await Promise.all([
9765
+ Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
9766
+ Promise.resolve(findContextFile(projectDirectory, PROJECT_CONTEXT_FILES, "project"))
9767
+ ]);
9768
+ if (globalResult.error) {
9769
+ errors.push(globalResult.error);
9770
+ }
9771
+ if (projectResult.error) {
9772
+ errors.push(projectResult.error);
9773
+ }
9774
+ const mergedContent = mergeContextContent(globalResult.result, projectResult.result);
9775
+ return {
9776
+ globalContext: globalResult.result,
9777
+ projectContext: projectResult.result,
9778
+ mergedContent,
9779
+ errors
9780
+ };
9781
+ }
9782
+
10054
9783
  // ../../b4m-core/packages/mcp/dist/src/client.js
10055
9784
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
10056
9785
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
@@ -10908,7 +10637,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
10908
10637
  // package.json
10909
10638
  var package_default = {
10910
10639
  name: "@bike4mind/cli",
10911
- version: "0.2.8-cli-bash-tools.17156+ced3f15ab",
10640
+ version: "0.2.8-feat-cli-context-file-loading.17149+1a918591e",
10912
10641
  type: "module",
10913
10642
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
10914
10643
  license: "UNLICENSED",
@@ -11011,10 +10740,10 @@ var package_default = {
11011
10740
  },
11012
10741
  devDependencies: {
11013
10742
  "@bike4mind/agents": "0.1.0",
11014
- "@bike4mind/common": "2.38.2-cli-bash-tools.17156+ced3f15ab",
11015
- "@bike4mind/mcp": "1.20.2-cli-bash-tools.17156+ced3f15ab",
11016
- "@bike4mind/services": "2.33.2-cli-bash-tools.17156+ced3f15ab",
11017
- "@bike4mind/utils": "2.1.3-cli-bash-tools.17156+ced3f15ab",
10743
+ "@bike4mind/common": "2.38.2-feat-cli-context-file-loading.17149+1a918591e",
10744
+ "@bike4mind/mcp": "1.20.2-feat-cli-context-file-loading.17149+1a918591e",
10745
+ "@bike4mind/services": "2.33.2-feat-cli-context-file-loading.17149+1a918591e",
10746
+ "@bike4mind/utils": "2.1.3-feat-cli-context-file-loading.17149+1a918591e",
11018
10747
  "@types/better-sqlite3": "^7.6.12",
11019
10748
  "@types/diff": "^5.0.9",
11020
10749
  "@types/jsonwebtoken": "^9.0.4",
@@ -11027,7 +10756,7 @@ var package_default = {
11027
10756
  typescript: "^5.9.3",
11028
10757
  vitest: "^3.2.4"
11029
10758
  },
11030
- gitHead: "ced3f15ab25eb7967bc31832769ab00be9965ff3"
10759
+ gitHead: "1a918591e2031dfd0197eab12dccf8e5a2db737e"
11031
10760
  };
11032
10761
 
11033
10762
  // src/config/constants.ts
@@ -11278,6 +11007,24 @@ function CliApp() {
11278
11007
  }
11279
11008
  const allTools = [...b4mTools, ...mcpTools];
11280
11009
  console.log(`\u2705 Total: ${allTools.length} tool(s) available`);
11010
+ const projectDir = state.configStore.getProjectConfigDir();
11011
+ const contextResult = await loadContextFiles(projectDir);
11012
+ if (contextResult.globalContext) {
11013
+ console.log(`\u{1F4C4} Global context: ${contextResult.globalContext.filename}`);
11014
+ }
11015
+ if (contextResult.projectContext) {
11016
+ console.log(`\u{1F4C4} Project context: ${contextResult.projectContext.filename}`);
11017
+ }
11018
+ for (const error of contextResult.errors) {
11019
+ console.log(`\u26A0\uFE0F Context file error: ${error}`);
11020
+ }
11021
+ const contextSection = contextResult.mergedContent ? `
11022
+
11023
+ ## Project Context
11024
+
11025
+ Follow these project-specific instructions:
11026
+
11027
+ ${contextResult.mergedContent}` : "";
11281
11028
  const cliSystemPrompt = `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
11282
11029
 
11283
11030
  CORE BEHAVIOR:
@@ -11307,7 +11054,7 @@ EXAMPLES:
11307
11054
  - "enhance README" \u2192 file_read \u2192 generate \u2192 create_file
11308
11055
  - "what packages installed?" \u2192 glob_files "**/package.json" \u2192 file_read
11309
11056
 
11310
- Remember: Use context from previous messages to understand follow-up questions.`;
11057
+ Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
11311
11058
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
11312
11059
  const agent = new ReActAgent({
11313
11060
  userId: config.userId,
@@ -11362,29 +11109,6 @@ Remember: Use context from previous messages to understand follow-up questions.`
11362
11109
  console.log("\u{1F4A1} Run /login to authenticate with your B4M account.\n");
11363
11110
  return;
11364
11111
  }
11365
- const currentSteps = [];
11366
- const stepHandler = (step) => {
11367
- currentSteps.push(step);
11368
- const currentSession = useCliStore.getState().session;
11369
- if (currentSession) {
11370
- const messages = [...currentSession.messages];
11371
- const lastMessage = messages[messages.length - 1];
11372
- if (lastMessage && lastMessage.role === "assistant") {
11373
- messages[messages.length - 1] = {
11374
- ...lastMessage,
11375
- metadata: {
11376
- ...lastMessage.metadata,
11377
- steps: [...currentSteps]
11378
- }
11379
- };
11380
- setStoreSession({
11381
- ...currentSession,
11382
- messages
11383
- });
11384
- }
11385
- }
11386
- };
11387
- state.agent.on("action", stepHandler);
11388
11112
  try {
11389
11113
  let messageContent = message;
11390
11114
  let userMessageContent = message;
@@ -11398,21 +11122,6 @@ Remember: Use context from previous messages to understand follow-up questions.`
11398
11122
  content: userMessageContent,
11399
11123
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
11400
11124
  };
11401
- const pendingAssistantMessage = {
11402
- role: "assistant",
11403
- content: "...",
11404
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11405
- metadata: {
11406
- steps: []
11407
- }
11408
- };
11409
- const sessionWithMessages = {
11410
- ...state.session,
11411
- messages: [...state.session.messages, userMessage, pendingAssistantMessage],
11412
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11413
- };
11414
- setState((prev) => ({ ...prev, session: sessionWithMessages }));
11415
- setStoreSession(sessionWithMessages);
11416
11125
  const recentMessages = state.session.messages.slice(-20);
11417
11126
  const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
11418
11127
  role: msg.role,
@@ -11426,28 +11135,26 @@ Remember: Use context from previous messages to understand follow-up questions.`
11426
11135
  console.log("\n\u26A0\uFE0F Action denied by user\n");
11427
11136
  }
11428
11137
  const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
11429
- const currentSession = useCliStore.getState().session;
11430
- if (!currentSession) return;
11431
- const finalMessages = [...currentSession.messages];
11432
- const finalAssistantMessage = {
11138
+ const assistantMessage = {
11433
11139
  role: "assistant",
11434
11140
  content: result.finalAnswer,
11435
- timestamp: pendingAssistantMessage.timestamp,
11141
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11436
11142
  metadata: {
11437
11143
  tokenUsage: {
11438
11144
  prompt: 0,
11145
+ // TODO: Extract from result
11439
11146
  completion: 0,
11440
11147
  total: result.completionInfo.totalTokens
11441
11148
  },
11442
11149
  steps: result.steps,
11443
11150
  // Complete history: thoughts, actions, observations
11444
11151
  permissionDenied
11152
+ // Mark if this ended due to permission denial
11445
11153
  }
11446
11154
  };
11447
- finalMessages[finalMessages.length - 1] = finalAssistantMessage;
11448
11155
  const updatedSession = {
11449
- ...currentSession,
11450
- messages: finalMessages,
11156
+ ...state.session,
11157
+ messages: [...state.session.messages, userMessage, assistantMessage],
11451
11158
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11452
11159
  metadata: {
11453
11160
  ...state.session.metadata,
@@ -11467,8 +11174,6 @@ Remember: Use context from previous messages to understand follow-up questions.`
11467
11174
  }
11468
11175
  }
11469
11176
  console.error("Error processing message:", error);
11470
- } finally {
11471
- state.agent.off("action", stepHandler);
11472
11177
  }
11473
11178
  };
11474
11179
  const handleImageDetected = async (imageData) => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-HD4QQGS6.js";
4
+ } from "./chunk-H6SHASMZ.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/llmMarkdownGenerator.js
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CurationArtifactType
4
- } from "./chunk-HD4QQGS6.js";
4
+ } from "./chunk-H6SHASMZ.js";
5
5
  import "./chunk-PDX44BCA.js";
6
6
 
7
7
  // ../../b4m-core/packages/services/dist/src/notebookCurationService/markdownGenerator.js
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  findMostSimilarMemento,
4
4
  getRelevantMementos
5
- } from "./chunk-6S7HV2GF.js";
6
- import "./chunk-SE2T5GED.js";
5
+ } from "./chunk-3CCBDIFR.js";
6
+ import "./chunk-DNSBC3LO.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-HD4QQGS6.js";
8
+ import "./chunk-H6SHASMZ.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  findMostSimilarMemento,
@@ -120,7 +120,7 @@ import {
120
120
  validateMermaidSyntax,
121
121
  warmUpSettingsCache,
122
122
  withRetry
123
- } from "./chunk-SE2T5GED.js";
123
+ } from "./chunk-DNSBC3LO.js";
124
124
  import {
125
125
  Logger,
126
126
  NotificationDeduplicator,
@@ -129,7 +129,7 @@ import {
129
129
  postLowCreditsNotificationToSlack,
130
130
  postMessageToSlack
131
131
  } from "./chunk-AMDXHL6S.js";
132
- import "./chunk-HD4QQGS6.js";
132
+ import "./chunk-H6SHASMZ.js";
133
133
  import "./chunk-PDX44BCA.js";
134
134
  export {
135
135
  AWSBackend,
@@ -292,7 +292,7 @@ import {
292
292
  validateQuestMasterArtifactV2,
293
293
  validateReactArtifactV2,
294
294
  validateSvgArtifactV2
295
- } from "./chunk-HD4QQGS6.js";
295
+ } from "./chunk-H6SHASMZ.js";
296
296
  import "./chunk-PDX44BCA.js";
297
297
  export {
298
298
  ALL_IMAGE_MODELS,
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  SubtractCreditsSchema,
4
4
  subtractCredits
5
- } from "./chunk-F2K7OCDJ.js";
6
- import "./chunk-SE2T5GED.js";
5
+ } from "./chunk-LIRY24M5.js";
6
+ import "./chunk-DNSBC3LO.js";
7
7
  import "./chunk-AMDXHL6S.js";
8
- import "./chunk-HD4QQGS6.js";
8
+ import "./chunk-H6SHASMZ.js";
9
9
  import "./chunk-PDX44BCA.js";
10
10
  export {
11
11
  SubtractCreditsSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.8-cli-bash-tools.17156+ced3f15ab",
3
+ "version": "0.2.8-feat-cli-context-file-loading.17149+1a918591e",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -103,10 +103,10 @@
103
103
  },
104
104
  "devDependencies": {
105
105
  "@bike4mind/agents": "0.1.0",
106
- "@bike4mind/common": "2.38.2-cli-bash-tools.17156+ced3f15ab",
107
- "@bike4mind/mcp": "1.20.2-cli-bash-tools.17156+ced3f15ab",
108
- "@bike4mind/services": "2.33.2-cli-bash-tools.17156+ced3f15ab",
109
- "@bike4mind/utils": "2.1.3-cli-bash-tools.17156+ced3f15ab",
106
+ "@bike4mind/common": "2.38.2-feat-cli-context-file-loading.17149+1a918591e",
107
+ "@bike4mind/mcp": "1.20.2-feat-cli-context-file-loading.17149+1a918591e",
108
+ "@bike4mind/services": "2.33.2-feat-cli-context-file-loading.17149+1a918591e",
109
+ "@bike4mind/utils": "2.1.3-feat-cli-context-file-loading.17149+1a918591e",
110
110
  "@types/better-sqlite3": "^7.6.12",
111
111
  "@types/diff": "^5.0.9",
112
112
  "@types/jsonwebtoken": "^9.0.4",
@@ -119,5 +119,5 @@
119
119
  "typescript": "^5.9.3",
120
120
  "vitest": "^3.2.4"
121
121
  },
122
- "gitHead": "ced3f15ab25eb7967bc31832769ab00be9965ff3"
122
+ "gitHead": "1a918591e2031dfd0197eab12dccf8e5a2db737e"
123
123
  }