@bike4mind/cli 0.2.31-update-default-agent-models.19589 → 0.2.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import "./chunk-GQGOWACU.js";
3
- import "./chunk-4P27WV34.js";
4
- import "./chunk-S6AUIWWB.js";
3
+ import "./chunk-C7LKV7P2.js";
4
+ import "./chunk-4R42FAEH.js";
5
5
  import "./chunk-BPFEGDC7.js";
6
6
  import "./chunk-BDQBOLYG.js";
7
7
  import {
8
8
  getEffectiveApiKey,
9
9
  getOpenWeatherKey,
10
10
  getSerperKey
11
- } from "./chunk-DQHCE3TN.js";
11
+ } from "./chunk-BD56DFFM.js";
12
12
  import {
13
13
  ConfigStore,
14
14
  logger
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  checkForUpdate,
18
18
  package_default
19
- } from "./chunk-SB53O27R.js";
19
+ } from "./chunk-PAE3OMY2.js";
20
20
  import {
21
21
  selectActiveBackgroundAgents,
22
22
  useCliStore
@@ -32,7 +32,7 @@ import {
32
32
  OpenAIBackend,
33
33
  OpenAIImageService,
34
34
  XAIImageService
35
- } from "./chunk-PV6PZFPC.js";
35
+ } from "./chunk-UAJBCJZ2.js";
36
36
  import {
37
37
  AiEvents,
38
38
  ApiKeyEvents,
@@ -733,8 +733,25 @@ var COMMANDS = [
733
733
  },
734
734
  {
735
735
  name: "rewind",
736
- description: "Rewind conversation to a previous point",
737
- aliases: ["undo"]
736
+ description: "Rewind conversation to a previous point"
737
+ },
738
+ {
739
+ name: "undo",
740
+ description: "Undo the last file change"
741
+ },
742
+ {
743
+ name: "checkpoints",
744
+ description: "List available file restore points"
745
+ },
746
+ {
747
+ name: "restore",
748
+ description: "Restore files to a specific checkpoint",
749
+ args: "<number>"
750
+ },
751
+ {
752
+ name: "diff",
753
+ description: "Show diff between current state and a checkpoint",
754
+ args: "[number]"
738
755
  },
739
756
  {
740
757
  name: "project-config",
@@ -2838,9 +2855,346 @@ var CommandHistoryStore = class {
2838
2855
  }
2839
2856
  };
2840
2857
 
2841
- // src/storage/CustomCommandStore.ts
2842
- import fs5 from "fs/promises";
2858
+ // src/storage/CheckpointStore.ts
2859
+ import { promises as fs5 } from "fs";
2860
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync, unlinkSync } from "fs";
2861
+ import { execFileSync } from "child_process";
2843
2862
  import path6 from "path";
2863
+ var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
2864
+ var DEFAULT_KEEP_COUNT = 50;
2865
+ var ABSENT_MARKER = ".b4m-absent";
2866
+ var CheckpointStore = class {
2867
+ constructor(projectDir) {
2868
+ this.metadata = null;
2869
+ this.sessionId = null;
2870
+ this.initialized = false;
2871
+ this.projectDir = projectDir;
2872
+ this.shadowRepoDir = path6.join(projectDir, ".b4m", "shadow-repo");
2873
+ this.metadataPath = path6.join(projectDir, ".b4m", "checkpoints.json");
2874
+ }
2875
+ /**
2876
+ * Initialize the shadow git repository and load metadata
2877
+ */
2878
+ async init(sessionId) {
2879
+ this.sessionId = sessionId;
2880
+ await fs5.mkdir(path6.join(this.projectDir, ".b4m"), { recursive: true });
2881
+ if (!existsSync4(path6.join(this.shadowRepoDir, ".git"))) {
2882
+ await fs5.mkdir(this.shadowRepoDir, { recursive: true });
2883
+ this.git("init");
2884
+ this.git("config", "user.email", "checkpoint@b4m.local");
2885
+ this.git("config", "user.name", "B4M Checkpoint");
2886
+ this.git("commit", "--allow-empty", "-m", "checkpoint-init");
2887
+ }
2888
+ await this.loadMetadata();
2889
+ await this.ensureGitignore();
2890
+ await this.pruneCheckpoints(DEFAULT_KEEP_COUNT);
2891
+ this.initialized = true;
2892
+ }
2893
+ /**
2894
+ * Update session ID (e.g., on /clear or /resume)
2895
+ */
2896
+ setSessionId(sessionId) {
2897
+ this.sessionId = sessionId;
2898
+ }
2899
+ /**
2900
+ * Create a checkpoint by snapshotting the current state of target files
2901
+ * before they are modified by a tool.
2902
+ *
2903
+ * @param toolName - The tool about to modify files
2904
+ * @param filePaths - Relative paths of files about to be modified
2905
+ * @param name - Optional human-readable checkpoint name
2906
+ */
2907
+ async createCheckpoint(toolName, filePaths, name) {
2908
+ if (!this.initialized || !this.sessionId) {
2909
+ return null;
2910
+ }
2911
+ const checkpointName = name || `before-${toolName}-${path6.basename(filePaths[0] || "unknown")}`;
2912
+ try {
2913
+ let hasChanges = false;
2914
+ for (const filePath of filePaths) {
2915
+ const absolutePath = this.validatePathWithinProject(filePath);
2916
+ const shadowPath = path6.join(this.shadowRepoDir, filePath);
2917
+ const shadowDir = path6.dirname(shadowPath);
2918
+ const absentMarkerPath = path6.join(shadowDir, `${path6.basename(filePath)}${ABSENT_MARKER}`);
2919
+ await fs5.mkdir(shadowDir, { recursive: true });
2920
+ if (existsSync4(absolutePath)) {
2921
+ const stats = await fs5.lstat(absolutePath);
2922
+ if (stats.isSymbolicLink()) {
2923
+ continue;
2924
+ }
2925
+ if (stats.size > MAX_FILE_SIZE2) {
2926
+ continue;
2927
+ }
2928
+ if (existsSync4(absentMarkerPath)) {
2929
+ await fs5.unlink(absentMarkerPath);
2930
+ }
2931
+ await fs5.copyFile(absolutePath, shadowPath);
2932
+ hasChanges = true;
2933
+ } else {
2934
+ if (existsSync4(shadowPath)) {
2935
+ await fs5.unlink(shadowPath);
2936
+ }
2937
+ await fs5.writeFile(absentMarkerPath, "", "utf-8");
2938
+ hasChanges = true;
2939
+ }
2940
+ }
2941
+ if (!hasChanges) {
2942
+ return null;
2943
+ }
2944
+ this.git("add", "-A");
2945
+ try {
2946
+ this.git("diff", "--cached", "--quiet");
2947
+ return null;
2948
+ } catch {
2949
+ }
2950
+ this.git("commit", "-m", checkpointName);
2951
+ const sha = this.git("rev-parse", "--short", "HEAD").trim();
2952
+ const checkpoint = {
2953
+ id: sha,
2954
+ name: checkpointName,
2955
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2956
+ toolName,
2957
+ filePaths: [...filePaths],
2958
+ sessionId: this.sessionId
2959
+ };
2960
+ if (!this.metadata) {
2961
+ this.metadata = { checkpoints: [], createdAt: (/* @__PURE__ */ new Date()).toISOString() };
2962
+ }
2963
+ this.metadata.checkpoints.push(checkpoint);
2964
+ await this.saveMetadata();
2965
+ return checkpoint;
2966
+ } catch {
2967
+ return null;
2968
+ }
2969
+ }
2970
+ /**
2971
+ * List checkpoints for the current session (most recent first)
2972
+ */
2973
+ listCheckpoints() {
2974
+ if (!this.metadata || !this.sessionId) {
2975
+ return [];
2976
+ }
2977
+ return this.metadata.checkpoints.filter((cp) => cp.sessionId === this.sessionId).reverse();
2978
+ }
2979
+ /**
2980
+ * Get a specific checkpoint by 1-based index (1 = most recent)
2981
+ */
2982
+ getCheckpoint(index) {
2983
+ const checkpoints = this.listCheckpoints();
2984
+ if (index < 1 || index > checkpoints.length) {
2985
+ return null;
2986
+ }
2987
+ return checkpoints[index - 1];
2988
+ }
2989
+ /**
2990
+ * Restore files to the state captured in a specific checkpoint
2991
+ *
2992
+ * @param index - 1-based index (1 = most recent)
2993
+ * @returns The checkpoint that was restored to
2994
+ */
2995
+ async restoreCheckpoint(index) {
2996
+ const checkpoint = this.getCheckpoint(index);
2997
+ if (!checkpoint) {
2998
+ throw new Error(`Checkpoint #${index} not found. Use /checkpoints to see available restore points.`);
2999
+ }
3000
+ for (const filePath of checkpoint.filePaths) {
3001
+ const absolutePath = this.validatePathWithinProject(filePath);
3002
+ try {
3003
+ const content = this.git("show", `${checkpoint.id}:${filePath}`);
3004
+ await fs5.mkdir(path6.dirname(absolutePath), { recursive: true });
3005
+ await fs5.writeFile(absolutePath, content, "utf-8");
3006
+ } catch {
3007
+ try {
3008
+ this.git("show", `${checkpoint.id}:${path6.dirname(filePath)}/${path6.basename(filePath)}${ABSENT_MARKER}`);
3009
+ if (existsSync4(absolutePath)) {
3010
+ await fs5.unlink(absolutePath);
3011
+ }
3012
+ } catch {
3013
+ }
3014
+ }
3015
+ }
3016
+ return checkpoint;
3017
+ }
3018
+ /**
3019
+ * Undo the last file change (restore to most recent checkpoint)
3020
+ */
3021
+ async undoLast() {
3022
+ return this.restoreCheckpoint(1);
3023
+ }
3024
+ /**
3025
+ * Get diff between current file state and a checkpoint
3026
+ *
3027
+ * @param index - 1-based index (1 = most recent, default)
3028
+ * @returns Unified diff string
3029
+ */
3030
+ getCheckpointDiff(index = 1) {
3031
+ const checkpoint = this.getCheckpoint(index);
3032
+ if (!checkpoint) {
3033
+ throw new Error(`Checkpoint #${index} not found. Use /checkpoints to see available restore points.`);
3034
+ }
3035
+ const diffParts = [];
3036
+ for (const filePath of checkpoint.filePaths) {
3037
+ const absolutePath = this.validatePathWithinProject(filePath);
3038
+ const tmpCheckpoint = path6.join(this.shadowRepoDir, ".diff-a");
3039
+ const tmpCurrent = path6.join(this.shadowRepoDir, ".diff-b");
3040
+ try {
3041
+ let checkpointContent;
3042
+ try {
3043
+ checkpointContent = this.git("show", `${checkpoint.id}:${filePath}`);
3044
+ } catch {
3045
+ checkpointContent = "";
3046
+ }
3047
+ let currentContent = "";
3048
+ if (existsSync4(absolutePath)) {
3049
+ currentContent = readFileSync4(absolutePath, "utf-8");
3050
+ }
3051
+ if (checkpointContent === currentContent) {
3052
+ continue;
3053
+ }
3054
+ writeFileSync(tmpCheckpoint, checkpointContent, "utf-8");
3055
+ writeFileSync(tmpCurrent, currentContent, "utf-8");
3056
+ try {
3057
+ this.git(
3058
+ "diff",
3059
+ "--no-index",
3060
+ "--color",
3061
+ `--src-prefix=checkpoint:`,
3062
+ `--dst-prefix=current:`,
3063
+ tmpCheckpoint,
3064
+ tmpCurrent
3065
+ );
3066
+ } catch (diffError) {
3067
+ if (diffError && typeof diffError === "object" && "stdout" in diffError) {
3068
+ const output = diffError.stdout?.toString() || "";
3069
+ if (output) {
3070
+ diffParts.push(`--- ${filePath} (checkpoint #${index})
3071
+ +++ ${filePath} (current)
3072
+ ${output}`);
3073
+ }
3074
+ }
3075
+ }
3076
+ } catch {
3077
+ } finally {
3078
+ try {
3079
+ unlinkSync(tmpCheckpoint);
3080
+ } catch {
3081
+ }
3082
+ try {
3083
+ unlinkSync(tmpCurrent);
3084
+ } catch {
3085
+ }
3086
+ }
3087
+ }
3088
+ return diffParts.join("\n");
3089
+ }
3090
+ /**
3091
+ * Prune old checkpoints beyond the keep count
3092
+ */
3093
+ async pruneCheckpoints(keepCount = DEFAULT_KEEP_COUNT) {
3094
+ if (!this.metadata) return;
3095
+ const total = this.metadata.checkpoints.length;
3096
+ if (total <= keepCount) return;
3097
+ this.metadata.checkpoints = this.metadata.checkpoints.slice(-keepCount);
3098
+ await this.saveMetadata();
3099
+ try {
3100
+ this.git("gc", "--auto", "--quiet");
3101
+ } catch {
3102
+ }
3103
+ }
3104
+ /**
3105
+ * Clean up the shadow repository entirely
3106
+ */
3107
+ async cleanup() {
3108
+ try {
3109
+ await fs5.rm(this.shadowRepoDir, { recursive: true, force: true });
3110
+ if (existsSync4(this.metadataPath)) {
3111
+ await fs5.unlink(this.metadataPath);
3112
+ }
3113
+ this.metadata = null;
3114
+ this.initialized = false;
3115
+ } catch {
3116
+ }
3117
+ }
3118
+ // --- Private helpers ---
3119
+ /**
3120
+ * Validate that a file path resolves within the project directory.
3121
+ * Prevents path traversal attacks (e.g., ../../etc/passwd).
3122
+ */
3123
+ validatePathWithinProject(filePath) {
3124
+ const absolutePath = path6.resolve(this.projectDir, filePath);
3125
+ const normalizedProject = path6.resolve(this.projectDir) + path6.sep;
3126
+ if (!absolutePath.startsWith(normalizedProject) && absolutePath !== path6.resolve(this.projectDir)) {
3127
+ throw new Error(`Path traversal detected: ${filePath}`);
3128
+ }
3129
+ return absolutePath;
3130
+ }
3131
+ /**
3132
+ * Execute a git command in the shadow repo
3133
+ */
3134
+ git(...args) {
3135
+ return execFileSync("git", args, {
3136
+ cwd: this.shadowRepoDir,
3137
+ encoding: "utf-8",
3138
+ stdio: ["pipe", "pipe", "pipe"],
3139
+ timeout: 1e4
3140
+ });
3141
+ }
3142
+ /**
3143
+ * Load checkpoint metadata from disk
3144
+ */
3145
+ async loadMetadata() {
3146
+ try {
3147
+ if (existsSync4(this.metadataPath)) {
3148
+ const data = await fs5.readFile(this.metadataPath, "utf-8");
3149
+ this.metadata = JSON.parse(data);
3150
+ } else {
3151
+ this.metadata = {
3152
+ checkpoints: [],
3153
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
3154
+ };
3155
+ }
3156
+ } catch {
3157
+ this.metadata = {
3158
+ checkpoints: [],
3159
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
3160
+ };
3161
+ }
3162
+ }
3163
+ /**
3164
+ * Save checkpoint metadata to disk
3165
+ */
3166
+ async saveMetadata() {
3167
+ if (!this.metadata) return;
3168
+ await fs5.writeFile(this.metadataPath, JSON.stringify(this.metadata, null, 2), "utf-8");
3169
+ }
3170
+ /**
3171
+ * Ensure .b4m/ is in .gitignore
3172
+ */
3173
+ async ensureGitignore() {
3174
+ const gitignorePath = path6.join(this.projectDir, ".gitignore");
3175
+ const entryToAdd = ".b4m/";
3176
+ try {
3177
+ let content = "";
3178
+ try {
3179
+ content = await fs5.readFile(gitignorePath, "utf-8");
3180
+ } catch {
3181
+ }
3182
+ if (content.includes(entryToAdd) || content.includes(".b4m")) {
3183
+ return;
3184
+ }
3185
+ const newContent = content.trim() + (content ? "\n" : "") + `
3186
+ # B4M checkpoint data
3187
+ ${entryToAdd}
3188
+ `;
3189
+ await fs5.writeFile(gitignorePath, newContent, "utf-8");
3190
+ } catch {
3191
+ }
3192
+ }
3193
+ };
3194
+
3195
+ // src/storage/CustomCommandStore.ts
3196
+ import fs6 from "fs/promises";
3197
+ import path7 from "path";
2844
3198
  import os from "os";
2845
3199
 
2846
3200
  // src/utils/commandParser.ts
@@ -3004,14 +3358,14 @@ var CustomCommandStore = class {
3004
3358
  const home = os.homedir();
3005
3359
  const root = projectRoot || process.cwd();
3006
3360
  this.globalCommandsDirs = [
3007
- path6.join(home, ".bike4mind", "commands"),
3008
- path6.join(home, ".claude", "commands"),
3009
- path6.join(home, ".claude", "skills")
3361
+ path7.join(home, ".bike4mind", "commands"),
3362
+ path7.join(home, ".claude", "commands"),
3363
+ path7.join(home, ".claude", "skills")
3010
3364
  ];
3011
3365
  this.projectCommandsDirs = [
3012
- path6.join(root, ".bike4mind", "commands"),
3013
- path6.join(root, ".claude", "commands"),
3014
- path6.join(root, ".claude", "skills")
3366
+ path7.join(root, ".bike4mind", "commands"),
3367
+ path7.join(root, ".claude", "commands"),
3368
+ path7.join(root, ".claude", "skills")
3015
3369
  ];
3016
3370
  }
3017
3371
  /**
@@ -3036,7 +3390,7 @@ var CustomCommandStore = class {
3036
3390
  */
3037
3391
  async loadCommandsFromDirectory(directory, source) {
3038
3392
  try {
3039
- const stats = await fs5.stat(directory);
3393
+ const stats = await fs6.stat(directory);
3040
3394
  if (!stats.isDirectory()) {
3041
3395
  return;
3042
3396
  }
@@ -3069,9 +3423,9 @@ var CustomCommandStore = class {
3069
3423
  async findCommandFiles(directory) {
3070
3424
  const files = [];
3071
3425
  try {
3072
- const entries = await fs5.readdir(directory, { withFileTypes: true });
3426
+ const entries = await fs6.readdir(directory, { withFileTypes: true });
3073
3427
  for (const entry of entries) {
3074
- const fullPath = path6.join(directory, entry.name);
3428
+ const fullPath = path7.join(directory, entry.name);
3075
3429
  if (entry.isDirectory()) {
3076
3430
  const subFiles = await this.findCommandFiles(fullPath);
3077
3431
  files.push(...subFiles);
@@ -3091,14 +3445,14 @@ var CustomCommandStore = class {
3091
3445
  * @param source - Source identifier ('global' or 'project')
3092
3446
  */
3093
3447
  async loadCommandFile(filePath, source) {
3094
- const filename = path6.basename(filePath);
3448
+ const filename = path7.basename(filePath);
3095
3449
  const isSkillFile = filename.toLowerCase() === "skill.md";
3096
3450
  const commandName = isSkillFile ? this.extractSkillName(filePath) : extractCommandName(filename);
3097
3451
  if (!commandName) {
3098
3452
  console.warn(`Invalid command filename: ${filename} (must end with .md and have valid name)`);
3099
3453
  return;
3100
3454
  }
3101
- const fileContent = await fs5.readFile(filePath, "utf-8");
3455
+ const fileContent = await fs6.readFile(filePath, "utf-8");
3102
3456
  const command = parseCommandFile(fileContent, filePath, commandName, source);
3103
3457
  this.commands.set(commandName, command);
3104
3458
  }
@@ -3110,7 +3464,7 @@ var CustomCommandStore = class {
3110
3464
  * @returns Skill name or null if invalid
3111
3465
  */
3112
3466
  extractSkillName(filePath) {
3113
- const parentDir = path6.basename(path6.dirname(filePath));
3467
+ const parentDir = path7.basename(path7.dirname(filePath));
3114
3468
  return parentDir && parentDir !== "skills" ? parentDir : null;
3115
3469
  }
3116
3470
  /**
@@ -3172,15 +3526,15 @@ var CustomCommandStore = class {
3172
3526
  */
3173
3527
  async createCommandFile(name, isGlobal = false) {
3174
3528
  const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
3175
- const filePath = path6.join(targetDir, `${name}.md`);
3176
- const fileExists = await fs5.access(filePath).then(
3529
+ const filePath = path7.join(targetDir, `${name}.md`);
3530
+ const fileExists = await fs6.access(filePath).then(
3177
3531
  () => true,
3178
3532
  () => false
3179
3533
  );
3180
3534
  if (fileExists) {
3181
3535
  throw new Error(`Command file already exists: ${filePath}`);
3182
3536
  }
3183
- await fs5.mkdir(targetDir, { recursive: true });
3537
+ await fs6.mkdir(targetDir, { recursive: true });
3184
3538
  const template = `---
3185
3539
  description: ${name} command
3186
3540
  argument-hint: [args]
@@ -3195,7 +3549,7 @@ You can use:
3195
3549
  - $1, $2, etc. for positional arguments
3196
3550
  - @filename for file references
3197
3551
  `;
3198
- await fs5.writeFile(filePath, template, "utf-8");
3552
+ await fs6.writeFile(filePath, template, "utf-8");
3199
3553
  return filePath;
3200
3554
  }
3201
3555
  };
@@ -4427,7 +4781,7 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
4427
4781
  import OpenAI2 from "openai";
4428
4782
 
4429
4783
  // ../../b4m-core/packages/services/dist/src/importHistoryService/index.js
4430
- import fs6, { unlinkSync } from "fs";
4784
+ import fs7, { unlinkSync as unlinkSync2 } from "fs";
4431
4785
  import yauzl from "yauzl";
4432
4786
  import axios3 from "axios";
4433
4787
 
@@ -6118,8 +6472,8 @@ async function processAndStoreImages(images, context) {
6118
6472
  const buffer = await downloadImage(image);
6119
6473
  const fileType = await fileTypeFromBuffer2(buffer);
6120
6474
  const filename = `${uuidv45()}.${fileType?.ext}`;
6121
- const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
6122
- return path19;
6475
+ const path21 = await context.imageGenerateStorage.upload(buffer, filename, {});
6476
+ return path21;
6123
6477
  }));
6124
6478
  }
6125
6479
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -7445,8 +7799,8 @@ async function processAndStoreImage(imageUrl, context) {
7445
7799
  const buffer = await downloadImage2(imageUrl);
7446
7800
  const fileType = await fileTypeFromBuffer3(buffer);
7447
7801
  const filename = `${uuidv46()}.${fileType?.ext}`;
7448
- const path19 = await context.imageGenerateStorage.upload(buffer, filename, {});
7449
- return path19;
7802
+ const path21 = await context.imageGenerateStorage.upload(buffer, filename, {});
7803
+ return path21;
7450
7804
  }
7451
7805
  async function generateFullMask(imageBuffer) {
7452
7806
  const sharp = (await import("sharp")).default;
@@ -8064,7 +8418,7 @@ var blogDraftTool = {
8064
8418
  outputFormat,
8065
8419
  additionalInstructions: params.additionalInstructions
8066
8420
  });
8067
- const modelToUse = context.model || "claude-sonnet-4-5-20250929";
8421
+ const modelToUse = context.model || ChatModels.CLAUDE_4_6_SONNET;
8068
8422
  logger2.info("Using model for blog draft:", { model: modelToUse });
8069
8423
  let llmResponse = "";
8070
8424
  await llm.complete(modelToUse, [
@@ -9387,33 +9741,33 @@ var planetVisibilityTool = {
9387
9741
  };
9388
9742
 
9389
9743
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
9390
- import { promises as fs7 } from "fs";
9391
- import { existsSync as existsSync4, statSync as statSync4 } from "fs";
9392
- import path7 from "path";
9393
- var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
9744
+ import { promises as fs8 } from "fs";
9745
+ import { existsSync as existsSync5, statSync as statSync4 } from "fs";
9746
+ import path8 from "path";
9747
+ var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
9394
9748
  async function readFileContent(params) {
9395
9749
  const { path: filePath, encoding = "utf-8", offset = 0, limit } = params;
9396
- const normalizedPath = path7.normalize(filePath);
9397
- const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
9398
- const cwd = path7.resolve(process.cwd());
9750
+ const normalizedPath = path8.normalize(filePath);
9751
+ const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
9752
+ const cwd = path8.resolve(process.cwd());
9399
9753
  if (!resolvedPath.startsWith(cwd)) {
9400
9754
  throw new Error(`Access denied: Cannot read files outside of current working directory`);
9401
9755
  }
9402
- if (!existsSync4(resolvedPath)) {
9756
+ if (!existsSync5(resolvedPath)) {
9403
9757
  throw new Error(`File not found: ${filePath}`);
9404
9758
  }
9405
9759
  const stats = statSync4(resolvedPath);
9406
9760
  if (stats.isDirectory()) {
9407
9761
  throw new Error(`Path is a directory, not a file: ${filePath}`);
9408
9762
  }
9409
- if (stats.size > MAX_FILE_SIZE2) {
9410
- throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE2 / 1024 / 1024}MB)`);
9763
+ if (stats.size > MAX_FILE_SIZE3) {
9764
+ throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE3 / 1024 / 1024}MB)`);
9411
9765
  }
9412
9766
  const isBinary = await checkIfBinary(resolvedPath);
9413
9767
  if (isBinary && encoding === "utf-8") {
9414
9768
  throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
9415
9769
  }
9416
- const content = await fs7.readFile(resolvedPath, encoding);
9770
+ const content = await fs8.readFile(resolvedPath, encoding);
9417
9771
  if (typeof content === "string") {
9418
9772
  const lines = content.split("\n");
9419
9773
  const totalLines = lines.length;
@@ -9451,7 +9805,7 @@ ${content}`;
9451
9805
  }
9452
9806
  async function checkIfBinary(filePath) {
9453
9807
  const buffer = Buffer.alloc(8192);
9454
- const fd = await fs7.open(filePath, "r");
9808
+ const fd = await fs8.open(filePath, "r");
9455
9809
  try {
9456
9810
  const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
9457
9811
  const chunk = buffer.slice(0, bytesRead);
@@ -9468,7 +9822,7 @@ var fileReadTool = {
9468
9822
  context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
9469
9823
  try {
9470
9824
  const content = await readFileContent(params);
9471
- const stats = statSync4(path7.resolve(process.cwd(), path7.normalize(params.path)));
9825
+ const stats = statSync4(path8.resolve(process.cwd(), path8.normalize(params.path)));
9472
9826
  context.logger.info("\u2705 FileRead: Success", {
9473
9827
  path: params.path,
9474
9828
  size: stats.size,
@@ -9512,25 +9866,25 @@ var fileReadTool = {
9512
9866
  };
9513
9867
 
9514
9868
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
9515
- import { promises as fs8 } from "fs";
9516
- import { existsSync as existsSync5 } from "fs";
9517
- import path8 from "path";
9869
+ import { promises as fs9 } from "fs";
9870
+ import { existsSync as existsSync6 } from "fs";
9871
+ import path9 from "path";
9518
9872
  async function createFile(params) {
9519
9873
  const { path: filePath, content, createDirectories = true } = params;
9520
- const normalizedPath = path8.normalize(filePath);
9521
- const resolvedPath = path8.resolve(process.cwd(), normalizedPath);
9522
- const cwd = path8.resolve(process.cwd());
9874
+ const normalizedPath = path9.normalize(filePath);
9875
+ const resolvedPath = path9.resolve(process.cwd(), normalizedPath);
9876
+ const cwd = path9.resolve(process.cwd());
9523
9877
  if (!resolvedPath.startsWith(cwd)) {
9524
9878
  throw new Error(`Access denied: Cannot create files outside of current working directory`);
9525
9879
  }
9526
- const fileExists = existsSync5(resolvedPath);
9880
+ const fileExists = existsSync6(resolvedPath);
9527
9881
  const action = fileExists ? "overwritten" : "created";
9528
9882
  if (createDirectories) {
9529
- const dir = path8.dirname(resolvedPath);
9530
- await fs8.mkdir(dir, { recursive: true });
9883
+ const dir = path9.dirname(resolvedPath);
9884
+ await fs9.mkdir(dir, { recursive: true });
9531
9885
  }
9532
- await fs8.writeFile(resolvedPath, content, "utf-8");
9533
- const stats = await fs8.stat(resolvedPath);
9886
+ await fs9.writeFile(resolvedPath, content, "utf-8");
9887
+ const stats = await fs9.stat(resolvedPath);
9534
9888
  const lines = content.split("\n").length;
9535
9889
  return `File ${action} successfully: ${filePath}
9536
9890
  Size: ${stats.size} bytes
@@ -9541,7 +9895,7 @@ var createFileTool = {
9541
9895
  implementation: (context) => ({
9542
9896
  toolFn: async (value) => {
9543
9897
  const params = value;
9544
- const fileExists = existsSync5(path8.resolve(process.cwd(), path8.normalize(params.path)));
9898
+ const fileExists = existsSync6(path9.resolve(process.cwd(), path9.normalize(params.path)));
9545
9899
  context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
9546
9900
  path: params.path,
9547
9901
  size: params.content.length
@@ -9583,7 +9937,7 @@ var createFileTool = {
9583
9937
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
9584
9938
  import { glob } from "glob";
9585
9939
  import { stat } from "fs/promises";
9586
- import path9 from "path";
9940
+ import path10 from "path";
9587
9941
  var DEFAULT_IGNORE_PATTERNS = [
9588
9942
  "**/node_modules/**",
9589
9943
  "**/.git/**",
@@ -9599,7 +9953,7 @@ var DEFAULT_IGNORE_PATTERNS = [
9599
9953
  async function findFiles(params) {
9600
9954
  const { pattern, dir_path, case_sensitive = true, respect_git_ignore = true } = params;
9601
9955
  const baseCwd = process.cwd();
9602
- const targetDir = dir_path ? path9.resolve(baseCwd, path9.normalize(dir_path)) : baseCwd;
9956
+ const targetDir = dir_path ? path10.resolve(baseCwd, path10.normalize(dir_path)) : baseCwd;
9603
9957
  if (!targetDir.startsWith(baseCwd)) {
9604
9958
  throw new Error(`Access denied: Cannot search outside of current working directory`);
9605
9959
  }
@@ -9639,7 +9993,7 @@ async function findFiles(params) {
9639
9993
  const summary = `Found ${filesWithStats.length} file(s)${truncated ? ` (showing first ${MAX_RESULTS})` : ""} matching: ${pattern}`;
9640
9994
  const dirInfo = dir_path ? `
9641
9995
  Directory: ${dir_path}` : "";
9642
- const filesList = results.map((file) => path9.relative(baseCwd, file.path)).join("\n");
9996
+ const filesList = results.map((file) => path10.relative(baseCwd, file.path)).join("\n");
9643
9997
  return `${summary}${dirInfo}
9644
9998
 
9645
9999
  ${filesList}`;
@@ -9693,27 +10047,48 @@ var globFilesTool = {
9693
10047
 
9694
10048
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/grepSearch/index.js
9695
10049
  import { stat as stat2 } from "fs/promises";
9696
- import path10 from "path";
9697
- import { execFile } from "child_process";
10050
+ import { existsSync as existsSync7 } from "fs";
10051
+ import path11 from "path";
10052
+ import { execFile, execFileSync as execFileSync2 } from "child_process";
9698
10053
  import { promisify } from "util";
9699
10054
  import { createRequire } from "module";
9700
10055
  var execFileAsync = promisify(execFile);
9701
10056
  var require2 = createRequire(import.meta.url);
10057
+ var cachedRgPath = null;
9702
10058
  function getRipgrepPath() {
10059
+ if (cachedRgPath)
10060
+ return cachedRgPath;
9703
10061
  try {
9704
10062
  const ripgrepPath = require2.resolve("@vscode/ripgrep");
9705
- const ripgrepDir = path10.dirname(ripgrepPath);
9706
- const rgBinary = path10.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
10063
+ const ripgrepDir = path11.dirname(ripgrepPath);
10064
+ const rgBinary = path11.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
10065
+ if (!existsSync7(rgBinary)) {
10066
+ const postinstallScript = path11.join(ripgrepDir, "..", "lib", "postinstall.js");
10067
+ if (existsSync7(postinstallScript)) {
10068
+ execFileSync2(process.execPath, [postinstallScript], {
10069
+ cwd: path11.join(ripgrepDir, ".."),
10070
+ stdio: "pipe",
10071
+ timeout: 3e4
10072
+ });
10073
+ }
10074
+ if (!existsSync7(rgBinary)) {
10075
+ throw new Error(`ripgrep binary not found at ${rgBinary}. The postinstall download may have failed. Try running: cd ${path11.join(ripgrepDir, "..")} && node lib/postinstall.js`);
10076
+ }
10077
+ }
10078
+ cachedRgPath = rgBinary;
9707
10079
  return rgBinary;
9708
10080
  } catch (error) {
10081
+ if (error instanceof Error && error.message.includes("ripgrep binary not found")) {
10082
+ throw error;
10083
+ }
9709
10084
  throw new Error("ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services");
9710
10085
  }
9711
10086
  }
9712
10087
  function isPathWithinWorkspace(targetPath, baseCwd) {
9713
- const resolvedTarget = path10.resolve(targetPath);
9714
- const resolvedBase = path10.resolve(baseCwd);
9715
- const relativePath = path10.relative(resolvedBase, resolvedTarget);
9716
- return !relativePath.startsWith("..") && !path10.isAbsolute(relativePath);
10088
+ const resolvedTarget = path11.resolve(targetPath);
10089
+ const resolvedBase = path11.resolve(baseCwd);
10090
+ const relativePath = path11.relative(resolvedBase, resolvedTarget);
10091
+ return !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
9717
10092
  }
9718
10093
  function convertGlobToRipgrepGlobs(globPattern) {
9719
10094
  if (!globPattern || globPattern === "**/*") {
@@ -9729,7 +10104,7 @@ function convertGlobToRipgrepGlobs(globPattern) {
9729
10104
  async function searchFiles2(params) {
9730
10105
  const { pattern, dir_path, include } = params;
9731
10106
  const baseCwd = process.cwd();
9732
- const targetDir = dir_path ? path10.resolve(baseCwd, dir_path) : baseCwd;
10107
+ const targetDir = dir_path ? path11.resolve(baseCwd, dir_path) : baseCwd;
9733
10108
  if (!isPathWithinWorkspace(targetDir, baseCwd)) {
9734
10109
  throw new Error(`Path validation failed: "${dir_path}" resolves outside the allowed workspace directory`);
9735
10110
  }
@@ -9791,7 +10166,7 @@ async function searchFiles2(params) {
9791
10166
  if (item.type === "match") {
9792
10167
  const match = item;
9793
10168
  allMatches.push({
9794
- filePath: path10.relative(targetDir, match.data.path.text) || path10.basename(match.data.path.text),
10169
+ filePath: path11.relative(targetDir, match.data.path.text) || path11.basename(match.data.path.text),
9795
10170
  lineNumber: match.data.line_number,
9796
10171
  line: match.data.lines.text.trimEnd()
9797
10172
  // Remove trailing newline
@@ -9884,18 +10259,18 @@ var grepSearchTool = {
9884
10259
  };
9885
10260
 
9886
10261
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/deleteFile/index.js
9887
- import { promises as fs9 } from "fs";
9888
- import { existsSync as existsSync6, statSync as statSync5 } from "fs";
9889
- import path11 from "path";
10262
+ import { promises as fs10 } from "fs";
10263
+ import { existsSync as existsSync8, statSync as statSync5 } from "fs";
10264
+ import path12 from "path";
9890
10265
  async function deleteFile(params) {
9891
10266
  const { path: filePath, recursive = false } = params;
9892
- const normalizedPath = path11.normalize(filePath);
9893
- const resolvedPath = path11.resolve(process.cwd(), normalizedPath);
9894
- const cwd = path11.resolve(process.cwd());
10267
+ const normalizedPath = path12.normalize(filePath);
10268
+ const resolvedPath = path12.resolve(process.cwd(), normalizedPath);
10269
+ const cwd = path12.resolve(process.cwd());
9895
10270
  if (!resolvedPath.startsWith(cwd)) {
9896
10271
  throw new Error(`Access denied: Cannot delete files outside of current working directory`);
9897
10272
  }
9898
- if (!existsSync6(resolvedPath)) {
10273
+ if (!existsSync8(resolvedPath)) {
9899
10274
  throw new Error(`File or directory not found: ${filePath}`);
9900
10275
  }
9901
10276
  const stats = statSync5(resolvedPath);
@@ -9905,10 +10280,10 @@ async function deleteFile(params) {
9905
10280
  throw new Error(`Path is a directory: ${filePath}. Use recursive=true to delete directories and their contents.`);
9906
10281
  }
9907
10282
  if (isDirectory) {
9908
- await fs9.rm(resolvedPath, { recursive: true, force: true });
10283
+ await fs10.rm(resolvedPath, { recursive: true, force: true });
9909
10284
  return `Directory deleted successfully: ${filePath}`;
9910
10285
  } else {
9911
- await fs9.unlink(resolvedPath);
10286
+ await fs10.unlink(resolvedPath);
9912
10287
  return `File deleted successfully: ${filePath}
9913
10288
  Size: ${size} bytes`;
9914
10289
  }
@@ -9918,8 +10293,8 @@ var deleteFileTool = {
9918
10293
  implementation: (context) => ({
9919
10294
  toolFn: async (value) => {
9920
10295
  const params = value;
9921
- const resolvedPath = path11.resolve(process.cwd(), path11.normalize(params.path));
9922
- const isDirectory = existsSync6(resolvedPath) && statSync5(resolvedPath).isDirectory();
10296
+ const resolvedPath = path12.resolve(process.cwd(), path12.normalize(params.path));
10297
+ const isDirectory = existsSync8(resolvedPath) && statSync5(resolvedPath).isDirectory();
9923
10298
  context.logger.info(`\u{1F5D1}\uFE0F DeleteFile: Deleting ${isDirectory ? "directory" : "file"}`, {
9924
10299
  path: params.path,
9925
10300
  recursive: params.recursive
@@ -11496,6 +11871,7 @@ var solverMetadata = {
11496
11871
  requires: "Backend compute",
11497
11872
  available: false,
11498
11873
  tagline: "Quantum-inspired classical simulation",
11874
+ externalUrl: "https://q.bike4mind.com/jobs/e499f34a-d40e-4cfb-98e5-47e0a2091f3d",
11499
11875
  fullDescription: "A classical simulator of the Quantum Approximate Optimization Algorithm (QAOA). Encodes the job-shop scheduling problem as a QUBO (Quadratic Unconstrained Binary Optimization), then simulates the quantum variational circuit classically. Provides a preview of quantum advantage without requiring real quantum hardware.",
11500
11876
  howItWorks: [
11501
11877
  "Encode scheduling problem as QUBO matrix",
@@ -11541,6 +11917,7 @@ var solverMetadata = {
11541
11917
  requires: "IonQ API key + credits",
11542
11918
  available: false,
11543
11919
  tagline: "Real quantum hardware optimization",
11920
+ externalUrl: "https://cloud.ionq.com/jobs/019c07ff-205a-76d0-9f83-24098ea09589",
11544
11921
  fullDescription: "Runs the Quantum Approximate Optimization Algorithm on IonQ\u2019s trapped-ion quantum computers. This is real quantum computation \u2014 the problem is encoded as a QUBO, compiled to native quantum gates, and executed on physical qubits. Results include genuine quantum effects like superposition and entanglement.",
11545
11922
  howItWorks: [
11546
11923
  "Encode scheduling problem as QUBO matrix",
@@ -12598,7 +12975,7 @@ var navigateViewTool = {
12598
12975
 
12599
12976
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/bashExecute/index.js
12600
12977
  import { spawn } from "child_process";
12601
- import path12 from "path";
12978
+ import path13 from "path";
12602
12979
  var DEFAULT_TIMEOUT_MS = 6e4;
12603
12980
  var MAX_OUTPUT_SIZE = 100 * 1024;
12604
12981
  var DANGEROUS_PATTERNS = [
@@ -12782,7 +13159,7 @@ async function executeBashCommand(params) {
12782
13159
  };
12783
13160
  }
12784
13161
  const baseCwd = process.cwd();
12785
- const targetCwd = relativeCwd ? path12.resolve(baseCwd, relativeCwd) : baseCwd;
13162
+ const targetCwd = relativeCwd ? path13.resolve(baseCwd, relativeCwd) : baseCwd;
12786
13163
  const effectiveTimeout = Math.min(timeout, 5 * 60 * 1e3);
12787
13164
  return new Promise((resolve3) => {
12788
13165
  let stdout = "";
@@ -13785,9 +14162,9 @@ function parseQuery(query) {
13785
14162
  }
13786
14163
 
13787
14164
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/editLocalFile/index.js
13788
- import { promises as fs10 } from "fs";
13789
- import { existsSync as existsSync7 } from "fs";
13790
- import path13 from "path";
14165
+ import { promises as fs11 } from "fs";
14166
+ import { existsSync as existsSync9 } from "fs";
14167
+ import path14 from "path";
13791
14168
  import { diffLines as diffLines3 } from "diff";
13792
14169
  function generateDiff(original, modified) {
13793
14170
  const differences = diffLines3(original, modified);
@@ -13811,16 +14188,16 @@ function generateDiff(original, modified) {
13811
14188
  }
13812
14189
  async function editLocalFile(params) {
13813
14190
  const { path: filePath, old_string, new_string } = params;
13814
- const normalizedPath = path13.normalize(filePath);
13815
- const resolvedPath = path13.resolve(process.cwd(), normalizedPath);
13816
- const cwd = path13.resolve(process.cwd());
14191
+ const normalizedPath = path14.normalize(filePath);
14192
+ const resolvedPath = path14.resolve(process.cwd(), normalizedPath);
14193
+ const cwd = path14.resolve(process.cwd());
13817
14194
  if (!resolvedPath.startsWith(cwd)) {
13818
14195
  throw new Error(`Access denied: Cannot edit files outside of current working directory`);
13819
14196
  }
13820
- if (!existsSync7(resolvedPath)) {
14197
+ if (!existsSync9(resolvedPath)) {
13821
14198
  throw new Error(`File not found: ${filePath}`);
13822
14199
  }
13823
- const currentContent = await fs10.readFile(resolvedPath, "utf-8");
14200
+ const currentContent = await fs11.readFile(resolvedPath, "utf-8");
13824
14201
  if (!currentContent.includes(old_string)) {
13825
14202
  const preview = old_string.length > 100 ? old_string.substring(0, 100) + "..." : old_string;
13826
14203
  throw new Error(`String to replace not found in file. Make sure the old_string matches exactly (including whitespace and line endings). Searched for: "${preview}"`);
@@ -13830,7 +14207,7 @@ async function editLocalFile(params) {
13830
14207
  throw new Error(`Found ${occurrences} occurrences of the string to replace. Please provide a more specific old_string that matches exactly one location.`);
13831
14208
  }
13832
14209
  const newContent = currentContent.replace(old_string, new_string);
13833
- await fs10.writeFile(resolvedPath, newContent, "utf-8");
14210
+ await fs11.writeFile(resolvedPath, newContent, "utf-8");
13834
14211
  const diffResult = generateDiff(old_string, new_string);
13835
14212
  return `File edited successfully: ${filePath}
13836
14213
  Changes: +${diffResult.additions} lines, -${diffResult.deletions} lines
@@ -14390,7 +14767,8 @@ var SUBAGENT_TIMEOUT_MS = 5 * 60 * 1e3;
14390
14767
  var CodeReviewAgent = (config) => ({
14391
14768
  name: "code_review",
14392
14769
  description: "Code review specialist for analyzing code quality, bugs, and improvements",
14393
- model: config?.model ?? "claude-sonnet-4-5-20250929",
14770
+ model: config?.model ?? ChatModels.CLAUDE_4_6_SONNET,
14771
+ fallbackModels: [ChatModels.GPT4_1, ChatModels.GPT4_1_MINI],
14394
14772
  defaultThoroughness: config?.defaultThoroughness ?? "medium",
14395
14773
  maxIterations: { quick: 3, medium: 8, very_thorough: 15 },
14396
14774
  deniedTools: ["image_generation", "edit_image", "delegate_to_agent", ...config?.extraDeniedTools ?? []],
@@ -14430,7 +14808,8 @@ Focus on actionable, specific feedback referencing exact code locations. Your re
14430
14808
  var ProjectManagerAgent = (config) => ({
14431
14809
  name: "project_manager",
14432
14810
  description: "Project management via Jira and Confluence (create issues, search, update status, write docs). ALWAYS delegate Jira/Confluence requests to this agent \u2014 you do not have direct access to these tools",
14433
- model: config?.model ?? "claude-sonnet-4-5-20250929",
14811
+ model: config?.model ?? ChatModels.CLAUDE_4_6_SONNET,
14812
+ fallbackModels: [ChatModels.GPT4_1, ChatModels.GPT4_1_MINI],
14434
14813
  defaultThoroughness: config?.defaultThoroughness ?? "medium",
14435
14814
  maxIterations: { quick: 3, medium: 8, very_thorough: 15 },
14436
14815
  allowedTools: [...config?.extraAllowedTools ?? []],
@@ -14471,7 +14850,8 @@ Be precise with issue keys and project names. Your results will be used by the m
14471
14850
  var GithubManagerAgent = (config) => ({
14472
14851
  name: "github_manager",
14473
14852
  description: "GitHub operations (issues, pull requests, code search, branches, workflows, reviews). ALWAYS delegate GitHub requests to this agent \u2014 you do not have direct access to these tools",
14474
- model: config?.model ?? "claude-sonnet-4-5-20250929",
14853
+ model: config?.model ?? ChatModels.CLAUDE_4_6_SONNET,
14854
+ fallbackModels: [ChatModels.GPT4_1, ChatModels.GPT4_1_MINI],
14475
14855
  defaultThoroughness: config?.defaultThoroughness ?? "medium",
14476
14856
  maxIterations: { quick: 3, medium: 8, very_thorough: 15 },
14477
14857
  allowedTools: [...config?.extraAllowedTools ?? []],
@@ -14479,12 +14859,27 @@ var GithubManagerAgent = (config) => ({
14479
14859
  exclusiveMcpServers: ["github"],
14480
14860
  systemPrompt: `You are a GitHub specialist with access to GitHub's API. Your job is to help manage repositories, issues, pull requests, code search, branches, and CI/CD workflows.
14481
14861
 
14482
- ## First Step: Resolve Repository Context
14483
- ALWAYS start by calling the \`github__current_user\` tool. The response includes:
14862
+ ## First Step: ALWAYS Resolve Repository Context Automatically
14863
+ Before doing ANYTHING else, call the \`github__current_user\` tool. The response includes:
14484
14864
  - **selected_repositories**: The list of repositories the user has enabled for AI access (in \`owner/repo\` format)
14485
14865
  - **user**: The authenticated user's profile (login, name, etc.)
14486
14866
 
14487
- Use this to resolve the owner and repo name when the user does not specify them. If only one repository is selected, default to that repository. If multiple are selected and the user's request is ambiguous, mention the available repositories and ask for clarification via your response.
14867
+ ### Repository Resolution Rules (CRITICAL)
14868
+ You MUST use the selected_repositories list to automatically resolve the owner and repo. NEVER ask the user for the org, owner, or full repository path. Instead:
14869
+
14870
+ 1. **Exact match**: If the user says a repo name that exactly matches a repo name in selected_repositories, use it immediately.
14871
+ 2. **Partial/fuzzy match**: If the user provides a partial name, match it against any repo in selected_repositories whose name contains that term (e.g., if the user says "lumina" and selected_repositories contains \`SomeOrg/lumina5\`, use that).
14872
+ 3. **Single repo shortcut**: If only one repository is selected, ALWAYS default to it \u2014 no questions asked.
14873
+ 4. **Multiple matches**: Only if multiple selected repos match the user's term AND you truly cannot disambiguate, list the matching repos and ask which one. Do NOT list repos that don't match.
14874
+ 5. **No match**: If nothing in selected_repositories matches, tell the user which repos are available and ask them to clarify.
14875
+
14876
+ **NEVER ask the user to "paste the GitHub URL" or provide the org/owner name.** You already have this information from \`github__current_user\`. The owner and repo are embedded in the \`owner/repo\` format of each selected repository entry.
14877
+
14878
+ ### Defaults for Ambiguous Requests
14879
+ - **Timezone**: Default to UTC unless the user specifies otherwise.
14880
+ - **PR/Issue state**: Default to \`open\` unless the user specifies otherwise.
14881
+ - **Scope**: All matching repos unless the user narrows it down.
14882
+ - **When in doubt, act**: Prefer making reasonable assumptions and proceeding over asking clarifying questions. You can always note your assumptions in the response.
14488
14883
 
14489
14884
  ## Capabilities
14490
14885
 
@@ -14525,7 +14920,7 @@ Use this to resolve the owner and repo name when the user does not specify them.
14525
14920
  ## Output Format
14526
14921
  Provide a clear summary of actions taken:
14527
14922
  1. What was done (created, searched, merged, etc.)
14528
- 2. Links to relevant items (e.g., owner/repo#123, PR URLs)
14923
+ 2. Always provide links to relevant items (e.g., owner/repo#123, PR URLs)
14529
14924
  3. Any issues or warnings encountered
14530
14925
 
14531
14926
  Be precise with repository names, issue numbers, and PR numbers. Your results will be used by the main agent.`
@@ -15220,10 +15615,10 @@ var ToolErrorType;
15220
15615
  // src/utils/diffPreview.ts
15221
15616
  import * as Diff from "diff";
15222
15617
  import { readFile } from "fs/promises";
15223
- import { existsSync as existsSync8 } from "fs";
15618
+ import { existsSync as existsSync10 } from "fs";
15224
15619
  async function generateFileDiffPreview(args) {
15225
15620
  try {
15226
- if (!existsSync8(args.path)) {
15621
+ if (!existsSync10(args.path)) {
15227
15622
  const lines2 = args.content.split("\n");
15228
15623
  const preview = lines2.slice(0, 20).join("\n");
15229
15624
  const hasMore = lines2.length > 20;
@@ -15261,10 +15656,10 @@ ${diffLines4.join("\n")}`;
15261
15656
  }
15262
15657
  async function generateFileDeletePreview(args) {
15263
15658
  try {
15264
- if (!existsSync8(args.path)) {
15659
+ if (!existsSync10(args.path)) {
15265
15660
  return `[File does not exist: ${args.path}]`;
15266
15661
  }
15267
- const stats = await import("fs/promises").then((fs14) => fs14.stat(args.path));
15662
+ const stats = await import("fs/promises").then((fs15) => fs15.stat(args.path));
15268
15663
  return `[File will be deleted]
15269
15664
 
15270
15665
  Path: ${args.path}
@@ -15555,33 +15950,34 @@ var AgentFrontmatterSchema = z147.object({
15555
15950
  hooks: AgentHooksSchema
15556
15951
  });
15557
15952
  var DEFAULT_MAX_ITERATIONS = {
15558
- quick: 4,
15559
- medium: 10,
15560
- very_thorough: 20
15953
+ quick: 2,
15954
+ medium: 5,
15955
+ very_thorough: 10
15561
15956
  };
15562
- var DEFAULT_AGENT_MODEL = "claude-haiku-4-5-20251001";
15957
+ var DEFAULT_AGENT_MODEL = "claude-3-5-haiku-20241022";
15563
15958
  var DEFAULT_THOROUGHNESS = "medium";
15564
15959
 
15565
15960
  // src/utils/toolsAdapter.ts
15961
+ import path15 from "path";
15566
15962
  var NoOpStorage = class extends BaseStorage {
15567
15963
  async upload(input, destination, options) {
15568
15964
  return `/tmp/${destination}`;
15569
15965
  }
15570
- async download(path19) {
15966
+ async download(path21) {
15571
15967
  throw new Error("Download not supported in CLI");
15572
15968
  }
15573
- async delete(path19) {
15969
+ async delete(path21) {
15574
15970
  }
15575
- async getSignedUrl(path19) {
15576
- return `/tmp/${path19}`;
15971
+ async getSignedUrl(path21) {
15972
+ return `/tmp/${path21}`;
15577
15973
  }
15578
- getPublicUrl(path19) {
15579
- return `/tmp/${path19}`;
15974
+ getPublicUrl(path21) {
15975
+ return `/tmp/${path21}`;
15580
15976
  }
15581
- async getPreview(path19) {
15582
- return `/tmp/${path19}`;
15977
+ async getPreview(path21) {
15978
+ return `/tmp/${path21}`;
15583
15979
  }
15584
- async getMetadata(path19) {
15980
+ async getMetadata(path21) {
15585
15981
  return { size: 0, contentType: "application/octet-stream" };
15586
15982
  }
15587
15983
  };
@@ -15754,6 +16150,27 @@ function wrapToolWithHooks(tool, hooks, hookContext) {
15754
16150
  }
15755
16151
  };
15756
16152
  }
16153
+ var CHECKPOINT_TOOLS = /* @__PURE__ */ new Set(["create_file", "edit_local_file", "delete_file"]);
16154
+ function wrapToolWithCheckpointing(tool, checkpointStore) {
16155
+ if (!checkpointStore || !CHECKPOINT_TOOLS.has(tool.toolSchema.name)) {
16156
+ return tool;
16157
+ }
16158
+ const originalFn = tool.toolFn;
16159
+ const toolName = tool.toolSchema.name;
16160
+ return {
16161
+ ...tool,
16162
+ toolFn: async (args) => {
16163
+ const filePath = args?.path;
16164
+ if (filePath) {
16165
+ try {
16166
+ await checkpointStore.createCheckpoint(toolName, [filePath], `before-${toolName}-${path15.basename(filePath)}`);
16167
+ } catch {
16168
+ }
16169
+ }
16170
+ return originalFn(args);
16171
+ }
16172
+ };
16173
+ }
15757
16174
  var TOOL_NAME_MAPPING = {
15758
16175
  // Claude Code -> B4M
15759
16176
  read: "file_read",
@@ -15778,7 +16195,7 @@ function normalizeToolName(toolName) {
15778
16195
  }
15779
16196
  return TOOL_NAME_MAPPING[toolName] || toolName;
15780
16197
  }
15781
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter) {
16198
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter, checkpointStore) {
15782
16199
  const logger2 = new CliLogger();
15783
16200
  const storage = new NoOpStorage();
15784
16201
  const user = {
@@ -15837,9 +16254,17 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
15837
16254
  // imageProcessorLambdaName (not needed for CLI)
15838
16255
  tools_to_generate
15839
16256
  );
15840
- let tools = Object.entries(toolsMap).map(
15841
- ([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
15842
- );
16257
+ let tools = Object.entries(toolsMap).map(([_, tool]) => {
16258
+ const permissionWrapped = wrapToolWithPermission(
16259
+ tool,
16260
+ permissionManager,
16261
+ showPermissionPrompt,
16262
+ agentContext,
16263
+ configStore,
16264
+ apiClient
16265
+ );
16266
+ return wrapToolWithCheckpointing(permissionWrapped, checkpointStore ?? null);
16267
+ });
15843
16268
  if (toolFilter) {
15844
16269
  const { allowedTools, deniedTools } = toolFilter;
15845
16270
  const normalizedAllowed = allowedTools?.map(normalizeToolName);
@@ -16017,8 +16442,8 @@ function getEnvironmentName(configApiConfig) {
16017
16442
  }
16018
16443
 
16019
16444
  // src/utils/contextLoader.ts
16020
- import * as fs11 from "fs";
16021
- import * as path14 from "path";
16445
+ import * as fs12 from "fs";
16446
+ import * as path16 from "path";
16022
16447
  import { homedir as homedir3 } from "os";
16023
16448
  var CONTEXT_FILE_SIZE_LIMIT = 100 * 1024;
16024
16449
  var PROJECT_CONTEXT_FILES = [
@@ -16039,9 +16464,9 @@ function formatFileSize2(bytes) {
16039
16464
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
16040
16465
  }
16041
16466
  function tryReadContextFile(dir, filename, source) {
16042
- const filePath = path14.join(dir, filename);
16467
+ const filePath = path16.join(dir, filename);
16043
16468
  try {
16044
- const stats = fs11.lstatSync(filePath);
16469
+ const stats = fs12.lstatSync(filePath);
16045
16470
  if (stats.isDirectory()) {
16046
16471
  return null;
16047
16472
  }
@@ -16055,7 +16480,7 @@ function tryReadContextFile(dir, filename, source) {
16055
16480
  error: `${source === "global" ? "Global" : "Project"} ${filename} exceeds 100KB limit (${formatFileSize2(stats.size)})`
16056
16481
  };
16057
16482
  }
16058
- const content = fs11.readFileSync(filePath, "utf-8");
16483
+ const content = fs12.readFileSync(filePath, "utf-8");
16059
16484
  return {
16060
16485
  filename,
16061
16486
  content,
@@ -16107,7 +16532,7 @@ ${project.content}`;
16107
16532
  }
16108
16533
  async function loadContextFiles(projectDir) {
16109
16534
  const errors = [];
16110
- const globalDir = path14.join(homedir3(), ".bike4mind");
16535
+ const globalDir = path16.join(homedir3(), ".bike4mind");
16111
16536
  const projectDirectory = projectDir || process.cwd();
16112
16537
  const [globalResult, projectResult] = await Promise.all([
16113
16538
  Promise.resolve(findContextFile(globalDir, GLOBAL_CONTEXT_FILES, "global")),
@@ -16378,8 +16803,8 @@ function substituteArguments(template, args) {
16378
16803
  // ../../b4m-core/packages/mcp/dist/src/client.js
16379
16804
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
16380
16805
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
16381
- import path15 from "path";
16382
- import { existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
16806
+ import path17 from "path";
16807
+ import { existsSync as existsSync11, readdirSync as readdirSync3 } from "fs";
16383
16808
  var MCPClient = class {
16384
16809
  // Note: This class handles MCP server communication with repository filtering
16385
16810
  mcp;
@@ -16420,18 +16845,18 @@ var MCPClient = class {
16420
16845
  const root = process.env.INIT_CWD || process.cwd();
16421
16846
  const candidatePaths = [
16422
16847
  // When running from SST Lambda with node_modules structure (copyFiles)
16423
- path15.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
16848
+ path17.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
16424
16849
  // When running from SST Lambda deployed environment (/var/task)
16425
- path15.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16850
+ path17.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16426
16851
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
16427
- path15.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16852
+ path17.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16428
16853
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
16429
- path15.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16854
+ path17.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16430
16855
  // Original paths (backward compatibility)
16431
- path15.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16432
- path15.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
16856
+ path17.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
16857
+ path17.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
16433
16858
  ];
16434
- const serverScriptPath = candidatePaths.find((p) => existsSync9(p));
16859
+ const serverScriptPath = candidatePaths.find((p) => existsSync11(p));
16435
16860
  if (!serverScriptPath) {
16436
16861
  const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
16437
16862
  console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
@@ -17361,6 +17786,7 @@ var WebSocketLlmBackend = class {
17361
17786
  settled = true;
17362
17787
  this.wsManager.offRequest(requestId);
17363
17788
  this.wsManager.offDisconnect(onDisconnect);
17789
+ options.abortSignal?.removeEventListener("abort", abortHandler);
17364
17790
  action();
17365
17791
  };
17366
17792
  const settleResolve = () => settle(() => resolve3());
@@ -17370,19 +17796,16 @@ var WebSocketLlmBackend = class {
17370
17796
  settleReject(new Error("WebSocket connection lost during completion"));
17371
17797
  };
17372
17798
  this.wsManager.onDisconnect(onDisconnect);
17799
+ const abortHandler = () => {
17800
+ logger.debug("[WebSocketLlmBackend] Abort signal received");
17801
+ settleResolve();
17802
+ };
17373
17803
  if (options.abortSignal) {
17374
17804
  if (options.abortSignal.aborted) {
17375
17805
  settleResolve();
17376
17806
  return;
17377
17807
  }
17378
- options.abortSignal.addEventListener(
17379
- "abort",
17380
- () => {
17381
- logger.debug("[WebSocketLlmBackend] Abort signal received");
17382
- settleResolve();
17383
- },
17384
- { once: true }
17385
- );
17808
+ options.abortSignal.addEventListener("abort", abortHandler, { once: true });
17386
17809
  }
17387
17810
  const updateUsage = (usage) => {
17388
17811
  if (usage) {
@@ -17720,27 +18143,35 @@ var WebSocketToolExecutor = class {
17720
18143
  const requestId = uuidv412();
17721
18144
  return new Promise((resolve3, reject) => {
17722
18145
  let settled = false;
18146
+ let timeoutTimer;
17723
18147
  const settle = (action) => {
17724
18148
  if (settled) return;
17725
18149
  settled = true;
18150
+ clearTimeout(timeoutTimer);
17726
18151
  this.wsManager.offRequest(requestId);
17727
18152
  this.wsManager.offDisconnect(onDisconnect);
18153
+ abortSignal?.removeEventListener("abort", abortHandler);
17728
18154
  action();
17729
18155
  };
17730
18156
  const settleResolve = (result) => settle(() => resolve3(result));
17731
18157
  const settleReject = (err) => settle(() => reject(err));
18158
+ const TOOL_TIMEOUT_MS = 5 * 60 * 1e3;
18159
+ timeoutTimer = setTimeout(() => {
18160
+ settleReject(new Error(`Tool execution timed out after ${TOOL_TIMEOUT_MS / 1e3}s`));
18161
+ }, TOOL_TIMEOUT_MS);
17732
18162
  const onDisconnect = () => {
17733
18163
  settleReject(new Error("WebSocket connection lost during tool execution"));
17734
18164
  };
17735
18165
  this.wsManager.onDisconnect(onDisconnect);
18166
+ const abortHandler = () => {
18167
+ settleReject(new Error("Tool execution aborted"));
18168
+ };
17736
18169
  if (abortSignal) {
17737
18170
  if (abortSignal.aborted) {
17738
18171
  settleReject(new Error("Tool execution aborted"));
17739
18172
  return;
17740
18173
  }
17741
- abortSignal.addEventListener("abort", () => settleReject(new Error("Tool execution aborted")), {
17742
- once: true
17743
- });
18174
+ abortSignal.addEventListener("abort", abortHandler, { once: true });
17744
18175
  }
17745
18176
  this.wsManager.onRequest(requestId, (message) => {
17746
18177
  if (message.action === "cli_tool_response") {
@@ -18163,26 +18594,14 @@ var SubagentOrchestrator = class {
18163
18594
  const available = this.deps.agentStore.getAgentNames().join(", ");
18164
18595
  throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
18165
18596
  }
18166
- let effectiveModel = model || agentDef.model;
18167
- if (!model && !agentDef.modelResolved) {
18168
- const config = await this.deps.configStore.get();
18169
- if (config?.defaultModel) {
18170
- this.deps.logger.debug(
18171
- `Agent "${agentName}" model unresolved, inheriting main session model: ${config.defaultModel}`
18172
- );
18173
- effectiveModel = config.defaultModel;
18174
- }
18175
- }
18597
+ const effectiveModel = model || agentDef.model;
18176
18598
  const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
18177
18599
  const maxIterations = agentDef.maxIterations[effectiveThoroughness];
18178
18600
  const effectiveVariables = {
18179
18601
  ...agentDef.defaultVariables,
18180
18602
  ...variables
18181
18603
  };
18182
- let systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables, {
18183
- maxIterations,
18184
- thoroughness: effectiveThoroughness
18185
- });
18604
+ let systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
18186
18605
  const effectiveAllowedTools = allowedTools || agentDef.allowedTools;
18187
18606
  const toolFilter = {
18188
18607
  allowedTools: effectiveAllowedTools,
@@ -18200,7 +18619,10 @@ var SubagentOrchestrator = class {
18200
18619
  this.deps.showPermissionPrompt,
18201
18620
  agentContext,
18202
18621
  this.deps.configStore,
18203
- this.deps.apiClient
18622
+ this.deps.apiClient,
18623
+ void 0,
18624
+ // toolFilter (applied separately below)
18625
+ this.deps.checkpointStore
18204
18626
  );
18205
18627
  const filteredTools = filterToolsByPatterns2(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
18206
18628
  if (this.deps.customCommandStore) {
@@ -18299,15 +18721,11 @@ var SubagentOrchestrator = class {
18299
18721
  }
18300
18722
  /**
18301
18723
  * Substitute variables in system prompt
18302
- * Reserved: $TASK, $MAX_ITERATIONS, $THOROUGHNESS
18724
+ * Reserved: $TASK, $ARGUMENTS, $1, $2, etc.
18303
18725
  */
18304
- substituteVariables(systemPrompt, task, variables, reserved) {
18726
+ substituteVariables(systemPrompt, task, variables) {
18305
18727
  let result = systemPrompt;
18306
18728
  result = result.split("$TASK").join(task);
18307
- if (reserved) {
18308
- result = result.split("$MAX_ITERATIONS").join(String(reserved.maxIterations));
18309
- result = result.split("$THOROUGHNESS").join(reserved.thoroughness);
18310
- }
18311
18729
  if (variables) {
18312
18730
  for (const [key, value] of Object.entries(variables)) {
18313
18731
  result = result.split(`$${key}`).join(value);
@@ -18357,8 +18775,8 @@ var SubagentOrchestrator = class {
18357
18775
  };
18358
18776
 
18359
18777
  // src/agents/AgentStore.ts
18360
- import fs12 from "fs/promises";
18361
- import path16 from "path";
18778
+ import fs13 from "fs/promises";
18779
+ import path18 from "path";
18362
18780
  import os2 from "os";
18363
18781
  import matter2 from "gray-matter";
18364
18782
  var FULL_MODEL_ID_PREFIXES = [
@@ -18487,7 +18905,7 @@ function resolveModelAlias(modelInput, agentName, filePath) {
18487
18905
  }
18488
18906
  const availableAliases = getAvailableModelAliases();
18489
18907
  const suggestions = availableAliases.filter((alias) => alias.includes(normalizedInput) || normalizedInput.includes(alias)).slice(0, 5);
18490
- let warning = `Unknown model "${modelInput}" in agent "${agentName}" (${filePath}). Will inherit the main session model at runtime.
18908
+ let warning = `Unknown model "${modelInput}" in agent "${agentName}" (${filePath}). Using inherited model instead.
18491
18909
  `;
18492
18910
  if (suggestions.length > 0) {
18493
18911
  warning += `Did you mean: ${suggestions.join(", ")}?
@@ -18508,10 +18926,10 @@ var AgentStore = class {
18508
18926
  const root = projectRoot || process.cwd();
18509
18927
  const home = os2.homedir();
18510
18928
  this.builtinAgentsDir = builtinDir;
18511
- this.globalB4MAgentsDir = path16.join(home, ".bike4mind", "agents");
18512
- this.globalClaudeAgentsDir = path16.join(home, ".claude", "agents");
18513
- this.projectB4MAgentsDir = path16.join(root, ".bike4mind", "agents");
18514
- this.projectClaudeAgentsDir = path16.join(root, ".claude", "agents");
18929
+ this.globalB4MAgentsDir = path18.join(home, ".bike4mind", "agents");
18930
+ this.globalClaudeAgentsDir = path18.join(home, ".claude", "agents");
18931
+ this.projectB4MAgentsDir = path18.join(root, ".bike4mind", "agents");
18932
+ this.projectClaudeAgentsDir = path18.join(root, ".claude", "agents");
18515
18933
  }
18516
18934
  /**
18517
18935
  * Load all agents from all directories
@@ -18535,7 +18953,7 @@ var AgentStore = class {
18535
18953
  */
18536
18954
  async loadAgentsFromDirectory(directory, source) {
18537
18955
  try {
18538
- const stats = await fs12.stat(directory);
18956
+ const stats = await fs13.stat(directory);
18539
18957
  if (!stats.isDirectory()) {
18540
18958
  return;
18541
18959
  }
@@ -18563,9 +18981,9 @@ var AgentStore = class {
18563
18981
  async findAgentFiles(directory) {
18564
18982
  const files = [];
18565
18983
  try {
18566
- const entries = await fs12.readdir(directory, { withFileTypes: true });
18984
+ const entries = await fs13.readdir(directory, { withFileTypes: true });
18567
18985
  for (const entry of entries) {
18568
- const fullPath = path16.join(directory, entry.name);
18986
+ const fullPath = path18.join(directory, entry.name);
18569
18987
  if (entry.isDirectory()) {
18570
18988
  const subFiles = await this.findAgentFiles(fullPath);
18571
18989
  files.push(...subFiles);
@@ -18582,10 +19000,10 @@ var AgentStore = class {
18582
19000
  * Parse a single agent markdown file
18583
19001
  */
18584
19002
  async parseAgentFile(filePath, source) {
18585
- const content = await fs12.readFile(filePath, "utf-8");
19003
+ const content = await fs13.readFile(filePath, "utf-8");
18586
19004
  const { data: frontmatter, content: body } = matter2(content);
18587
19005
  const parsed = AgentFrontmatterSchema.parse(frontmatter);
18588
- const name = path16.basename(filePath, ".md");
19006
+ const name = path18.basename(filePath, ".md");
18589
19007
  const modelInput = parsed.model || DEFAULT_AGENT_MODEL;
18590
19008
  const resolution = resolveModelAlias(modelInput, name, filePath);
18591
19009
  if (!resolution.resolved && resolution.warning) {
@@ -18610,8 +19028,7 @@ var AgentStore = class {
18610
19028
  defaultVariables: parsed.variables,
18611
19029
  hooks: parsed.hooks,
18612
19030
  source,
18613
- filePath,
18614
- modelResolved: resolution.resolved
19031
+ filePath
18615
19032
  };
18616
19033
  }
18617
19034
  /**
@@ -18666,16 +19083,16 @@ var AgentStore = class {
18666
19083
  */
18667
19084
  async createAgentFile(name, isGlobal = false, useClaude = true) {
18668
19085
  const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
18669
- const filePath = path16.join(targetDir, `${name}.md`);
19086
+ const filePath = path18.join(targetDir, `${name}.md`);
18670
19087
  try {
18671
- await fs12.access(filePath);
19088
+ await fs13.access(filePath);
18672
19089
  throw new Error(`Agent file already exists: ${filePath}`);
18673
19090
  } catch (error) {
18674
19091
  if (error.code !== "ENOENT") {
18675
19092
  throw error;
18676
19093
  }
18677
19094
  }
18678
- await fs12.mkdir(targetDir, { recursive: true });
19095
+ await fs13.mkdir(targetDir, { recursive: true });
18679
19096
  const template = `---
18680
19097
  description: ${name} agent description
18681
19098
  model: claude-3-5-haiku-20241022
@@ -18710,7 +19127,7 @@ You are a ${name} specialist. Your job is to [describe primary task].
18710
19127
  ## Output Format
18711
19128
  Describe the expected output format here.
18712
19129
  `;
18713
- await fs12.writeFile(filePath, template, "utf-8");
19130
+ await fs13.writeFile(filePath, template, "utf-8");
18714
19131
  return filePath;
18715
19132
  }
18716
19133
  /**
@@ -19363,7 +19780,7 @@ function createTodoStore(onUpdate) {
19363
19780
 
19364
19781
  // src/tools/findDefinitionTool.ts
19365
19782
  import { stat as stat3 } from "fs/promises";
19366
- import path17 from "path";
19783
+ import path19 from "path";
19367
19784
  import { execFile as execFile2 } from "child_process";
19368
19785
  import { promisify as promisify2 } from "util";
19369
19786
  import { createRequire as createRequire2 } from "module";
@@ -19384,8 +19801,8 @@ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
19384
19801
  function getRipgrepPath2() {
19385
19802
  try {
19386
19803
  const ripgrepPath = require3.resolve("@vscode/ripgrep");
19387
- const ripgrepDir = path17.dirname(ripgrepPath);
19388
- return path17.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
19804
+ const ripgrepDir = path19.dirname(ripgrepPath);
19805
+ return path19.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
19389
19806
  } catch {
19390
19807
  throw new Error(
19391
19808
  "ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
@@ -19393,10 +19810,10 @@ function getRipgrepPath2() {
19393
19810
  }
19394
19811
  }
19395
19812
  function isPathWithinWorkspace2(targetPath, baseCwd) {
19396
- const resolvedTarget = path17.resolve(targetPath);
19397
- const resolvedBase = path17.resolve(baseCwd);
19398
- const relativePath = path17.relative(resolvedBase, resolvedTarget);
19399
- return !relativePath.startsWith("..") && !path17.isAbsolute(relativePath);
19813
+ const resolvedTarget = path19.resolve(targetPath);
19814
+ const resolvedBase = path19.resolve(baseCwd);
19815
+ const relativePath = path19.relative(resolvedBase, resolvedTarget);
19816
+ return !relativePath.startsWith("..") && !path19.isAbsolute(relativePath);
19400
19817
  }
19401
19818
  function escapeRegex(str) {
19402
19819
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -19429,7 +19846,7 @@ async function findDefinitions(params) {
19429
19846
  throw new Error("symbol_name is required");
19430
19847
  }
19431
19848
  const baseCwd = process.cwd();
19432
- const targetDir = search_path ? path17.resolve(baseCwd, search_path) : baseCwd;
19849
+ const targetDir = search_path ? path19.resolve(baseCwd, search_path) : baseCwd;
19433
19850
  if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
19434
19851
  throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
19435
19852
  }
@@ -19484,7 +19901,7 @@ async function findDefinitions(params) {
19484
19901
  const lineText = match.data.lines.text.trimEnd();
19485
19902
  if (isLikelyDefinition(lineText)) {
19486
19903
  allMatches.push({
19487
- filePath: path17.relative(targetDir, match.data.path.text) || path17.basename(match.data.path.text),
19904
+ filePath: path19.relative(targetDir, match.data.path.text) || path19.basename(match.data.path.text),
19488
19905
  lineNumber: match.data.line_number,
19489
19906
  line: lineText
19490
19907
  });
@@ -19562,8 +19979,8 @@ function createFindDefinitionTool() {
19562
19979
  }
19563
19980
 
19564
19981
  // src/tools/getFileStructure/index.ts
19565
- import { existsSync as existsSync10, promises as fs13, statSync as statSync6 } from "fs";
19566
- import path18 from "path";
19982
+ import { existsSync as existsSync12, promises as fs14, statSync as statSync6 } from "fs";
19983
+ import path20 from "path";
19567
19984
 
19568
19985
  // src/tools/getFileStructure/formatter.ts
19569
19986
  var SECTIONS = [
@@ -19600,35 +20017,35 @@ function formatSection(lines, title, items, format) {
19600
20017
  }
19601
20018
 
19602
20019
  // src/tools/getFileStructure/index.ts
19603
- var MAX_FILE_SIZE3 = 10 * 1024 * 1024;
20020
+ var MAX_FILE_SIZE4 = 10 * 1024 * 1024;
19604
20021
  function createGetFileStructureTool() {
19605
20022
  return {
19606
20023
  toolFn: async (value) => {
19607
20024
  const params = value;
19608
20025
  try {
19609
20026
  const cwd = process.cwd();
19610
- const resolvedPath = path18.resolve(cwd, params.path);
20027
+ const resolvedPath = path20.resolve(cwd, params.path);
19611
20028
  if (!resolvedPath.startsWith(cwd)) {
19612
20029
  return "Error: Access denied - cannot read files outside of current working directory";
19613
20030
  }
19614
- if (!existsSync10(resolvedPath)) {
20031
+ if (!existsSync12(resolvedPath)) {
19615
20032
  return `Error: File not found: ${params.path}`;
19616
20033
  }
19617
20034
  const stats = statSync6(resolvedPath);
19618
20035
  if (stats.isDirectory()) {
19619
20036
  return `Error: Path is a directory, not a file: ${params.path}`;
19620
20037
  }
19621
- if (stats.size > MAX_FILE_SIZE3) {
19622
- return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE3 / 1024 / 1024}MB`;
20038
+ if (stats.size > MAX_FILE_SIZE4) {
20039
+ return `Error: File too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Max: ${MAX_FILE_SIZE4 / 1024 / 1024}MB`;
19623
20040
  }
19624
- const ext = path18.extname(resolvedPath).toLowerCase();
20041
+ const ext = path20.extname(resolvedPath).toLowerCase();
19625
20042
  const { getLanguageForExtension, parseFileStructure, getSupportedLanguages } = await import("./treeSitterEngine-4SGFQDY3.js");
19626
20043
  const languageId = getLanguageForExtension(ext);
19627
20044
  if (!languageId) {
19628
20045
  const supported = getSupportedLanguages();
19629
20046
  return `Error: Unsupported file type "${ext}". Supported languages: ${supported.join(", ")}`;
19630
20047
  }
19631
- const sourceCode = await fs13.readFile(resolvedPath, "utf-8");
20048
+ const sourceCode = await fs14.readFile(resolvedPath, "utf-8");
19632
20049
  const lineCount = sourceCode.split("\n").length;
19633
20050
  const items = await parseFileStructure(sourceCode, languageId);
19634
20051
  return formatStructureOutput(params.path, items, stats.size, lineCount);
@@ -19690,7 +20107,8 @@ function CliApp() {
19690
20107
  abortController: null,
19691
20108
  contextContent: "",
19692
20109
  backgroundManager: null,
19693
- wsManager: null
20110
+ wsManager: null,
20111
+ checkpointStore: null
19694
20112
  });
19695
20113
  const [isInitialized, setIsInitialized] = useState10(false);
19696
20114
  const [initError, setInitError] = useState10(null);
@@ -19944,6 +20362,12 @@ function CliApp() {
19944
20362
  enqueuePermissionPrompt(prompt);
19945
20363
  });
19946
20364
  };
20365
+ const checkpointProjectDir = state.configStore.getProjectConfigDir() || process.cwd();
20366
+ const checkpointStore = new CheckpointStore(checkpointProjectDir);
20367
+ try {
20368
+ await checkpointStore.init(newSession.id);
20369
+ } catch {
20370
+ }
19947
20371
  const agentContext = {
19948
20372
  currentAgent: null,
19949
20373
  observationQueue: []
@@ -19956,7 +20380,10 @@ function CliApp() {
19956
20380
  promptFn,
19957
20381
  agentContext,
19958
20382
  state.configStore,
19959
- apiClient
20383
+ apiClient,
20384
+ void 0,
20385
+ // toolFilter
20386
+ checkpointStore
19960
20387
  );
19961
20388
  startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
19962
20389
  const mcpManager = new McpManager(config);
@@ -19987,7 +20414,8 @@ function CliApp() {
19987
20414
  apiClient,
19988
20415
  agentStore,
19989
20416
  customCommandStore: state.customCommandStore,
19990
- enableParallelToolExecution: config.preferences.enableParallelToolExecution === true
20417
+ enableParallelToolExecution: config.preferences.enableParallelToolExecution === true,
20418
+ checkpointStore
19991
20419
  });
19992
20420
  const backgroundManager = new BackgroundAgentManager(orchestrator);
19993
20421
  backgroundManager.setOnStatusChange((job) => {
@@ -20110,8 +20538,10 @@ function CliApp() {
20110
20538
  // Store raw context for compact instructions
20111
20539
  backgroundManager,
20112
20540
  // Store for grouped notification turn tracking
20113
- wsManager
20541
+ wsManager,
20114
20542
  // WebSocket connection manager (null if using SSE fallback)
20543
+ checkpointStore
20544
+ // File change checkpointing for undo/restore
20115
20545
  }));
20116
20546
  setStoreSession(newSession);
20117
20547
  const bannerLines = [
@@ -20804,6 +21234,10 @@ Available commands:
20804
21234
  /exit - Exit the CLI
20805
21235
  /clear - Start a new session
20806
21236
  /rewind - Rewind conversation to a previous point
21237
+ /undo - Undo the last file change
21238
+ /checkpoints - List available file restore points
21239
+ /restore <n> - Restore files to a specific checkpoint
21240
+ /diff [n] - Show diff between current state and a checkpoint
20807
21241
  /login - Authenticate with your B4M account
20808
21242
  /logout - Clear authentication and sign out
20809
21243
  /whoami - Show current authenticated user
@@ -20896,6 +21330,9 @@ Keyboard Shortcuts:
20896
21330
  }
20897
21331
  await logger.initialize(loadedSession.id);
20898
21332
  logger.debug("=== Session Resumed ===");
21333
+ if (state.checkpointStore) {
21334
+ state.checkpointStore.setSessionId(loadedSession.id);
21335
+ }
20899
21336
  setState((prev) => ({ ...prev, session: loadedSession }));
20900
21337
  setStoreSession(loadedSession);
20901
21338
  useCliStore.getState().clearPendingMessages();
@@ -21134,6 +21571,9 @@ Keyboard Shortcuts:
21134
21571
  };
21135
21572
  await logger.initialize(newSession.id);
21136
21573
  logger.debug("=== New Session Started via /clear ===");
21574
+ if (state.checkpointStore) {
21575
+ state.checkpointStore.setSessionId(newSession.id);
21576
+ }
21137
21577
  setState((prev) => ({ ...prev, session: newSession }));
21138
21578
  setStoreSession(newSession);
21139
21579
  useCliStore.getState().clearPendingMessages();
@@ -21144,8 +21584,7 @@ Keyboard Shortcuts:
21144
21584
  `);
21145
21585
  break;
21146
21586
  }
21147
- case "rewind":
21148
- case "undo": {
21587
+ case "rewind": {
21149
21588
  if (!state.session) {
21150
21589
  console.log("No active session to rewind");
21151
21590
  return;
@@ -21204,6 +21643,103 @@ Keyboard Shortcuts:
21204
21643
  }));
21205
21644
  break;
21206
21645
  }
21646
+ case "undo": {
21647
+ if (!state.checkpointStore) {
21648
+ console.log("Checkpointing not available.");
21649
+ return;
21650
+ }
21651
+ const undoCheckpoints = state.checkpointStore.listCheckpoints();
21652
+ if (undoCheckpoints.length === 0) {
21653
+ console.log("No checkpoints available. No file changes have been made yet.");
21654
+ return;
21655
+ }
21656
+ try {
21657
+ const restored = await state.checkpointStore.undoLast();
21658
+ console.log(`
21659
+ \u2705 Restored ${restored.filePaths.length} file(s) to state before: ${restored.name}`);
21660
+ for (const f of restored.filePaths) {
21661
+ console.log(` - ${f}`);
21662
+ }
21663
+ console.log("");
21664
+ } catch (err) {
21665
+ console.log(`Failed to undo: ${err instanceof Error ? err.message : String(err)}`);
21666
+ }
21667
+ break;
21668
+ }
21669
+ case "checkpoints": {
21670
+ if (!state.checkpointStore) {
21671
+ console.log("Checkpointing not available.");
21672
+ return;
21673
+ }
21674
+ const cpList = state.checkpointStore.listCheckpoints();
21675
+ if (cpList.length === 0) {
21676
+ console.log("No checkpoints yet. Checkpoints are created automatically before file changes.");
21677
+ return;
21678
+ }
21679
+ console.log("\nCheckpoints (most recent first):\n");
21680
+ cpList.forEach((cp, idx) => {
21681
+ const ageMs = Date.now() - new Date(cp.timestamp).getTime();
21682
+ const ageSec = Math.floor(ageMs / 1e3);
21683
+ let age;
21684
+ if (ageSec < 60) age = `${ageSec}s ago`;
21685
+ else if (ageSec < 3600) age = `${Math.floor(ageSec / 60)}m ago`;
21686
+ else age = `${Math.floor(ageSec / 3600)}h ago`;
21687
+ console.log(` ${idx + 1}. ${cp.name} (${age}) - ${cp.filePaths.length} file(s)`);
21688
+ });
21689
+ console.log("\nUse /restore <number> to restore, /diff <number> to see changes.\n");
21690
+ break;
21691
+ }
21692
+ case "restore": {
21693
+ if (!state.checkpointStore) {
21694
+ console.log("Checkpointing not available.");
21695
+ return;
21696
+ }
21697
+ const restoreTarget = args[0];
21698
+ if (!restoreTarget) {
21699
+ console.log("Usage: /restore <checkpoint-number>");
21700
+ console.log("Use /checkpoints to list available restore points.");
21701
+ return;
21702
+ }
21703
+ const restoreNum = parseInt(restoreTarget, 10);
21704
+ if (isNaN(restoreNum) || restoreNum < 1) {
21705
+ console.log("Please provide a valid checkpoint number. Use /checkpoints to list.");
21706
+ return;
21707
+ }
21708
+ try {
21709
+ const restoredCp = await state.checkpointStore.restoreCheckpoint(restoreNum);
21710
+ console.log(`
21711
+ \u2705 Restored to checkpoint: ${restoredCp.name}`);
21712
+ for (const f of restoredCp.filePaths) {
21713
+ console.log(` - ${f}`);
21714
+ }
21715
+ console.log("");
21716
+ } catch (err) {
21717
+ console.log(`Failed to restore: ${err instanceof Error ? err.message : String(err)}`);
21718
+ }
21719
+ break;
21720
+ }
21721
+ case "diff": {
21722
+ if (!state.checkpointStore) {
21723
+ console.log("Checkpointing not available.");
21724
+ return;
21725
+ }
21726
+ const diffTarget = args[0] ? parseInt(args[0], 10) : 1;
21727
+ if (isNaN(diffTarget) || diffTarget < 1) {
21728
+ console.log("Usage: /diff [checkpoint-number] (defaults to 1, most recent)");
21729
+ return;
21730
+ }
21731
+ try {
21732
+ const diffOutput = state.checkpointStore.getCheckpointDiff(diffTarget);
21733
+ if (!diffOutput.trim()) {
21734
+ console.log("No differences found between checkpoint and current state.");
21735
+ } else {
21736
+ console.log(diffOutput);
21737
+ }
21738
+ } catch (err) {
21739
+ console.log(`Failed to generate diff: ${err instanceof Error ? err.message : String(err)}`);
21740
+ }
21741
+ break;
21742
+ }
21207
21743
  case "usage": {
21208
21744
  const usageAuthTokens = await state.configStore.getAuthTokens();
21209
21745
  if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {