@bike4mind/cli 0.2.26-refactor-dry-credit-deduction.18613 → 0.2.26

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
@@ -5,7 +5,7 @@ import {
5
5
  getEffectiveApiKey,
6
6
  getOpenWeatherKey,
7
7
  getSerperKey
8
- } from "./chunk-OHEBSPEW.js";
8
+ } from "./chunk-HVF6LOKH.js";
9
9
  import {
10
10
  ConfigStore
11
11
  } from "./chunk-23T2XGSZ.js";
@@ -13,8 +13,8 @@ import {
13
13
  selectActiveBackgroundAgents,
14
14
  useCliStore
15
15
  } from "./chunk-TVW4ZESU.js";
16
- import "./chunk-Z7PQXS5G.js";
17
- import "./chunk-3EQCYEKW.js";
16
+ import "./chunk-UA7GLVP3.js";
17
+ import "./chunk-QD65IUNH.js";
18
18
  import {
19
19
  BFLImageService,
20
20
  BaseStorage,
@@ -26,7 +26,7 @@ import {
26
26
  OpenAIBackend,
27
27
  OpenAIImageService,
28
28
  XAIImageService
29
- } from "./chunk-I7ZMNVAN.js";
29
+ } from "./chunk-MMAOFFE3.js";
30
30
  import {
31
31
  AiEvents,
32
32
  ApiKeyEvents,
@@ -82,7 +82,7 @@ import {
82
82
  XAI_IMAGE_MODELS,
83
83
  b4mLLMTools,
84
84
  getMcpProviderMetadata
85
- } from "./chunk-L42V7DCM.js";
85
+ } from "./chunk-F3HPUK2I.js";
86
86
  import {
87
87
  Logger
88
88
  } from "./chunk-OCYRD7D6.js";
@@ -3783,6 +3783,7 @@ var DEFAULT_TOOL_CATEGORIES = {
3783
3783
  current_datetime: "auto_approve",
3784
3784
  dice_roll: "auto_approve",
3785
3785
  prompt_enhancement: "auto_approve",
3786
+ find_definition: "auto_approve",
3786
3787
  weather_info: "prompt_default",
3787
3788
  // ===== PROMPT ALWAYS (Dangerous tools) =====
3788
3789
  // These tools can modify files, execute code, or have other dangerous side effects
@@ -6057,8 +6058,8 @@ async function processAndStoreImages(images, context) {
6057
6058
  const buffer = await downloadImage(image);
6058
6059
  const fileType = await fileTypeFromBuffer2(buffer);
6059
6060
  const filename = `${uuidv45()}.${fileType?.ext}`;
6060
- const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
6061
- return path17;
6061
+ const path18 = await context.imageGenerateStorage.upload(buffer, filename, {});
6062
+ return path18;
6062
6063
  }));
6063
6064
  }
6064
6065
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -7373,8 +7374,8 @@ async function processAndStoreImage(imageUrl, context) {
7373
7374
  const buffer = await downloadImage2(imageUrl);
7374
7375
  const fileType = await fileTypeFromBuffer3(buffer);
7375
7376
  const filename = `${uuidv46()}.${fileType?.ext}`;
7376
- const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
7377
- return path17;
7377
+ const path18 = await context.imageGenerateStorage.upload(buffer, filename, {});
7378
+ return path18;
7378
7379
  }
7379
7380
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
7380
7381
  await context.onFinish?.("edit_image", storedImagePath);
@@ -10512,9 +10513,9 @@ async function getFileStats(files, since, filterPath) {
10512
10513
  }
10513
10514
  });
10514
10515
  const result = files.map((file) => {
10515
- const stat3 = stats.get(file.path);
10516
- if (stat3) {
10517
- return { ...file, ...stat3 };
10516
+ const stat4 = stats.get(file.path);
10517
+ if (stat4) {
10518
+ return { ...file, ...stat4 };
10518
10519
  }
10519
10520
  return file;
10520
10521
  });
@@ -10867,6 +10868,486 @@ var QuestStartBodySchema = z139.object({
10867
10868
  // ../../b4m-core/packages/services/dist/src/llm/StatusManager.js
10868
10869
  import throttle from "lodash/throttle.js";
10869
10870
 
10871
+ // ../../b4m-core/packages/services/dist/src/conversationContextService/types.js
10872
+ var CONVERSATION_CONTEXT_DEFAULTS = {
10873
+ maxEntitiesPerType: 20,
10874
+ entityTTLMs: 60 * 60 * 1e3
10875
+ // 1 hour
10876
+ };
10877
+
10878
+ // ../../b4m-core/packages/services/dist/src/conversationContextService/extractors/github.js
10879
+ var GITHUB_PATTERNS = {
10880
+ // GitHub URL patterns
10881
+ // https://github.com/owner/repo
10882
+ repoUrl: /https?:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)(?:\/|$|\s|[)\]>])/gi,
10883
+ // https://github.com/owner/repo/pull/123
10884
+ prUrl: /https?:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)\/pull\/(\d+)/gi,
10885
+ // https://github.com/owner/repo/issues/123
10886
+ issueUrl: /https?:\/\/github\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)\/issues\/(\d+)/gi,
10887
+ // owner/repo format (e.g., "milliononmars/lumina5")
10888
+ // Must not be preceded by @ (to avoid email-like patterns)
10889
+ // Must not be followed by common file extensions
10890
+ repoSlash: /(?<![/@])([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)(?!\.(?:js|ts|tsx|jsx|json|md|css|html|py|go|rs|java|rb|php|c|cpp|h|hpp))/g
10891
+ };
10892
+ var GitHubExtractor = class {
10893
+ /**
10894
+ * Extract GitHub entities from text
10895
+ */
10896
+ extract(text, source) {
10897
+ const entities = [];
10898
+ const seenRepos = /* @__PURE__ */ new Set();
10899
+ const seenPRs = /* @__PURE__ */ new Set();
10900
+ const seenIssues = /* @__PURE__ */ new Set();
10901
+ let match;
10902
+ GITHUB_PATTERNS.prUrl.lastIndex = 0;
10903
+ while ((match = GITHUB_PATTERNS.prUrl.exec(text)) !== null) {
10904
+ const [, owner, repo, number] = match;
10905
+ const key = `${owner}/${repo}#${number}`;
10906
+ if (!seenPRs.has(key)) {
10907
+ seenPRs.add(key);
10908
+ entities.push({
10909
+ entity: {
10910
+ type: "github_pr",
10911
+ entity: { owner, repo, number: parseInt(number, 10) }
10912
+ },
10913
+ source
10914
+ });
10915
+ const repoKey = `${owner}/${repo}`;
10916
+ if (!seenRepos.has(repoKey)) {
10917
+ seenRepos.add(repoKey);
10918
+ entities.push({
10919
+ entity: {
10920
+ type: "github_repo",
10921
+ entity: { owner, repo }
10922
+ },
10923
+ source
10924
+ });
10925
+ }
10926
+ }
10927
+ }
10928
+ GITHUB_PATTERNS.issueUrl.lastIndex = 0;
10929
+ while ((match = GITHUB_PATTERNS.issueUrl.exec(text)) !== null) {
10930
+ const [, owner, repo, number] = match;
10931
+ const key = `${owner}/${repo}#${number}`;
10932
+ if (!seenIssues.has(key)) {
10933
+ seenIssues.add(key);
10934
+ entities.push({
10935
+ entity: {
10936
+ type: "github_issue",
10937
+ entity: { owner, repo, number: parseInt(number, 10) }
10938
+ },
10939
+ source
10940
+ });
10941
+ const repoKey = `${owner}/${repo}`;
10942
+ if (!seenRepos.has(repoKey)) {
10943
+ seenRepos.add(repoKey);
10944
+ entities.push({
10945
+ entity: {
10946
+ type: "github_repo",
10947
+ entity: { owner, repo }
10948
+ },
10949
+ source
10950
+ });
10951
+ }
10952
+ }
10953
+ }
10954
+ GITHUB_PATTERNS.repoUrl.lastIndex = 0;
10955
+ while ((match = GITHUB_PATTERNS.repoUrl.exec(text)) !== null) {
10956
+ const [, owner, repo] = match;
10957
+ const key = `${owner}/${repo}`;
10958
+ if (!seenRepos.has(key)) {
10959
+ seenRepos.add(key);
10960
+ entities.push({
10961
+ entity: {
10962
+ type: "github_repo",
10963
+ entity: { owner, repo: repo.replace(/\.git$/, "") }
10964
+ },
10965
+ source
10966
+ });
10967
+ }
10968
+ }
10969
+ GITHUB_PATTERNS.repoSlash.lastIndex = 0;
10970
+ while ((match = GITHUB_PATTERNS.repoSlash.exec(text)) !== null) {
10971
+ const [, owner, repo] = match;
10972
+ if (["http", "https", "git", "ssh", "ftp"].includes(owner.toLowerCase())) {
10973
+ continue;
10974
+ }
10975
+ const skipOwners = [
10976
+ "node_modules",
10977
+ "src",
10978
+ "dist",
10979
+ "build",
10980
+ "lib",
10981
+ "bin",
10982
+ // URL components that shouldn't be treated as owners
10983
+ "github",
10984
+ "com",
10985
+ "www",
10986
+ "api",
10987
+ "raw",
10988
+ "gist"
10989
+ ];
10990
+ const skipRepos = [
10991
+ // GitHub URL path segments that aren't repos
10992
+ "pull",
10993
+ "pulls",
10994
+ "issue",
10995
+ "issues",
10996
+ "blob",
10997
+ "tree",
10998
+ "commit",
10999
+ "commits",
11000
+ "compare",
11001
+ "releases",
11002
+ "tags",
11003
+ "branches",
11004
+ "actions",
11005
+ "settings",
11006
+ "wiki",
11007
+ "projects",
11008
+ "security",
11009
+ "pulse",
11010
+ "graphs",
11011
+ "network"
11012
+ ];
11013
+ if (skipOwners.includes(owner.toLowerCase())) {
11014
+ continue;
11015
+ }
11016
+ if (skipRepos.includes(repo.toLowerCase())) {
11017
+ continue;
11018
+ }
11019
+ const key = `${owner}/${repo}`;
11020
+ if (!seenRepos.has(key)) {
11021
+ seenRepos.add(key);
11022
+ entities.push({
11023
+ entity: {
11024
+ type: "github_repo",
11025
+ entity: { owner, repo: repo.replace(/\.git$/, "") }
11026
+ },
11027
+ source
11028
+ });
11029
+ }
11030
+ }
11031
+ return { entities };
11032
+ }
11033
+ };
11034
+ var githubExtractor = new GitHubExtractor();
11035
+
11036
+ // ../../b4m-core/packages/services/dist/src/conversationContextService/extractors/jira.js
11037
+ var SKIP_PROJECT_KEYS = /* @__PURE__ */ new Set([
11038
+ "AT",
11039
+ "IN",
11040
+ "ON",
11041
+ "TO",
11042
+ "OF",
11043
+ "BY",
11044
+ "IS",
11045
+ "IT",
11046
+ "OR",
11047
+ "AS",
11048
+ "AN",
11049
+ "A",
11050
+ "BE",
11051
+ "DO",
11052
+ "GO",
11053
+ "IF",
11054
+ "NO",
11055
+ "SO",
11056
+ "UP",
11057
+ "WE",
11058
+ "MY",
11059
+ "HE",
11060
+ "ME",
11061
+ "THE",
11062
+ "AND",
11063
+ "FOR",
11064
+ "ARE",
11065
+ "BUT",
11066
+ "NOT",
11067
+ "YOU",
11068
+ "ALL",
11069
+ "CAN",
11070
+ "HAD",
11071
+ "HER",
11072
+ "WAS",
11073
+ "ONE",
11074
+ "OUR",
11075
+ "OUT",
11076
+ "HAS",
11077
+ "HIS",
11078
+ "HOW",
11079
+ "ITS",
11080
+ "MAY",
11081
+ "NEW",
11082
+ "NOW",
11083
+ "OLD",
11084
+ "SEE",
11085
+ "WAY",
11086
+ "WHO",
11087
+ "BOY",
11088
+ "DID",
11089
+ "GET",
11090
+ "LET"
11091
+ ]);
11092
+ var JIRA_PATTERNS = {
11093
+ // Jira issue key format: PROJECT-123 (2-10 uppercase letters followed by hyphen and number)
11094
+ // Common Jira project key pattern
11095
+ issueKey: /\b([A-Z][A-Z0-9]{1,9})-(\d+)\b/g,
11096
+ // Jira URL patterns
11097
+ // https://company.atlassian.net/browse/PROJECT-123
11098
+ issueUrl: /https?:\/\/[a-zA-Z0-9_-]+\.atlassian\.net\/browse\/([A-Z][A-Z0-9]{1,9})-(\d+)/gi,
11099
+ // https://company.atlassian.net/jira/software/projects/PROJECT/...
11100
+ projectUrl: /https?:\/\/[a-zA-Z0-9_-]+\.atlassian\.net\/jira\/(?:software|core|servicedesk)\/projects\/([A-Z][A-Z0-9]{1,9})/gi,
11101
+ // Project key mentioned in context (e.g., "in the PROJ project")
11102
+ projectMention: /(?:project|board)\s+([A-Z][A-Z0-9]{1,9})\b/gi
11103
+ };
11104
+ var JiraExtractor = class {
11105
+ /**
11106
+ * Extract Jira entities from text
11107
+ */
11108
+ extract(text, source) {
11109
+ const entities = [];
11110
+ const seenProjects = /* @__PURE__ */ new Set();
11111
+ const seenIssues = /* @__PURE__ */ new Set();
11112
+ let match;
11113
+ JIRA_PATTERNS.issueUrl.lastIndex = 0;
11114
+ while ((match = JIRA_PATTERNS.issueUrl.exec(text)) !== null) {
11115
+ const [, projectKey, number] = match;
11116
+ const issueKey = `${projectKey.toUpperCase()}-${number}`;
11117
+ if (!seenIssues.has(issueKey)) {
11118
+ seenIssues.add(issueKey);
11119
+ entities.push({
11120
+ entity: {
11121
+ type: "jira_issue",
11122
+ entity: { key: issueKey }
11123
+ },
11124
+ source
11125
+ });
11126
+ const projectKeyUpper = projectKey.toUpperCase();
11127
+ if (!seenProjects.has(projectKeyUpper)) {
11128
+ seenProjects.add(projectKeyUpper);
11129
+ entities.push({
11130
+ entity: {
11131
+ type: "jira_project",
11132
+ entity: { key: projectKeyUpper }
11133
+ },
11134
+ source
11135
+ });
11136
+ }
11137
+ }
11138
+ }
11139
+ JIRA_PATTERNS.projectUrl.lastIndex = 0;
11140
+ while ((match = JIRA_PATTERNS.projectUrl.exec(text)) !== null) {
11141
+ const [, projectKey] = match;
11142
+ const projectKeyUpper = projectKey.toUpperCase();
11143
+ if (!seenProjects.has(projectKeyUpper)) {
11144
+ seenProjects.add(projectKeyUpper);
11145
+ entities.push({
11146
+ entity: {
11147
+ type: "jira_project",
11148
+ entity: { key: projectKeyUpper }
11149
+ },
11150
+ source
11151
+ });
11152
+ }
11153
+ }
11154
+ JIRA_PATTERNS.issueKey.lastIndex = 0;
11155
+ while ((match = JIRA_PATTERNS.issueKey.exec(text)) !== null) {
11156
+ const [, projectKey, number] = match;
11157
+ const issueKey = `${projectKey.toUpperCase()}-${number}`;
11158
+ if (!seenIssues.has(issueKey)) {
11159
+ seenIssues.add(issueKey);
11160
+ entities.push({
11161
+ entity: {
11162
+ type: "jira_issue",
11163
+ entity: { key: issueKey }
11164
+ },
11165
+ source
11166
+ });
11167
+ const projectKeyUpper = projectKey.toUpperCase();
11168
+ if (!seenProjects.has(projectKeyUpper)) {
11169
+ seenProjects.add(projectKeyUpper);
11170
+ entities.push({
11171
+ entity: {
11172
+ type: "jira_project",
11173
+ entity: { key: projectKeyUpper }
11174
+ },
11175
+ source
11176
+ });
11177
+ }
11178
+ }
11179
+ }
11180
+ JIRA_PATTERNS.projectMention.lastIndex = 0;
11181
+ while ((match = JIRA_PATTERNS.projectMention.exec(text)) !== null) {
11182
+ const [, projectKey] = match;
11183
+ const projectKeyUpper = projectKey.toUpperCase();
11184
+ if (SKIP_PROJECT_KEYS.has(projectKeyUpper)) {
11185
+ continue;
11186
+ }
11187
+ if (!seenProjects.has(projectKeyUpper)) {
11188
+ seenProjects.add(projectKeyUpper);
11189
+ entities.push({
11190
+ entity: {
11191
+ type: "jira_project",
11192
+ entity: { key: projectKeyUpper }
11193
+ },
11194
+ source
11195
+ });
11196
+ }
11197
+ }
11198
+ return { entities };
11199
+ }
11200
+ };
11201
+ var jiraExtractor = new JiraExtractor();
11202
+
11203
+ // ../../b4m-core/packages/services/dist/src/conversationContextService/extractors/confluence.js
11204
+ var CONFLUENCE_PATTERNS = {
11205
+ // Confluence page URL patterns
11206
+ // https://company.atlassian.net/wiki/spaces/SPACE/pages/123456/Page+Title
11207
+ pageUrl: /https?:\/\/[a-zA-Z0-9_-]+\.atlassian\.net\/wiki\/spaces\/([A-Z0-9_-]+)\/pages\/(\d+)(?:\/([^?\s]+))?/gi,
11208
+ // https://company.atlassian.net/wiki/spaces/SPACE
11209
+ spaceUrl: /https?:\/\/[a-zA-Z0-9_-]+\.atlassian\.net\/wiki\/spaces\/([A-Z0-9_-]+)(?:\/|$|\s|[)\]>])/gi,
11210
+ // Space key mentioned in context (e.g., "in the DOCS space")
11211
+ spaceMention: /(?:space|confluence space)\s+([A-Z][A-Z0-9_-]{0,9})\b/gi,
11212
+ // Page ID pattern (often appears in tool results)
11213
+ pageId: /(?:pageId|page_id|page id)[:\s]+["']?(\d+)["']?/gi
11214
+ };
11215
+ var ConfluenceExtractor = class {
11216
+ /**
11217
+ * Extract Confluence entities from text
11218
+ */
11219
+ extract(text, source) {
11220
+ const entities = [];
11221
+ const seenSpaces = /* @__PURE__ */ new Set();
11222
+ const seenPages = /* @__PURE__ */ new Set();
11223
+ let match;
11224
+ CONFLUENCE_PATTERNS.pageUrl.lastIndex = 0;
11225
+ while ((match = CONFLUENCE_PATTERNS.pageUrl.exec(text)) !== null) {
11226
+ const [, spaceKey, pageId, encodedTitle] = match;
11227
+ if (!seenPages.has(pageId)) {
11228
+ seenPages.add(pageId);
11229
+ let title = `Page ${pageId}`;
11230
+ if (encodedTitle) {
11231
+ try {
11232
+ title = decodeURIComponent(encodedTitle.replace(/\+/g, " "));
11233
+ } catch {
11234
+ title = encodedTitle.replace(/\+/g, " ");
11235
+ }
11236
+ }
11237
+ entities.push({
11238
+ entity: {
11239
+ type: "confluence_page",
11240
+ entity: {
11241
+ id: pageId,
11242
+ title,
11243
+ spaceKey: spaceKey.toUpperCase()
11244
+ }
11245
+ },
11246
+ source
11247
+ });
11248
+ const spaceKeyUpper = spaceKey.toUpperCase();
11249
+ if (!seenSpaces.has(spaceKeyUpper)) {
11250
+ seenSpaces.add(spaceKeyUpper);
11251
+ entities.push({
11252
+ entity: {
11253
+ type: "confluence_space",
11254
+ entity: { key: spaceKeyUpper }
11255
+ },
11256
+ source
11257
+ });
11258
+ }
11259
+ }
11260
+ }
11261
+ CONFLUENCE_PATTERNS.spaceUrl.lastIndex = 0;
11262
+ while ((match = CONFLUENCE_PATTERNS.spaceUrl.exec(text)) !== null) {
11263
+ const [, spaceKey] = match;
11264
+ const spaceKeyUpper = spaceKey.toUpperCase();
11265
+ if (!seenSpaces.has(spaceKeyUpper)) {
11266
+ seenSpaces.add(spaceKeyUpper);
11267
+ entities.push({
11268
+ entity: {
11269
+ type: "confluence_space",
11270
+ entity: { key: spaceKeyUpper }
11271
+ },
11272
+ source
11273
+ });
11274
+ }
11275
+ }
11276
+ CONFLUENCE_PATTERNS.spaceMention.lastIndex = 0;
11277
+ while ((match = CONFLUENCE_PATTERNS.spaceMention.exec(text)) !== null) {
11278
+ const [, spaceKey] = match;
11279
+ const spaceKeyUpper = spaceKey.toUpperCase();
11280
+ if (!seenSpaces.has(spaceKeyUpper)) {
11281
+ seenSpaces.add(spaceKeyUpper);
11282
+ entities.push({
11283
+ entity: {
11284
+ type: "confluence_space",
11285
+ entity: { key: spaceKeyUpper }
11286
+ },
11287
+ source
11288
+ });
11289
+ }
11290
+ }
11291
+ CONFLUENCE_PATTERNS.pageId.lastIndex = 0;
11292
+ while ((match = CONFLUENCE_PATTERNS.pageId.exec(text)) !== null) {
11293
+ const [, pageId] = match;
11294
+ if (!seenPages.has(pageId)) {
11295
+ seenPages.add(pageId);
11296
+ entities.push({
11297
+ entity: {
11298
+ type: "confluence_page",
11299
+ entity: {
11300
+ id: pageId,
11301
+ title: `Page ${pageId}`
11302
+ // Title unknown from just ID
11303
+ }
11304
+ },
11305
+ source
11306
+ });
11307
+ }
11308
+ }
11309
+ return { entities };
11310
+ }
11311
+ };
11312
+ var confluenceExtractor = new ConfluenceExtractor();
11313
+
11314
+ // ../../b4m-core/packages/services/dist/src/conversationContextService/extractors/index.js
11315
+ var CombinedExtractor = class {
11316
+ constructor() {
11317
+ this.extractors = [githubExtractor, jiraExtractor, confluenceExtractor];
11318
+ }
11319
+ /**
11320
+ * Extract all entities from text using all available extractors
11321
+ *
11322
+ * @param text - The text to extract entities from
11323
+ * @param source - The source of the text
11324
+ * @returns Combined extraction results from all extractors
11325
+ */
11326
+ extract(text, source) {
11327
+ const allEntities = [];
11328
+ for (const extractor of this.extractors) {
11329
+ const result = extractor.extract(text, source);
11330
+ allEntities.push(...result.entities);
11331
+ }
11332
+ return { entities: allEntities };
11333
+ }
11334
+ /**
11335
+ * Extract entities from multiple text blocks (e.g., messages in a conversation)
11336
+ *
11337
+ * @param texts - Array of text content with their sources
11338
+ * @returns Combined extraction results
11339
+ */
11340
+ extractFromMultiple(texts) {
11341
+ const allEntities = [];
11342
+ for (const { text, source } of texts) {
11343
+ const result = this.extract(text, source);
11344
+ allEntities.push(...result.entities);
11345
+ }
11346
+ return { entities: allEntities };
11347
+ }
11348
+ };
11349
+ var combinedExtractor = new CombinedExtractor();
11350
+
10870
11351
  // ../../b4m-core/packages/services/dist/src/llm/ChatCompletionProcess.js
10871
11352
  var DISABLE_SERVER_THROTTLING = process.env.DISABLE_SERVER_THROTTLING === "true";
10872
11353
  var questSaveMutex = new Mutex();
@@ -11294,6 +11775,7 @@ var AgentFrontmatterSchema = z146.object({
11294
11775
  model: z146.string().optional(),
11295
11776
  "allowed-tools": z146.array(z146.string()).optional(),
11296
11777
  "denied-tools": z146.array(z146.string()).optional(),
11778
+ skills: z146.array(z146.string()).optional(),
11297
11779
  "max-iterations": z146.object({
11298
11780
  quick: z146.number().int().positive().optional(),
11299
11781
  medium: z146.number().int().positive().optional(),
@@ -11316,21 +11798,21 @@ var NoOpStorage = class extends BaseStorage {
11316
11798
  async upload(input, destination, options) {
11317
11799
  return `/tmp/${destination}`;
11318
11800
  }
11319
- async download(path17) {
11801
+ async download(path18) {
11320
11802
  throw new Error("Download not supported in CLI");
11321
11803
  }
11322
- async delete(path17) {
11804
+ async delete(path18) {
11323
11805
  }
11324
- async getSignedUrl(path17) {
11325
- return `/tmp/${path17}`;
11806
+ async getSignedUrl(path18) {
11807
+ return `/tmp/${path18}`;
11326
11808
  }
11327
- getPublicUrl(path17) {
11328
- return `/tmp/${path17}`;
11809
+ getPublicUrl(path18) {
11810
+ return `/tmp/${path18}`;
11329
11811
  }
11330
- async getPreview(path17) {
11331
- return `/tmp/${path17}`;
11812
+ async getPreview(path18) {
11813
+ return `/tmp/${path18}`;
11332
11814
  }
11333
- async getMetadata(path17) {
11815
+ async getMetadata(path18) {
11334
11816
  return { size: 0, contentType: "application/octet-stream" };
11335
11817
  }
11336
11818
  };
@@ -13207,7 +13689,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
13207
13689
  // package.json
13208
13690
  var package_default = {
13209
13691
  name: "@bike4mind/cli",
13210
- version: "0.2.26-refactor-dry-credit-deduction.18613+04ea81997",
13692
+ version: "0.2.26",
13211
13693
  type: "module",
13212
13694
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
13213
13695
  license: "UNLICENSED",
@@ -13315,10 +13797,10 @@ var package_default = {
13315
13797
  },
13316
13798
  devDependencies: {
13317
13799
  "@bike4mind/agents": "0.1.0",
13318
- "@bike4mind/common": "2.48.1-refactor-dry-credit-deduction.18613+04ea81997",
13319
- "@bike4mind/mcp": "1.28.2-refactor-dry-credit-deduction.18613+04ea81997",
13320
- "@bike4mind/services": "2.46.1-refactor-dry-credit-deduction.18613+04ea81997",
13321
- "@bike4mind/utils": "2.3.5-refactor-dry-credit-deduction.18613+04ea81997",
13800
+ "@bike4mind/common": "2.49.0",
13801
+ "@bike4mind/mcp": "1.28.2",
13802
+ "@bike4mind/services": "2.47.0",
13803
+ "@bike4mind/utils": "2.4.0",
13322
13804
  "@types/better-sqlite3": "^7.6.13",
13323
13805
  "@types/diff": "^5.0.9",
13324
13806
  "@types/jsonwebtoken": "^9.0.4",
@@ -13335,7 +13817,7 @@ var package_default = {
13335
13817
  optionalDependencies: {
13336
13818
  "@vscode/ripgrep": "^1.17.0"
13337
13819
  },
13338
- gitHead: "04ea81997073bcb80acec65dc9f714cac430d530"
13820
+ gitHead: "38aa7cfdcf121f8fea9cc0bfb390a27e1434c76c"
13339
13821
  };
13340
13822
 
13341
13823
  // src/config/constants.ts
@@ -13366,104 +13848,368 @@ function filterToolsByPatterns(allTools, allowedPatterns, deniedPatterns) {
13366
13848
  });
13367
13849
  }
13368
13850
 
13369
- // src/agents/SubagentOrchestrator.ts
13370
- var SubagentOrchestrator = class {
13371
- constructor(deps) {
13372
- this.beforeRunCallback = null;
13373
- this.afterRunCallback = null;
13374
- this.deps = deps;
13851
+ // src/tools/skillTool.ts
13852
+ async function executeHook(script, context) {
13853
+ const result = await runShellCommand({
13854
+ command: script,
13855
+ cwd: process.cwd(),
13856
+ timeoutMs: 3e4,
13857
+ env: {
13858
+ ...process.env,
13859
+ SKILL_NAME: context.skillName,
13860
+ SKILL_ARGS: context.args,
13861
+ SKILL_RESULT: context.result || "",
13862
+ SKILL_ERROR: context.error || ""
13863
+ }
13864
+ });
13865
+ if (result.timedOut) {
13866
+ return { success: false, output: "Hook failed: timed out after 30s" };
13375
13867
  }
13376
- /**
13377
- * Set a callback to be invoked before each agent.run()
13378
- * Use this to subscribe to agent events (e.g., agent.on('action', handler))
13379
- */
13380
- setBeforeRunCallback(callback) {
13381
- this.beforeRunCallback = callback;
13868
+ if (result.exitCode !== 0) {
13869
+ return { success: false, output: `Hook failed: ${result.stderr || `exit code ${result.exitCode}`}` };
13382
13870
  }
13383
- /**
13384
- * Set a callback to be invoked after each agent.run()
13385
- * Use this to unsubscribe from agent events (e.g., agent.off('action', handler))
13386
- */
13387
- setAfterRunCallback(callback) {
13388
- this.afterRunCallback = callback;
13871
+ return { success: true, output: result.stdout || result.stderr };
13872
+ }
13873
+ function parseAgentConfig(agent) {
13874
+ if (!agent) {
13875
+ return { name: "general-purpose", thoroughness: void 0 };
13389
13876
  }
13390
- /**
13391
- * Delegate a task to an agent loaded from markdown definition
13392
- *
13393
- * @param options - Configuration for agent execution
13394
- * @returns Agent result with summary
13395
- */
13396
- async delegateToAgent(options) {
13397
- const { task, agentName, thoroughness, variables, parentSessionId, model, allowedTools } = options;
13398
- const agentDef = this.deps.agentStore.getAgent(agentName);
13399
- if (!agentDef) {
13400
- const available = this.deps.agentStore.getAgentNames().join(", ");
13401
- throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
13402
- }
13403
- const effectiveModel = model || agentDef.model;
13404
- const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
13405
- const maxIterations = agentDef.maxIterations[effectiveThoroughness];
13406
- const effectiveVariables = {
13407
- ...agentDef.defaultVariables,
13408
- ...variables
13409
- };
13410
- const systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
13411
- const effectiveAllowedTools = allowedTools || agentDef.allowedTools;
13412
- const toolFilter = {
13413
- allowedTools: effectiveAllowedTools,
13414
- deniedTools: [...agentDef.deniedTools || [], ...ALWAYS_DENIED_FOR_AGENTS]
13415
- };
13416
- const agentContext = {
13417
- currentAgent: null,
13418
- observationQueue: []
13419
- };
13420
- const { tools: allTools, agentContext: updatedContext } = generateCliTools(
13421
- this.deps.userId,
13422
- this.deps.llm,
13423
- effectiveModel,
13424
- this.deps.permissionManager,
13425
- this.deps.showPermissionPrompt,
13426
- agentContext,
13427
- this.deps.configStore,
13428
- this.deps.apiClient
13429
- );
13430
- const filteredTools = filterToolsByPatterns(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
13431
- const hookWrapperContext = {
13432
- sessionId: parentSessionId,
13433
- agentName,
13434
- cwd: process.cwd()
13435
- };
13436
- const hookedTools = filteredTools.map((tool) => wrapToolWithHooks(tool, agentDef.hooks, hookWrapperContext));
13437
- this.deps.logger.debug(
13438
- `Spawning "${agentName}" agent with ${hookedTools.length} tools, thoroughness: ${effectiveThoroughness}, max iterations: ${maxIterations}`
13439
- );
13440
- const agent = new ReActAgent({
13441
- userId: this.deps.userId,
13442
- logger: this.deps.logger,
13443
- llm: this.deps.llm,
13444
- model: effectiveModel,
13445
- tools: hookedTools,
13446
- maxIterations,
13447
- systemPrompt
13448
- });
13449
- updatedContext.currentAgent = agent;
13450
- if (this.beforeRunCallback) {
13451
- this.beforeRunCallback(agent, agentName);
13877
+ if (typeof agent === "string") {
13878
+ return { name: agent, thoroughness: void 0 };
13879
+ }
13880
+ return { name: agent.type, thoroughness: agent.thoroughness };
13881
+ }
13882
+ function parseArguments(argsString) {
13883
+ const args = [];
13884
+ let current = "";
13885
+ let inQuotes = false;
13886
+ let quoteChar = "";
13887
+ for (let i = 0; i < argsString.length; i++) {
13888
+ const char = argsString[i];
13889
+ if (!inQuotes && (char === '"' || char === "'")) {
13890
+ inQuotes = true;
13891
+ quoteChar = char;
13892
+ } else if (inQuotes && char === quoteChar) {
13893
+ inQuotes = false;
13894
+ quoteChar = "";
13895
+ } else if (!inQuotes && char === " ") {
13896
+ if (current.length > 0) {
13897
+ args.push(current);
13898
+ current = "";
13899
+ }
13900
+ } else {
13901
+ current += char;
13452
13902
  }
13453
- const startTime = Date.now();
13454
- let result;
13455
- try {
13456
- result = await agent.run(task, {
13457
- maxIterations,
13458
- parallelExecution: this.deps.enableParallelToolExecution === true,
13459
- isReadOnlyTool
13460
- });
13461
- } catch (error) {
13462
- if (error instanceof HookBlockedError) {
13463
- if (this.afterRunCallback) {
13464
- this.afterRunCallback(agent, agentName);
13465
- }
13466
- return {
13903
+ }
13904
+ if (current.length > 0) {
13905
+ args.push(current);
13906
+ }
13907
+ return args;
13908
+ }
13909
+ function createSkillTool(deps) {
13910
+ const { customCommandStore } = deps;
13911
+ return {
13912
+ toolFn: async (args) => {
13913
+ const params = args;
13914
+ if (!params.skill || typeof params.skill !== "string") {
13915
+ throw new Error("skill: skill parameter is required");
13916
+ }
13917
+ const skillName = params.skill.replace(/^\//, "");
13918
+ const argsString = params.args || "";
13919
+ const { allowedSkills } = deps;
13920
+ if (allowedSkills && allowedSkills.length > 0) {
13921
+ if (!allowedSkills.includes(skillName)) {
13922
+ throw new Error(
13923
+ `skill: "${skillName}" is not available to this agent. Allowed skills: ${allowedSkills.join(", ")}`
13924
+ );
13925
+ }
13926
+ }
13927
+ const command = customCommandStore.getCommand(skillName);
13928
+ if (!command) {
13929
+ const available = customCommandStore.getAllCommands().map((c) => c.name).join(", ");
13930
+ throw new Error(`skill: "${skillName}" not found. Available skills: ${available || "none"}`);
13931
+ }
13932
+ if (command.hooks?.["pre-invoke"]) {
13933
+ const hookResult = await executeHook(command.hooks["pre-invoke"], {
13934
+ skillName,
13935
+ args: argsString
13936
+ });
13937
+ if (!hookResult.success) {
13938
+ throw new Error(`Pre-invoke hook failed: ${hookResult.output}`);
13939
+ }
13940
+ }
13941
+ try {
13942
+ const argsArray = params.args ? parseArguments(params.args) : [];
13943
+ let expandedBody = substituteArguments(command.body, argsArray);
13944
+ const processed = await processFileReferences(expandedBody);
13945
+ expandedBody = processed.content;
13946
+ if (processed.errors.length > 0) {
13947
+ expandedBody += `
13948
+
13949
+ **File reference errors:**
13950
+ ${processed.errors.map((e) => `- ${e}`).join("\n")}`;
13951
+ }
13952
+ let result;
13953
+ const hasToolRestrictions = Boolean(command.allowedTools?.length);
13954
+ const hasModelOverride = Boolean(command.model);
13955
+ const needsFork = command.context === "fork" || hasToolRestrictions || hasModelOverride;
13956
+ if (needsFork) {
13957
+ const { subagentOrchestrator, sessionId } = deps;
13958
+ if (!subagentOrchestrator || !sessionId) {
13959
+ const missing = !subagentOrchestrator ? "subagentOrchestrator" : "sessionId";
13960
+ throw new Error(`Skill "${skillName}" requires forked context but ${missing} is not available`);
13961
+ }
13962
+ if (command.context !== "fork") {
13963
+ const reasons = [hasToolRestrictions && "allowed-tools", hasModelOverride && "model"].filter(Boolean);
13964
+ logger.info(`Auto-upgraded "/${skillName}" to fork context (uses: ${reasons.join(", ")})`);
13965
+ }
13966
+ const agentConfig = parseAgentConfig(command.agent);
13967
+ const agentResult = await subagentOrchestrator.delegateToAgent({
13968
+ task: expandedBody,
13969
+ agentName: agentConfig.name,
13970
+ thoroughness: agentConfig.thoroughness || command.thoroughness,
13971
+ variables: command.variables,
13972
+ parentSessionId: sessionId,
13973
+ // Pass skill-level overrides
13974
+ model: command.model,
13975
+ allowedTools: command.allowedTools
13976
+ });
13977
+ const agentName = agentConfig.name;
13978
+ result = `## Skill Executed: /${skillName} (via ${agentName} agent)
13979
+
13980
+ ${agentResult.summary}`;
13981
+ } else {
13982
+ result = `## Skill Loaded: /${skillName}
13983
+
13984
+ ${expandedBody}
13985
+
13986
+ ---
13987
+ *Follow the instructions above. This skill was invoked programmatically.*`;
13988
+ }
13989
+ if (command.hooks?.["post-invoke"]) {
13990
+ const hookResult = await executeHook(command.hooks["post-invoke"], {
13991
+ skillName,
13992
+ args: argsString,
13993
+ result
13994
+ });
13995
+ if (!hookResult.success) {
13996
+ logger.warn(`Post-invoke hook warning: ${hookResult.output}`);
13997
+ }
13998
+ }
13999
+ return result;
14000
+ } catch (error) {
14001
+ if (command.hooks?.["on-error"]) {
14002
+ const errorMessage = error instanceof Error ? error.message : String(error);
14003
+ const hookResult = await executeHook(command.hooks["on-error"], {
14004
+ skillName,
14005
+ args: argsString,
14006
+ error: errorMessage
14007
+ });
14008
+ if (hookResult.output) {
14009
+ logger.warn(`On-error hook output: ${hookResult.output}`);
14010
+ }
14011
+ }
14012
+ throw error;
14013
+ }
14014
+ },
14015
+ toolSchema: {
14016
+ name: "skill",
14017
+ description: `Execute a skill (custom slash command) within the conversation.
14018
+
14019
+ **When to use this tool:**
14020
+ - When a user asks you to use a skill by name (e.g., "use the review-pr skill")
14021
+ - When a skill would help accomplish the user's request
14022
+ - When you see /<skill-name> syntax in user messages
14023
+
14024
+ **How it works:**
14025
+ 1. Skills are loaded from markdown files in .bike4mind/commands/
14026
+ 2. The skill template is expanded with argument substitution ($1, $2, $ARGUMENTS)
14027
+ 3. File references (@filename) are resolved and content is injected
14028
+ 4. The expanded template is returned for you to follow
14029
+
14030
+ **Example invocations:**
14031
+ - skill({ skill: "commit" }) - invoke commit skill
14032
+ - skill({ skill: "review-pr", args: "123" }) - review PR #123
14033
+ - skill({ skill: "feature-code-map", args: "authentication" }) - generate code map
14034
+
14035
+ **Important:**
14036
+ - Skill names can be with or without leading slash: "commit" or "/commit"
14037
+ - Arguments are space-separated; use quotes for arguments with spaces
14038
+ - The tool returns instructions to follow, not a final answer`,
14039
+ parameters: {
14040
+ type: "object",
14041
+ properties: {
14042
+ skill: {
14043
+ type: "string",
14044
+ description: 'Name of the skill to invoke (e.g., "commit", "review-pr")'
14045
+ },
14046
+ args: {
14047
+ type: "string",
14048
+ description: `Optional arguments as space-separated string. Use quotes for arguments containing spaces (e.g., '123 "bug fix"').`
14049
+ }
14050
+ },
14051
+ required: ["skill"]
14052
+ }
14053
+ }
14054
+ };
14055
+ }
14056
+
14057
+ // src/core/skillsPrompt.ts
14058
+ function getSkillDisplayName(cmd) {
14059
+ return cmd.displayName || cmd.name;
14060
+ }
14061
+ function formatSkillEntry(cmd) {
14062
+ const displayName = getSkillDisplayName(cmd);
14063
+ const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
14064
+ const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
14065
+ return `- ${nameDisplay}${argHint}: ${cmd.description}
14066
+ `;
14067
+ }
14068
+ function formatSkillGroup(heading, commands) {
14069
+ if (commands.length === 0) {
14070
+ return "";
14071
+ }
14072
+ return `
14073
+ ### ${heading}
14074
+ ${commands.map(formatSkillEntry).join("")}`;
14075
+ }
14076
+ function filterAIVisibleSkills(commands) {
14077
+ return commands.filter((cmd) => !cmd.disableModelInvocation);
14078
+ }
14079
+ function filterSkillsByAllowedList(commands, allowedSkills) {
14080
+ if (!allowedSkills || allowedSkills.length === 0) {
14081
+ return commands;
14082
+ }
14083
+ return commands.filter((cmd) => allowedSkills.includes(cmd.name));
14084
+ }
14085
+ function buildSkillsPromptSection(commands, allowedSkills) {
14086
+ const filteredByAllowed = filterSkillsByAllowedList(commands, allowedSkills);
14087
+ const visibleCommands = filterAIVisibleSkills(filteredByAllowed);
14088
+ if (visibleCommands.length === 0) {
14089
+ return "";
14090
+ }
14091
+ const projectSkills = visibleCommands.filter((c) => c.source === "project");
14092
+ const globalSkills = visibleCommands.filter((c) => c.source === "global");
14093
+ return `
14094
+
14095
+ ## Available Skills
14096
+
14097
+ Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
14098
+ ` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
14099
+ }
14100
+
14101
+ // src/agents/SubagentOrchestrator.ts
14102
+ var SubagentOrchestrator = class {
14103
+ constructor(deps) {
14104
+ this.beforeRunCallback = null;
14105
+ this.afterRunCallback = null;
14106
+ this.deps = deps;
14107
+ }
14108
+ /**
14109
+ * Set a callback to be invoked before each agent.run()
14110
+ * Use this to subscribe to agent events (e.g., agent.on('action', handler))
14111
+ */
14112
+ setBeforeRunCallback(callback) {
14113
+ this.beforeRunCallback = callback;
14114
+ }
14115
+ /**
14116
+ * Set a callback to be invoked after each agent.run()
14117
+ * Use this to unsubscribe from agent events (e.g., agent.off('action', handler))
14118
+ */
14119
+ setAfterRunCallback(callback) {
14120
+ this.afterRunCallback = callback;
14121
+ }
14122
+ /**
14123
+ * Delegate a task to an agent loaded from markdown definition
14124
+ *
14125
+ * @param options - Configuration for agent execution
14126
+ * @returns Agent result with summary
14127
+ */
14128
+ async delegateToAgent(options) {
14129
+ const { task, agentName, thoroughness, variables, parentSessionId, model, allowedTools } = options;
14130
+ const agentDef = this.deps.agentStore.getAgent(agentName);
14131
+ if (!agentDef) {
14132
+ const available = this.deps.agentStore.getAgentNames().join(", ");
14133
+ throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
14134
+ }
14135
+ const effectiveModel = model || agentDef.model;
14136
+ const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
14137
+ const maxIterations = agentDef.maxIterations[effectiveThoroughness];
14138
+ const effectiveVariables = {
14139
+ ...agentDef.defaultVariables,
14140
+ ...variables
14141
+ };
14142
+ let systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
14143
+ const effectiveAllowedTools = allowedTools || agentDef.allowedTools;
14144
+ const toolFilter = {
14145
+ allowedTools: effectiveAllowedTools,
14146
+ deniedTools: [...agentDef.deniedTools || [], ...ALWAYS_DENIED_FOR_AGENTS]
14147
+ };
14148
+ const agentContext = {
14149
+ currentAgent: null,
14150
+ observationQueue: []
14151
+ };
14152
+ const { tools: allTools, agentContext: updatedContext } = generateCliTools(
14153
+ this.deps.userId,
14154
+ this.deps.llm,
14155
+ effectiveModel,
14156
+ this.deps.permissionManager,
14157
+ this.deps.showPermissionPrompt,
14158
+ agentContext,
14159
+ this.deps.configStore,
14160
+ this.deps.apiClient
14161
+ );
14162
+ const filteredTools = filterToolsByPatterns(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
14163
+ if (this.deps.customCommandStore) {
14164
+ const skillTool = createSkillTool({
14165
+ customCommandStore: this.deps.customCommandStore,
14166
+ subagentOrchestrator: this,
14167
+ sessionId: parentSessionId,
14168
+ allowedSkills: agentDef.skills
14169
+ });
14170
+ filteredTools.push(skillTool);
14171
+ const commands = this.deps.customCommandStore.getAllCommands();
14172
+ const skillsSection = buildSkillsPromptSection(commands, agentDef.skills);
14173
+ if (skillsSection) {
14174
+ systemPrompt += skillsSection;
14175
+ }
14176
+ }
14177
+ const hookWrapperContext = {
14178
+ sessionId: parentSessionId,
14179
+ agentName,
14180
+ cwd: process.cwd()
14181
+ };
14182
+ const hookedTools = filteredTools.map((tool) => wrapToolWithHooks(tool, agentDef.hooks, hookWrapperContext));
14183
+ this.deps.logger.debug(
14184
+ `Spawning "${agentName}" agent with ${hookedTools.length} tools, thoroughness: ${effectiveThoroughness}, max iterations: ${maxIterations}`
14185
+ );
14186
+ const agent = new ReActAgent({
14187
+ userId: this.deps.userId,
14188
+ logger: this.deps.logger,
14189
+ llm: this.deps.llm,
14190
+ model: effectiveModel,
14191
+ tools: hookedTools,
14192
+ maxIterations,
14193
+ systemPrompt
14194
+ });
14195
+ updatedContext.currentAgent = agent;
14196
+ if (this.beforeRunCallback) {
14197
+ this.beforeRunCallback(agent, agentName);
14198
+ }
14199
+ const startTime = Date.now();
14200
+ let result;
14201
+ try {
14202
+ result = await agent.run(task, {
14203
+ maxIterations,
14204
+ parallelExecution: this.deps.enableParallelToolExecution === true,
14205
+ isReadOnlyTool
14206
+ });
14207
+ } catch (error) {
14208
+ if (error instanceof HookBlockedError) {
14209
+ if (this.afterRunCallback) {
14210
+ this.afterRunCallback(agent, agentName);
14211
+ }
14212
+ return {
13467
14213
  agentName,
13468
14214
  thoroughness: effectiveThoroughness,
13469
14215
  summary: `Agent blocked: ${error.message}`,
@@ -13589,14 +14335,15 @@ var MODEL_ALIASES = {
13589
14335
  // Anthropic/Claude Models
13590
14336
  // ===================
13591
14337
  // Short aliases (most common)
13592
- opus: "claude-opus-4-5-20251101",
14338
+ opus: "claude-opus-4-6",
13593
14339
  sonnet: "claude-sonnet-4-5-20250929",
13594
14340
  haiku: "claude-3-5-haiku-20241022",
13595
14341
  // Claude-prefixed aliases
13596
- "claude-opus": "claude-opus-4-5-20251101",
14342
+ "claude-opus": "claude-opus-4-6",
13597
14343
  "claude-sonnet": "claude-sonnet-4-5-20250929",
13598
14344
  "claude-haiku": "claude-3-5-haiku-20241022",
13599
14345
  // Version-specific Claude aliases
14346
+ "claude-4.6-opus": "claude-opus-4-6",
13600
14347
  "claude-4.5-opus": "claude-opus-4-5-20251101",
13601
14348
  "claude-4.5-sonnet": "claude-sonnet-4-5-20250929",
13602
14349
  "claude-4.5-haiku": "claude-haiku-4-5-20251001",
@@ -13805,6 +14552,7 @@ var AgentStore = class {
13805
14552
  systemPrompt: body.trim(),
13806
14553
  allowedTools: parsed["allowed-tools"],
13807
14554
  deniedTools: parsed["denied-tools"],
14555
+ skills: parsed.skills,
13808
14556
  maxIterations: {
13809
14557
  quick: parsed["max-iterations"]?.quick ?? DEFAULT_MAX_ITERATIONS.quick,
13810
14558
  medium: parsed["max-iterations"]?.medium ?? DEFAULT_MAX_ITERATIONS.medium,
@@ -14303,323 +15051,125 @@ ${details}`;
14303
15051
  for (const [id] of toRemove) {
14304
15052
  this.jobs.delete(id);
14305
15053
  cleanedCount++;
14306
- }
14307
- }
14308
- return cleanedCount;
14309
- }
14310
- /**
14311
- * Get the current number of jobs (for monitoring)
14312
- */
14313
- getJobCount() {
14314
- let running = 0;
14315
- let queued = 0;
14316
- let completed = 0;
14317
- for (const internal of this.jobs.values()) {
14318
- switch (internal.job.status) {
14319
- case "running":
14320
- running++;
14321
- break;
14322
- case "queued":
14323
- queued++;
14324
- break;
14325
- default:
14326
- completed++;
14327
- }
14328
- }
14329
- return { total: this.jobs.size, running, queued, completed };
14330
- }
14331
- };
14332
-
14333
- // src/agents/backgroundTools.ts
14334
- function createBackgroundAgentTools(manager) {
14335
- const checkAgentStatus = {
14336
- toolFn: async (args) => {
14337
- const { job_id } = args;
14338
- if (!job_id) throw new Error("check_agent_status: job_id is required");
14339
- const job = manager.getJob(job_id);
14340
- if (!job) return `No background agent found with ID: ${job_id}`;
14341
- switch (job.status) {
14342
- case "queued":
14343
- return `Agent "${job.agentName}" is queued (waiting for a concurrency slot). Task: ${job.task}`;
14344
- case "running": {
14345
- const elapsed = Math.round((Date.now() - job.startTime) / 1e3);
14346
- return `Agent "${job.agentName}" is still running (${elapsed}s elapsed). Task: ${job.task}`;
14347
- }
14348
- case "completed": {
14349
- const result = manager.getResult(job_id);
14350
- return result?.summary || `Agent "${job.agentName}" completed but no summary available.`;
14351
- }
14352
- case "failed":
14353
- return `Agent "${job.agentName}" failed: ${job.error || "Unknown error"}`;
14354
- case "cancelled":
14355
- return `Agent "${job.agentName}" was cancelled.`;
14356
- }
14357
- },
14358
- toolSchema: {
14359
- name: "check_agent_status",
14360
- description: "Check the status and retrieve results of a background agent job. Use after spawning an agent with run_in_background: true.",
14361
- parameters: {
14362
- type: "object",
14363
- properties: {
14364
- job_id: {
14365
- type: "string",
14366
- description: "The job ID returned by agent_delegate when run_in_background was true"
14367
- }
14368
- },
14369
- required: ["job_id"]
14370
- }
14371
- }
14372
- };
14373
- const listBackgroundAgents = {
14374
- toolFn: async () => {
14375
- const jobs = manager.listJobs();
14376
- if (jobs.length === 0) return "No background agents.";
14377
- return jobs.map((job) => {
14378
- const elapsed = Math.round(((job.endTime || Date.now()) - job.startTime) / 1e3);
14379
- const statusIcons = {
14380
- queued: "\u{1F550}",
14381
- running: "\u23F3",
14382
- completed: "\u2705",
14383
- failed: "\u274C",
14384
- cancelled: "\u{1F6AB}"
14385
- };
14386
- const statusIcon = statusIcons[job.status] || "\u2753";
14387
- return `${statusIcon} [${job.id}] ${job.agentName} (${job.status}, ${elapsed}s) - ${job.task.slice(0, 80)}`;
14388
- }).join("\n");
14389
- },
14390
- toolSchema: {
14391
- name: "list_background_agents",
14392
- description: "List all background agent jobs with their current status.",
14393
- parameters: {
14394
- type: "object",
14395
- properties: {}
14396
- }
14397
- }
14398
- };
14399
- const cancelBackgroundAgent = {
14400
- toolFn: async (args) => {
14401
- const { job_id } = args;
14402
- if (!job_id) throw new Error("cancel_background_agent: job_id is required");
14403
- const cancelled = manager.cancelJob(job_id);
14404
- if (cancelled) return `Background agent ${job_id} has been cancelled.`;
14405
- const job = manager.getJob(job_id);
14406
- if (!job) return `No background agent found with ID: ${job_id}`;
14407
- return `Cannot cancel agent ${job_id} - status is already "${job.status}".`;
14408
- },
14409
- toolSchema: {
14410
- name: "cancel_background_agent",
14411
- description: "Cancel a running background agent job.",
14412
- parameters: {
14413
- type: "object",
14414
- properties: {
14415
- job_id: {
14416
- type: "string",
14417
- description: "The job ID of the background agent to cancel"
14418
- }
14419
- },
14420
- required: ["job_id"]
14421
- }
14422
- }
14423
- };
14424
- return [checkAgentStatus, listBackgroundAgents, cancelBackgroundAgent];
14425
- }
14426
-
14427
- // src/tools/skillTool.ts
14428
- async function executeHook(script, context) {
14429
- const result = await runShellCommand({
14430
- command: script,
14431
- cwd: process.cwd(),
14432
- timeoutMs: 3e4,
14433
- env: {
14434
- ...process.env,
14435
- SKILL_NAME: context.skillName,
14436
- SKILL_ARGS: context.args,
14437
- SKILL_RESULT: context.result || "",
14438
- SKILL_ERROR: context.error || ""
14439
- }
14440
- });
14441
- if (result.timedOut) {
14442
- return { success: false, output: "Hook failed: timed out after 30s" };
14443
- }
14444
- if (result.exitCode !== 0) {
14445
- return { success: false, output: `Hook failed: ${result.stderr || `exit code ${result.exitCode}`}` };
14446
- }
14447
- return { success: true, output: result.stdout || result.stderr };
14448
- }
14449
- function parseAgentConfig(agent) {
14450
- if (!agent) {
14451
- return { name: "general-purpose", thoroughness: void 0 };
14452
- }
14453
- if (typeof agent === "string") {
14454
- return { name: agent, thoroughness: void 0 };
14455
- }
14456
- return { name: agent.type, thoroughness: agent.thoroughness };
14457
- }
14458
- function parseArguments(argsString) {
14459
- const args = [];
14460
- let current = "";
14461
- let inQuotes = false;
14462
- let quoteChar = "";
14463
- for (let i = 0; i < argsString.length; i++) {
14464
- const char = argsString[i];
14465
- if (!inQuotes && (char === '"' || char === "'")) {
14466
- inQuotes = true;
14467
- quoteChar = char;
14468
- } else if (inQuotes && char === quoteChar) {
14469
- inQuotes = false;
14470
- quoteChar = "";
14471
- } else if (!inQuotes && char === " ") {
14472
- if (current.length > 0) {
14473
- args.push(current);
14474
- current = "";
14475
- }
14476
- } else {
14477
- current += char;
14478
- }
14479
- }
14480
- if (current.length > 0) {
14481
- args.push(current);
14482
- }
14483
- return args;
14484
- }
14485
- function createSkillTool(deps) {
14486
- const { customCommandStore } = deps;
14487
- return {
14488
- toolFn: async (args) => {
14489
- const params = args;
14490
- if (!params.skill || typeof params.skill !== "string") {
14491
- throw new Error("skill: skill parameter is required");
14492
- }
14493
- const skillName = params.skill.replace(/^\//, "");
14494
- const argsString = params.args || "";
14495
- const command = customCommandStore.getCommand(skillName);
14496
- if (!command) {
14497
- const available = customCommandStore.getAllCommands().map((c) => c.name).join(", ");
14498
- throw new Error(`skill: "${skillName}" not found. Available skills: ${available || "none"}`);
14499
- }
14500
- if (command.hooks?.["pre-invoke"]) {
14501
- const hookResult = await executeHook(command.hooks["pre-invoke"], {
14502
- skillName,
14503
- args: argsString
14504
- });
14505
- if (!hookResult.success) {
14506
- throw new Error(`Pre-invoke hook failed: ${hookResult.output}`);
14507
- }
14508
- }
14509
- try {
14510
- const argsArray = params.args ? parseArguments(params.args) : [];
14511
- let expandedBody = substituteArguments(command.body, argsArray);
14512
- const processed = await processFileReferences(expandedBody);
14513
- expandedBody = processed.content;
14514
- if (processed.errors.length > 0) {
14515
- expandedBody += `
14516
-
14517
- **File reference errors:**
14518
- ${processed.errors.map((e) => `- ${e}`).join("\n")}`;
14519
- }
14520
- let result;
14521
- const hasToolRestrictions = Boolean(command.allowedTools?.length);
14522
- const hasModelOverride = Boolean(command.model);
14523
- const needsFork = command.context === "fork" || hasToolRestrictions || hasModelOverride;
14524
- if (needsFork) {
14525
- const { subagentOrchestrator, sessionId } = deps;
14526
- if (!subagentOrchestrator || !sessionId) {
14527
- const missing = !subagentOrchestrator ? "subagentOrchestrator" : "sessionId";
14528
- throw new Error(`Skill "${skillName}" requires forked context but ${missing} is not available`);
14529
- }
14530
- if (command.context !== "fork") {
14531
- const reasons = [hasToolRestrictions && "allowed-tools", hasModelOverride && "model"].filter(Boolean);
14532
- logger.info(`Auto-upgraded "/${skillName}" to fork context (uses: ${reasons.join(", ")})`);
14533
- }
14534
- const agentConfig = parseAgentConfig(command.agent);
14535
- const agentResult = await subagentOrchestrator.delegateToAgent({
14536
- task: expandedBody,
14537
- agentName: agentConfig.name,
14538
- thoroughness: agentConfig.thoroughness || command.thoroughness,
14539
- variables: command.variables,
14540
- parentSessionId: sessionId,
14541
- // Pass skill-level overrides
14542
- model: command.model,
14543
- allowedTools: command.allowedTools
14544
- });
14545
- const agentName = agentConfig.name;
14546
- result = `## Skill Executed: /${skillName} (via ${agentName} agent)
14547
-
14548
- ${agentResult.summary}`;
14549
- } else {
14550
- result = `## Skill Loaded: /${skillName}
14551
-
14552
- ${expandedBody}
15054
+ }
15055
+ }
15056
+ return cleanedCount;
15057
+ }
15058
+ /**
15059
+ * Get the current number of jobs (for monitoring)
15060
+ */
15061
+ getJobCount() {
15062
+ let running = 0;
15063
+ let queued = 0;
15064
+ let completed = 0;
15065
+ for (const internal of this.jobs.values()) {
15066
+ switch (internal.job.status) {
15067
+ case "running":
15068
+ running++;
15069
+ break;
15070
+ case "queued":
15071
+ queued++;
15072
+ break;
15073
+ default:
15074
+ completed++;
15075
+ }
15076
+ }
15077
+ return { total: this.jobs.size, running, queued, completed };
15078
+ }
15079
+ };
14553
15080
 
14554
- ---
14555
- *Follow the instructions above. This skill was invoked programmatically.*`;
14556
- }
14557
- if (command.hooks?.["post-invoke"]) {
14558
- const hookResult = await executeHook(command.hooks["post-invoke"], {
14559
- skillName,
14560
- args: argsString,
14561
- result
14562
- });
14563
- if (!hookResult.success) {
14564
- logger.warn(`Post-invoke hook warning: ${hookResult.output}`);
14565
- }
15081
+ // src/agents/backgroundTools.ts
15082
+ function createBackgroundAgentTools(manager) {
15083
+ const checkAgentStatus = {
15084
+ toolFn: async (args) => {
15085
+ const { job_id } = args;
15086
+ if (!job_id) throw new Error("check_agent_status: job_id is required");
15087
+ const job = manager.getJob(job_id);
15088
+ if (!job) return `No background agent found with ID: ${job_id}`;
15089
+ switch (job.status) {
15090
+ case "queued":
15091
+ return `Agent "${job.agentName}" is queued (waiting for a concurrency slot). Task: ${job.task}`;
15092
+ case "running": {
15093
+ const elapsed = Math.round((Date.now() - job.startTime) / 1e3);
15094
+ return `Agent "${job.agentName}" is still running (${elapsed}s elapsed). Task: ${job.task}`;
14566
15095
  }
14567
- return result;
14568
- } catch (error) {
14569
- if (command.hooks?.["on-error"]) {
14570
- const errorMessage = error instanceof Error ? error.message : String(error);
14571
- const hookResult = await executeHook(command.hooks["on-error"], {
14572
- skillName,
14573
- args: argsString,
14574
- error: errorMessage
14575
- });
14576
- if (hookResult.output) {
14577
- logger.warn(`On-error hook output: ${hookResult.output}`);
14578
- }
15096
+ case "completed": {
15097
+ const result = manager.getResult(job_id);
15098
+ return result?.summary || `Agent "${job.agentName}" completed but no summary available.`;
14579
15099
  }
14580
- throw error;
15100
+ case "failed":
15101
+ return `Agent "${job.agentName}" failed: ${job.error || "Unknown error"}`;
15102
+ case "cancelled":
15103
+ return `Agent "${job.agentName}" was cancelled.`;
14581
15104
  }
14582
15105
  },
14583
15106
  toolSchema: {
14584
- name: "skill",
14585
- description: `Execute a skill (custom slash command) within the conversation.
14586
-
14587
- **When to use this tool:**
14588
- - When a user asks you to use a skill by name (e.g., "use the review-pr skill")
14589
- - When a skill would help accomplish the user's request
14590
- - When you see /<skill-name> syntax in user messages
14591
-
14592
- **How it works:**
14593
- 1. Skills are loaded from markdown files in .bike4mind/commands/
14594
- 2. The skill template is expanded with argument substitution ($1, $2, $ARGUMENTS)
14595
- 3. File references (@filename) are resolved and content is injected
14596
- 4. The expanded template is returned for you to follow
14597
-
14598
- **Example invocations:**
14599
- - skill({ skill: "commit" }) - invoke commit skill
14600
- - skill({ skill: "review-pr", args: "123" }) - review PR #123
14601
- - skill({ skill: "feature-code-map", args: "authentication" }) - generate code map
14602
-
14603
- **Important:**
14604
- - Skill names can be with or without leading slash: "commit" or "/commit"
14605
- - Arguments are space-separated; use quotes for arguments with spaces
14606
- - The tool returns instructions to follow, not a final answer`,
15107
+ name: "check_agent_status",
15108
+ description: "Check the status and retrieve results of a background agent job. Use after spawning an agent with run_in_background: true.",
14607
15109
  parameters: {
14608
15110
  type: "object",
14609
15111
  properties: {
14610
- skill: {
15112
+ job_id: {
14611
15113
  type: "string",
14612
- description: 'Name of the skill to invoke (e.g., "commit", "review-pr")'
14613
- },
14614
- args: {
15114
+ description: "The job ID returned by agent_delegate when run_in_background was true"
15115
+ }
15116
+ },
15117
+ required: ["job_id"]
15118
+ }
15119
+ }
15120
+ };
15121
+ const listBackgroundAgents = {
15122
+ toolFn: async () => {
15123
+ const jobs = manager.listJobs();
15124
+ if (jobs.length === 0) return "No background agents.";
15125
+ return jobs.map((job) => {
15126
+ const elapsed = Math.round(((job.endTime || Date.now()) - job.startTime) / 1e3);
15127
+ const statusIcons = {
15128
+ queued: "\u{1F550}",
15129
+ running: "\u23F3",
15130
+ completed: "\u2705",
15131
+ failed: "\u274C",
15132
+ cancelled: "\u{1F6AB}"
15133
+ };
15134
+ const statusIcon = statusIcons[job.status] || "\u2753";
15135
+ return `${statusIcon} [${job.id}] ${job.agentName} (${job.status}, ${elapsed}s) - ${job.task.slice(0, 80)}`;
15136
+ }).join("\n");
15137
+ },
15138
+ toolSchema: {
15139
+ name: "list_background_agents",
15140
+ description: "List all background agent jobs with their current status.",
15141
+ parameters: {
15142
+ type: "object",
15143
+ properties: {}
15144
+ }
15145
+ }
15146
+ };
15147
+ const cancelBackgroundAgent = {
15148
+ toolFn: async (args) => {
15149
+ const { job_id } = args;
15150
+ if (!job_id) throw new Error("cancel_background_agent: job_id is required");
15151
+ const cancelled = manager.cancelJob(job_id);
15152
+ if (cancelled) return `Background agent ${job_id} has been cancelled.`;
15153
+ const job = manager.getJob(job_id);
15154
+ if (!job) return `No background agent found with ID: ${job_id}`;
15155
+ return `Cannot cancel agent ${job_id} - status is already "${job.status}".`;
15156
+ },
15157
+ toolSchema: {
15158
+ name: "cancel_background_agent",
15159
+ description: "Cancel a running background agent job.",
15160
+ parameters: {
15161
+ type: "object",
15162
+ properties: {
15163
+ job_id: {
14615
15164
  type: "string",
14616
- description: `Optional arguments as space-separated string. Use quotes for arguments containing spaces (e.g., '123 "bug fix"').`
15165
+ description: "The job ID of the background agent to cancel"
14617
15166
  }
14618
15167
  },
14619
- required: ["skill"]
15168
+ required: ["job_id"]
14620
15169
  }
14621
15170
  }
14622
15171
  };
15172
+ return [checkAgentStatus, listBackgroundAgents, cancelBackgroundAgent];
14623
15173
  }
14624
15174
 
14625
15175
  // src/tools/writeTodosTool.ts
@@ -14747,41 +15297,204 @@ function createTodoStore(onUpdate) {
14747
15297
  };
14748
15298
  }
14749
15299
 
14750
- // src/core/skillsPrompt.ts
14751
- function getSkillDisplayName(cmd) {
14752
- return cmd.displayName || cmd.name;
15300
+ // src/tools/findDefinitionTool.ts
15301
+ import { stat as stat3 } from "fs/promises";
15302
+ import path17 from "path";
15303
+ import { execFile as execFile2 } from "child_process";
15304
+ import { promisify as promisify2 } from "util";
15305
+ import { createRequire as createRequire2 } from "module";
15306
+ var execFileAsync2 = promisify2(execFile2);
15307
+ var require3 = createRequire2(import.meta.url);
15308
+ var VALID_KINDS = ["class", "function", "type", "interface", "variable", "enum", "struct", "module"];
15309
+ var KIND_KEYWORDS = {
15310
+ class: ["class"],
15311
+ function: ["function", "def", "func", "fn"],
15312
+ type: ["type"],
15313
+ interface: ["interface", "trait", "protocol"],
15314
+ variable: ["const", "let", "var"],
15315
+ enum: ["enum"],
15316
+ struct: ["struct"],
15317
+ module: ["module", "namespace", "package"]
15318
+ };
15319
+ var ALL_KEYWORDS = Object.values(KIND_KEYWORDS).flat();
15320
+ function getRipgrepPath2() {
15321
+ try {
15322
+ const ripgrepPath = require3.resolve("@vscode/ripgrep");
15323
+ const ripgrepDir = path17.dirname(ripgrepPath);
15324
+ return path17.join(ripgrepDir, "..", "bin", "rg" + (process.platform === "win32" ? ".exe" : ""));
15325
+ } catch {
15326
+ throw new Error(
15327
+ "ripgrep is not available. Please install @vscode/ripgrep: pnpm add @vscode/ripgrep --filter @bike4mind/services"
15328
+ );
15329
+ }
14753
15330
  }
14754
- function formatSkillEntry(cmd) {
14755
- const displayName = getSkillDisplayName(cmd);
14756
- const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
14757
- const nameDisplay = cmd.displayName && cmd.displayName !== cmd.name ? `**${displayName}** (\`${cmd.name}\`)` : `**${cmd.name}**`;
14758
- return `- ${nameDisplay}${argHint}: ${cmd.description}
14759
- `;
15331
+ function isPathWithinWorkspace2(targetPath, baseCwd) {
15332
+ const resolvedTarget = path17.resolve(targetPath);
15333
+ const resolvedBase = path17.resolve(baseCwd);
15334
+ const relativePath = path17.relative(resolvedBase, resolvedTarget);
15335
+ return !relativePath.startsWith("..") && !path17.isAbsolute(relativePath);
14760
15336
  }
14761
- function formatSkillGroup(heading, commands) {
14762
- if (commands.length === 0) {
14763
- return "";
15337
+ function escapeRegex(str) {
15338
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15339
+ }
15340
+ function buildDefinitionPattern(symbolName, kind) {
15341
+ const escaped = escapeRegex(symbolName);
15342
+ let keywords;
15343
+ if (kind) {
15344
+ if (!VALID_KINDS.includes(kind)) {
15345
+ throw new Error(`Invalid kind "${kind}". Valid kinds: ${VALID_KINDS.join(", ")}`);
15346
+ }
15347
+ keywords = KIND_KEYWORDS[kind];
15348
+ } else {
15349
+ keywords = ALL_KEYWORDS;
14764
15350
  }
14765
- return `
14766
- ### ${heading}
14767
- ${commands.map(formatSkillEntry).join("")}`;
15351
+ const keywordGroup = keywords.join("|");
15352
+ return `(export\\s+(default\\s+)?)?(abstract\\s+)?(${keywordGroup})\\s+${escaped}\\w*`;
14768
15353
  }
14769
- function filterAIVisibleSkills(commands) {
14770
- return commands.filter((cmd) => !cmd.disableModelInvocation);
15354
+ function isLikelyDefinition(line) {
15355
+ const trimmed = line.trim();
15356
+ if (/^\s*(\/\/|\/\*|\*|#|"""|''')/.test(trimmed)) return false;
15357
+ if (/\bimport\s/.test(trimmed) && !/\bexport\b/.test(trimmed)) return false;
15358
+ if (/\brequire\s*\(/.test(trimmed) && !/\b(const|let|var)\b/.test(trimmed)) return false;
15359
+ if (/\b(jest|vi|sinon)\.(mock|stub|spy)\b/.test(trimmed)) return false;
15360
+ return true;
14771
15361
  }
14772
- function buildSkillsPromptSection(commands) {
14773
- const visibleCommands = filterAIVisibleSkills(commands);
14774
- if (visibleCommands.length === 0) {
14775
- return "";
15362
+ async function findDefinitions(params) {
15363
+ const { symbol_name, kind, search_path } = params;
15364
+ if (!symbol_name || !symbol_name.trim()) {
15365
+ throw new Error("symbol_name is required");
14776
15366
  }
14777
- const projectSkills = visibleCommands.filter((c) => c.source === "project");
14778
- const globalSkills = visibleCommands.filter((c) => c.source === "global");
14779
- return `
15367
+ const baseCwd = process.cwd();
15368
+ const targetDir = search_path ? path17.resolve(baseCwd, search_path) : baseCwd;
15369
+ if (!isPathWithinWorkspace2(targetDir, baseCwd)) {
15370
+ throw new Error(`Path validation failed: "${search_path}" resolves outside the allowed workspace directory`);
15371
+ }
15372
+ try {
15373
+ const stats = await stat3(targetDir);
15374
+ if (!stats.isDirectory()) {
15375
+ throw new Error(`Path is not a directory: ${search_path}`);
15376
+ }
15377
+ } catch (error) {
15378
+ if (error.code === "ENOENT") {
15379
+ throw new Error(`Path does not exist: ${search_path}`);
15380
+ }
15381
+ throw error;
15382
+ }
15383
+ const rgPath = getRipgrepPath2();
15384
+ const pattern = buildDefinitionPattern(symbol_name.trim(), kind);
15385
+ const rgArgs = [
15386
+ "--json",
15387
+ "--max-count",
15388
+ "50",
15389
+ // Definitions are rare per file
15390
+ "--max-filesize",
15391
+ "5M",
15392
+ // Case-sensitive — definition names are exact (unlike grep_search)
15393
+ pattern,
15394
+ targetDir
15395
+ ];
15396
+ let stdout;
15397
+ try {
15398
+ const result2 = await execFileAsync2(rgPath, rgArgs, {
15399
+ maxBuffer: 10 * 1024 * 1024
15400
+ // 10MB — definitions produce far less output than grep
15401
+ });
15402
+ stdout = result2.stdout;
15403
+ } catch (error) {
15404
+ const execError = error;
15405
+ if (execError.code === 1) {
15406
+ stdout = execError.stdout || "";
15407
+ } else if (execError.code === 2) {
15408
+ throw new Error(`Ripgrep error: ${execError.stderr || "Unknown error"}`);
15409
+ } else {
15410
+ throw error;
15411
+ }
15412
+ }
15413
+ const lines = stdout.split("\n").filter(Boolean);
15414
+ const allMatches = [];
15415
+ for (const line of lines) {
15416
+ try {
15417
+ const item = JSON.parse(line);
15418
+ if (item.type === "match") {
15419
+ const match = item;
15420
+ const lineText = match.data.lines.text.trimEnd();
15421
+ if (isLikelyDefinition(lineText)) {
15422
+ allMatches.push({
15423
+ filePath: path17.relative(targetDir, match.data.path.text) || path17.basename(match.data.path.text),
15424
+ lineNumber: match.data.line_number,
15425
+ line: lineText
15426
+ });
15427
+ }
15428
+ }
15429
+ } catch {
15430
+ continue;
15431
+ }
15432
+ }
15433
+ if (allMatches.length === 0) {
15434
+ const kindInfo = kind ? ` (kind: "${kind}")` : "";
15435
+ const pathInfo = search_path ? ` in "${search_path}"` : "";
15436
+ return `No definitions found for "${symbol_name}"${kindInfo}${pathInfo}.
15437
+
15438
+ Suggestions:
15439
+ - The symbol may be from an external package (node_modules)
15440
+ ` + (kind ? "- Try without the kind filter\n" : "") + "- Check for alternate naming (e.g., camelCase vs PascalCase)";
15441
+ }
15442
+ const sorted = allMatches.sort((a, b) => {
15443
+ const aIsExport = /\bexport\b/.test(a.line) ? 0 : 1;
15444
+ const bIsExport = /\bexport\b/.test(b.line) ? 0 : 1;
15445
+ if (aIsExport !== bIsExport) return aIsExport - bIsExport;
15446
+ const aIsTest = /(\btest\b|\bspec\b|__tests__|\.test\.|\.spec\.)/.test(a.filePath) ? 1 : 0;
15447
+ const bIsTest = /(\btest\b|\bspec\b|__tests__|\.test\.|\.spec\.)/.test(b.filePath) ? 1 : 0;
15448
+ if (aIsTest !== bIsTest) return aIsTest - bIsTest;
15449
+ return a.filePath.localeCompare(b.filePath);
15450
+ });
15451
+ const maxResults = 20;
15452
+ const displayed = sorted.slice(0, maxResults);
15453
+ const truncated = sorted.length > maxResults;
15454
+ const count = sorted.length;
15455
+ let result = `Found ${count} definition${count === 1 ? "" : "s"} for "${symbol_name}"${truncated ? ` (showing top ${maxResults})` : ""}:
14780
15456
 
14781
- ## Available Skills
15457
+ `;
15458
+ for (const match of displayed) {
15459
+ const trimmed = match.line.trim();
15460
+ const displayLine = trimmed.length > 200 ? trimmed.slice(0, 200) + "..." : trimmed;
15461
+ result += `${match.filePath}:${match.lineNumber}
15462
+ ${displayLine}
14782
15463
 
14783
- Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
14784
- ` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
15464
+ `;
15465
+ }
15466
+ return result.trim();
15467
+ }
15468
+ function createFindDefinitionTool() {
15469
+ return {
15470
+ toolFn: async (args) => {
15471
+ const params = args;
15472
+ return findDefinitions(params);
15473
+ },
15474
+ toolSchema: {
15475
+ name: "find_definition",
15476
+ description: "Find where a class, function, type, or interface is defined in the codebase. Much faster and more precise than grep_search for definition lookups. Works across all languages (TypeScript, JavaScript, Python, Go, Rust, etc.) automatically.",
15477
+ parameters: {
15478
+ type: "object",
15479
+ properties: {
15480
+ symbol_name: {
15481
+ type: "string",
15482
+ description: 'The name (or prefix) of the symbol to find. Supports prefix matching \u2014 "Bedrock" finds "BedrockBackend", "BedrockClient", etc.'
15483
+ },
15484
+ kind: {
15485
+ type: "string",
15486
+ description: "Optional: narrow search to a specific kind of definition",
15487
+ enum: ["class", "function", "type", "interface", "variable", "enum", "struct", "module"]
15488
+ },
15489
+ search_path: {
15490
+ type: "string",
15491
+ description: 'Optional: directory to search within, relative to current working directory (e.g., "src/auth", "packages/core")'
15492
+ }
15493
+ },
15494
+ required: ["symbol_name"]
15495
+ }
15496
+ }
15497
+ };
14785
15498
  }
14786
15499
 
14787
15500
  // src/index.tsx
@@ -15069,6 +15782,7 @@ function CliApp() {
15069
15782
  configStore: state.configStore,
15070
15783
  apiClient,
15071
15784
  agentStore,
15785
+ customCommandStore: state.customCommandStore,
15072
15786
  enableParallelToolExecution: config.preferences.enableParallelToolExecution === true
15073
15787
  });
15074
15788
  const backgroundManager = new BackgroundAgentManager(orchestrator);
@@ -15090,7 +15804,8 @@ function CliApp() {
15090
15804
  subagentOrchestrator: orchestrator,
15091
15805
  sessionId: newSession.id
15092
15806
  }) : null;
15093
- const cliTools = [agentDelegateTool, ...backgroundTools, writeTodosTool];
15807
+ const findDefinitionTool = createFindDefinitionTool();
15808
+ const cliTools = [agentDelegateTool, ...backgroundTools, writeTodosTool, findDefinitionTool];
15094
15809
  if (skillTool) {
15095
15810
  cliTools.push(skillTool);
15096
15811
  }