@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/bin/bike4mind-cli.mjs +1 -1
- package/dist/{artifactExtractor-VSHQYMJQ.js → artifactExtractor-75EEGBLM.js} +1 -1
- package/dist/{chunk-L42V7DCM.js → chunk-F3HPUK2I.js} +5 -3
- package/dist/{chunk-OHEBSPEW.js → chunk-HVF6LOKH.js} +2 -2
- package/dist/{chunk-I7ZMNVAN.js → chunk-MMAOFFE3.js} +224 -164
- package/dist/{chunk-3EQCYEKW.js → chunk-QD65IUNH.js} +2 -2
- package/dist/{chunk-Z7PQXS5G.js → chunk-UA7GLVP3.js} +2 -2
- package/dist/{create-UJLOXLQC.js → create-64AOQ36G.js} +3 -3
- package/dist/index.js +1167 -452
- package/dist/{llmMarkdownGenerator-PDEONFXK.js → llmMarkdownGenerator-CXBTJVHZ.js} +1 -1
- package/dist/{markdownGenerator-ODF3RWO6.js → markdownGenerator-P5HR3BBR.js} +1 -1
- package/dist/{mementoService-VYVPR6FI.js → mementoService-CGGNHR2A.js} +3 -3
- package/dist/{src-ZVULY6BY.js → src-VPA22RDR.js} +2 -2
- package/dist/{src-ET5ESBHL.js → src-ZA4LU3XB.js} +1 -1
- package/dist/{subtractCredits-3S7XTEHQ.js → subtractCredits-4CDISJCO.js} +3 -3
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getEffectiveApiKey,
|
|
6
6
|
getOpenWeatherKey,
|
|
7
7
|
getSerperKey
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
17
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
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
|
|
6061
|
-
return
|
|
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
|
|
7377
|
-
return
|
|
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
|
|
10516
|
-
if (
|
|
10517
|
-
return { ...file, ...
|
|
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(
|
|
11801
|
+
async download(path18) {
|
|
11320
11802
|
throw new Error("Download not supported in CLI");
|
|
11321
11803
|
}
|
|
11322
|
-
async delete(
|
|
11804
|
+
async delete(path18) {
|
|
11323
11805
|
}
|
|
11324
|
-
async getSignedUrl(
|
|
11325
|
-
return `/tmp/${
|
|
11806
|
+
async getSignedUrl(path18) {
|
|
11807
|
+
return `/tmp/${path18}`;
|
|
11326
11808
|
}
|
|
11327
|
-
getPublicUrl(
|
|
11328
|
-
return `/tmp/${
|
|
11809
|
+
getPublicUrl(path18) {
|
|
11810
|
+
return `/tmp/${path18}`;
|
|
11329
11811
|
}
|
|
11330
|
-
async getPreview(
|
|
11331
|
-
return `/tmp/${
|
|
11812
|
+
async getPreview(path18) {
|
|
11813
|
+
return `/tmp/${path18}`;
|
|
11332
11814
|
}
|
|
11333
|
-
async getMetadata(
|
|
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
|
|
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.
|
|
13319
|
-
"@bike4mind/mcp": "1.28.2
|
|
13320
|
-
"@bike4mind/services": "2.
|
|
13321
|
-
"@bike4mind/utils": "2.
|
|
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: "
|
|
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/
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
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
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
|
|
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
|
-
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
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
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
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
|
-
|
|
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: "
|
|
14585
|
-
description:
|
|
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
|
-
|
|
15112
|
+
job_id: {
|
|
14611
15113
|
type: "string",
|
|
14612
|
-
description:
|
|
14613
|
-
}
|
|
14614
|
-
|
|
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:
|
|
15165
|
+
description: "The job ID of the background agent to cancel"
|
|
14617
15166
|
}
|
|
14618
15167
|
},
|
|
14619
|
-
required: ["
|
|
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/
|
|
14751
|
-
|
|
14752
|
-
|
|
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
|
|
14755
|
-
const
|
|
14756
|
-
const
|
|
14757
|
-
const
|
|
14758
|
-
return
|
|
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
|
|
14762
|
-
|
|
14763
|
-
|
|
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
|
-
|
|
14766
|
-
|
|
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
|
|
14770
|
-
|
|
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
|
|
14773
|
-
const
|
|
14774
|
-
if (
|
|
14775
|
-
|
|
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
|
|
14778
|
-
const
|
|
14779
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14784
|
-
|
|
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
|
|
15807
|
+
const findDefinitionTool = createFindDefinitionTool();
|
|
15808
|
+
const cliTools = [agentDelegateTool, ...backgroundTools, writeTodosTool, findDefinitionTool];
|
|
15094
15809
|
if (skillTool) {
|
|
15095
15810
|
cliTools.push(skillTool);
|
|
15096
15811
|
}
|