@bike4mind/cli 0.2.19-feat-github-webhook-endpoint.17809 → 0.2.19-feat-unified-agent-system.17810
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/agents/defaults/explore.md +55 -0
- package/dist/agents/defaults/plan.md +51 -0
- package/dist/agents/defaults/review.md +50 -0
- package/dist/index.js +693 -272
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -529,6 +529,20 @@ var COMMANDS = [
|
|
|
529
529
|
name: "mcp",
|
|
530
530
|
description: "Show MCP server status and connected tools",
|
|
531
531
|
aliases: ["mcp:list"]
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "agents",
|
|
535
|
+
description: "List all available agents",
|
|
536
|
+
aliases: ["agents:list"]
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "agents:new",
|
|
540
|
+
description: "Create a new agent definition",
|
|
541
|
+
args: "<name>"
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "agents:reload",
|
|
545
|
+
description: "Reload agent definitions from disk"
|
|
532
546
|
}
|
|
533
547
|
];
|
|
534
548
|
function getAllCommandNames() {
|
|
@@ -2402,10 +2416,13 @@ var FrontmatterSchema = z.object({
|
|
|
2402
2416
|
description: z.string().optional(),
|
|
2403
2417
|
"argument-hint": z.string().optional(),
|
|
2404
2418
|
model: z.string().optional(),
|
|
2419
|
+
// Agent integration fields
|
|
2420
|
+
agent: z.string().optional(),
|
|
2421
|
+
thoroughness: z.enum(["quick", "medium", "very_thorough"]).optional(),
|
|
2422
|
+
variables: z.record(z.string()).optional(),
|
|
2405
2423
|
// Future fields (ignored for now):
|
|
2406
2424
|
"allowed-tools": z.any().optional(),
|
|
2407
2425
|
context: z.any().optional(),
|
|
2408
|
-
agent: z.any().optional(),
|
|
2409
2426
|
"disable-model-invocation": z.any().optional(),
|
|
2410
2427
|
hooks: z.any().optional()
|
|
2411
2428
|
});
|
|
@@ -2438,7 +2455,10 @@ function parseCommandFile(fileContent, filePath, commandName, source) {
|
|
|
2438
2455
|
model: validFrontmatter.model,
|
|
2439
2456
|
body: body.trim(),
|
|
2440
2457
|
source,
|
|
2441
|
-
filePath
|
|
2458
|
+
filePath,
|
|
2459
|
+
agent: validFrontmatter.agent,
|
|
2460
|
+
thoroughness: validFrontmatter.thoroughness,
|
|
2461
|
+
variables: validFrontmatter.variables
|
|
2442
2462
|
};
|
|
2443
2463
|
} catch (error) {
|
|
2444
2464
|
throw new Error(
|
|
@@ -5215,8 +5235,8 @@ async function processAndStoreImages(images, context) {
|
|
|
5215
5235
|
const buffer = await downloadImage(image);
|
|
5216
5236
|
const fileType = await fileTypeFromBuffer2(buffer);
|
|
5217
5237
|
const filename = `${uuidv45()}.${fileType?.ext}`;
|
|
5218
|
-
const
|
|
5219
|
-
return
|
|
5238
|
+
const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
5239
|
+
return path17;
|
|
5220
5240
|
}));
|
|
5221
5241
|
}
|
|
5222
5242
|
async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
|
|
@@ -6428,8 +6448,8 @@ async function processAndStoreImage(imageUrl, context) {
|
|
|
6428
6448
|
const buffer = await downloadImage2(imageUrl);
|
|
6429
6449
|
const fileType = await fileTypeFromBuffer3(buffer);
|
|
6430
6450
|
const filename = `${uuidv46()}.${fileType?.ext}`;
|
|
6431
|
-
const
|
|
6432
|
-
return
|
|
6451
|
+
const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
|
|
6452
|
+
return path17;
|
|
6433
6453
|
}
|
|
6434
6454
|
async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
|
|
6435
6455
|
await context.onFinish?.("edit_image", storedImagePath);
|
|
@@ -7958,8 +7978,8 @@ var getHeliocentricCoords = (planet, T) => {
|
|
|
7958
7978
|
const sinNode = Math.sin(longNode);
|
|
7959
7979
|
const x = r * (cosNode * cosOmega - sinNode * sinOmega * cosI);
|
|
7960
7980
|
const y = r * (sinNode * cosOmega + cosNode * sinOmega * cosI);
|
|
7961
|
-
const
|
|
7962
|
-
return { x, y, z:
|
|
7981
|
+
const z145 = r * sinOmega * sinI;
|
|
7982
|
+
return { x, y, z: z145, r };
|
|
7963
7983
|
};
|
|
7964
7984
|
var getGeocentricEcliptic = (planet, earth) => {
|
|
7965
7985
|
const dx = planet.x - earth.x;
|
|
@@ -9682,7 +9702,7 @@ async function generateFileDeletePreview(args) {
|
|
|
9682
9702
|
if (!existsSync7(args.path)) {
|
|
9683
9703
|
return `[File does not exist: ${args.path}]`;
|
|
9684
9704
|
}
|
|
9685
|
-
const stats = await import("fs/promises").then((
|
|
9705
|
+
const stats = await import("fs/promises").then((fs14) => fs14.stat(args.path));
|
|
9686
9706
|
return `[File will be deleted]
|
|
9687
9707
|
|
|
9688
9708
|
Path: ${args.path}
|
|
@@ -10032,21 +10052,21 @@ var NoOpStorage = class extends BaseStorage {
|
|
|
10032
10052
|
async upload(input, destination, options) {
|
|
10033
10053
|
return `/tmp/${destination}`;
|
|
10034
10054
|
}
|
|
10035
|
-
async download(
|
|
10055
|
+
async download(path17) {
|
|
10036
10056
|
throw new Error("Download not supported in CLI");
|
|
10037
10057
|
}
|
|
10038
|
-
async delete(
|
|
10058
|
+
async delete(path17) {
|
|
10039
10059
|
}
|
|
10040
|
-
async getSignedUrl(
|
|
10041
|
-
return `/tmp/${
|
|
10060
|
+
async getSignedUrl(path17) {
|
|
10061
|
+
return `/tmp/${path17}`;
|
|
10042
10062
|
}
|
|
10043
|
-
getPublicUrl(
|
|
10044
|
-
return `/tmp/${
|
|
10063
|
+
getPublicUrl(path17) {
|
|
10064
|
+
return `/tmp/${path17}`;
|
|
10045
10065
|
}
|
|
10046
|
-
async getPreview(
|
|
10047
|
-
return `/tmp/${
|
|
10066
|
+
async getPreview(path17) {
|
|
10067
|
+
return `/tmp/${path17}`;
|
|
10048
10068
|
}
|
|
10049
|
-
async getMetadata(
|
|
10069
|
+
async getMetadata(path17) {
|
|
10050
10070
|
return { size: 0, contentType: "application/octet-stream" };
|
|
10051
10071
|
}
|
|
10052
10072
|
};
|
|
@@ -11563,7 +11583,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
11563
11583
|
// package.json
|
|
11564
11584
|
var package_default = {
|
|
11565
11585
|
name: "@bike4mind/cli",
|
|
11566
|
-
version: "0.2.19-feat-
|
|
11586
|
+
version: "0.2.19-feat-unified-agent-system.17810+1e592a9b4",
|
|
11567
11587
|
type: "module",
|
|
11568
11588
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
11569
11589
|
license: "UNLICENSED",
|
|
@@ -11670,10 +11690,10 @@ var package_default = {
|
|
|
11670
11690
|
},
|
|
11671
11691
|
devDependencies: {
|
|
11672
11692
|
"@bike4mind/agents": "0.1.0",
|
|
11673
|
-
"@bike4mind/common": "2.43.1-feat-
|
|
11674
|
-
"@bike4mind/mcp": "1.23.1-feat-
|
|
11675
|
-
"@bike4mind/services": "2.41.2-feat-
|
|
11676
|
-
"@bike4mind/utils": "2.2.1-feat-
|
|
11693
|
+
"@bike4mind/common": "2.43.1-feat-unified-agent-system.17810+1e592a9b4",
|
|
11694
|
+
"@bike4mind/mcp": "1.23.1-feat-unified-agent-system.17810+1e592a9b4",
|
|
11695
|
+
"@bike4mind/services": "2.41.2-feat-unified-agent-system.17810+1e592a9b4",
|
|
11696
|
+
"@bike4mind/utils": "2.2.1-feat-unified-agent-system.17810+1e592a9b4",
|
|
11677
11697
|
"@types/better-sqlite3": "^7.6.13",
|
|
11678
11698
|
"@types/diff": "^5.0.9",
|
|
11679
11699
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -11690,7 +11710,7 @@ var package_default = {
|
|
|
11690
11710
|
optionalDependencies: {
|
|
11691
11711
|
"@vscode/ripgrep": "^1.17.0"
|
|
11692
11712
|
},
|
|
11693
|
-
gitHead: "
|
|
11713
|
+
gitHead: "1e592a9b411d5d9436c7ee8f49608dfdaaab3228"
|
|
11694
11714
|
};
|
|
11695
11715
|
|
|
11696
11716
|
// src/config/constants.ts
|
|
@@ -11698,6 +11718,194 @@ var USAGE_DAYS = 30;
|
|
|
11698
11718
|
var MODEL_NAME_COLUMN_WIDTH = 18;
|
|
11699
11719
|
var USAGE_CACHE_TTL = 5 * 60 * 1e3;
|
|
11700
11720
|
|
|
11721
|
+
// src/agents/types.ts
|
|
11722
|
+
import { z as z144 } from "zod";
|
|
11723
|
+
var ALWAYS_DENIED_FOR_AGENTS = [
|
|
11724
|
+
"agent_delegate"
|
|
11725
|
+
// No agent chaining
|
|
11726
|
+
];
|
|
11727
|
+
var HookDefinitionSchema = z144.object({
|
|
11728
|
+
type: z144.enum(["command", "prompt"]),
|
|
11729
|
+
command: z144.string().optional(),
|
|
11730
|
+
prompt: z144.string().optional(),
|
|
11731
|
+
timeout: z144.number().optional()
|
|
11732
|
+
});
|
|
11733
|
+
var HookMatcherSchema = z144.object({
|
|
11734
|
+
matcher: z144.string().optional(),
|
|
11735
|
+
hooks: z144.array(HookDefinitionSchema)
|
|
11736
|
+
});
|
|
11737
|
+
var AgentHooksSchema = z144.object({
|
|
11738
|
+
PreToolUse: z144.array(HookMatcherSchema).optional(),
|
|
11739
|
+
PostToolUse: z144.array(HookMatcherSchema).optional(),
|
|
11740
|
+
PostToolUseFailure: z144.array(HookMatcherSchema).optional(),
|
|
11741
|
+
Stop: z144.array(HookMatcherSchema).optional()
|
|
11742
|
+
}).optional();
|
|
11743
|
+
var AgentFrontmatterSchema = z144.object({
|
|
11744
|
+
description: z144.string().min(1, "Agent description is required"),
|
|
11745
|
+
model: z144.string().optional(),
|
|
11746
|
+
"allowed-tools": z144.array(z144.string()).optional(),
|
|
11747
|
+
"denied-tools": z144.array(z144.string()).optional(),
|
|
11748
|
+
"max-iterations": z144.object({
|
|
11749
|
+
quick: z144.number().int().positive().optional(),
|
|
11750
|
+
medium: z144.number().int().positive().optional(),
|
|
11751
|
+
very_thorough: z144.number().int().positive().optional()
|
|
11752
|
+
}).optional(),
|
|
11753
|
+
"default-thoroughness": z144.enum(["quick", "medium", "very_thorough"]).optional(),
|
|
11754
|
+
variables: z144.record(z144.string()).optional(),
|
|
11755
|
+
hooks: AgentHooksSchema
|
|
11756
|
+
});
|
|
11757
|
+
var DEFAULT_MAX_ITERATIONS = {
|
|
11758
|
+
quick: 2,
|
|
11759
|
+
medium: 5,
|
|
11760
|
+
very_thorough: 10
|
|
11761
|
+
};
|
|
11762
|
+
var DEFAULT_AGENT_MODEL = "claude-3-5-haiku-20241022";
|
|
11763
|
+
var DEFAULT_THOROUGHNESS = "medium";
|
|
11764
|
+
|
|
11765
|
+
// src/agents/toolFilter.ts
|
|
11766
|
+
function matchesToolPattern(toolName, pattern) {
|
|
11767
|
+
const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
11768
|
+
return new RegExp(`^${regexPattern}$`).test(toolName);
|
|
11769
|
+
}
|
|
11770
|
+
function matchesAnyPattern(toolName, patterns) {
|
|
11771
|
+
return patterns.some((pattern) => matchesToolPattern(toolName, pattern));
|
|
11772
|
+
}
|
|
11773
|
+
function filterToolsByPatterns(allTools, allowedPatterns, deniedPatterns) {
|
|
11774
|
+
return allTools.filter((tool) => {
|
|
11775
|
+
const toolName = tool.toolSchema.name;
|
|
11776
|
+
if (deniedPatterns && deniedPatterns.length > 0) {
|
|
11777
|
+
if (matchesAnyPattern(toolName, deniedPatterns)) {
|
|
11778
|
+
return false;
|
|
11779
|
+
}
|
|
11780
|
+
}
|
|
11781
|
+
if (!allowedPatterns || allowedPatterns.length === 0) {
|
|
11782
|
+
return true;
|
|
11783
|
+
}
|
|
11784
|
+
return matchesAnyPattern(toolName, allowedPatterns);
|
|
11785
|
+
});
|
|
11786
|
+
}
|
|
11787
|
+
|
|
11788
|
+
// src/agents/hookExecutor.ts
|
|
11789
|
+
import { spawn as spawn2 } from "child_process";
|
|
11790
|
+
var DEFAULT_HOOK_TIMEOUT_SECONDS = 60;
|
|
11791
|
+
async function executeCommandHook(hook, context) {
|
|
11792
|
+
if (!hook.command) {
|
|
11793
|
+
return { decision: "allow" };
|
|
11794
|
+
}
|
|
11795
|
+
const timeoutSeconds = hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS;
|
|
11796
|
+
const timeoutMs = timeoutSeconds * 1e3;
|
|
11797
|
+
return new Promise((resolve3) => {
|
|
11798
|
+
const child = spawn2("bash", ["-c", hook.command], {
|
|
11799
|
+
cwd: context.cwd,
|
|
11800
|
+
env: {
|
|
11801
|
+
...process.env,
|
|
11802
|
+
B4M_PROJECT_DIR: context.cwd,
|
|
11803
|
+
B4M_AGENT_NAME: context.agent_name,
|
|
11804
|
+
B4M_SESSION_ID: context.session_id
|
|
11805
|
+
},
|
|
11806
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
11807
|
+
});
|
|
11808
|
+
let stdout = "";
|
|
11809
|
+
let stderr = "";
|
|
11810
|
+
child.stdout.on("data", (data) => {
|
|
11811
|
+
stdout += data;
|
|
11812
|
+
});
|
|
11813
|
+
child.stderr.on("data", (data) => {
|
|
11814
|
+
stderr += data;
|
|
11815
|
+
});
|
|
11816
|
+
const timer = setTimeout(() => {
|
|
11817
|
+
child.kill("SIGTERM");
|
|
11818
|
+
resolve3({
|
|
11819
|
+
decision: "allow",
|
|
11820
|
+
reason: `Hook timed out after ${timeoutSeconds}s`
|
|
11821
|
+
});
|
|
11822
|
+
}, timeoutMs);
|
|
11823
|
+
child.on("close", (code) => {
|
|
11824
|
+
clearTimeout(timer);
|
|
11825
|
+
if (code === 2) {
|
|
11826
|
+
resolve3({
|
|
11827
|
+
decision: "deny",
|
|
11828
|
+
reason: stderr.trim() || "Hook blocked execution"
|
|
11829
|
+
});
|
|
11830
|
+
return;
|
|
11831
|
+
}
|
|
11832
|
+
if (code !== 0) {
|
|
11833
|
+
console.warn(`Hook exited with code ${code}: ${stderr.trim()}`);
|
|
11834
|
+
resolve3({ decision: "allow" });
|
|
11835
|
+
return;
|
|
11836
|
+
}
|
|
11837
|
+
try {
|
|
11838
|
+
const result = JSON.parse(stdout.trim());
|
|
11839
|
+
resolve3({
|
|
11840
|
+
decision: result.decision || "allow",
|
|
11841
|
+
reason: result.reason,
|
|
11842
|
+
updatedInput: result.updatedInput
|
|
11843
|
+
});
|
|
11844
|
+
} catch {
|
|
11845
|
+
resolve3({ decision: "allow" });
|
|
11846
|
+
}
|
|
11847
|
+
});
|
|
11848
|
+
child.on("error", (error) => {
|
|
11849
|
+
clearTimeout(timer);
|
|
11850
|
+
console.warn(`Hook execution error: ${error.message}`);
|
|
11851
|
+
resolve3({ decision: "allow" });
|
|
11852
|
+
});
|
|
11853
|
+
child.stdin.write(JSON.stringify(context));
|
|
11854
|
+
child.stdin.end();
|
|
11855
|
+
});
|
|
11856
|
+
}
|
|
11857
|
+
function matchesToolPattern2(toolName, pattern) {
|
|
11858
|
+
try {
|
|
11859
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
11860
|
+
return regex.test(toolName);
|
|
11861
|
+
} catch {
|
|
11862
|
+
return false;
|
|
11863
|
+
}
|
|
11864
|
+
}
|
|
11865
|
+
async function executeHooks(hooks, context) {
|
|
11866
|
+
if (!hooks || hooks.length === 0) {
|
|
11867
|
+
return { decision: "allow" };
|
|
11868
|
+
}
|
|
11869
|
+
const matchingHooks = [];
|
|
11870
|
+
for (const matcher of hooks) {
|
|
11871
|
+
const shouldMatch = !matcher.matcher || !context.tool_name || matchesToolPattern2(context.tool_name, matcher.matcher);
|
|
11872
|
+
if (shouldMatch) {
|
|
11873
|
+
matchingHooks.push(...matcher.hooks);
|
|
11874
|
+
}
|
|
11875
|
+
}
|
|
11876
|
+
if (matchingHooks.length === 0) {
|
|
11877
|
+
return { decision: "allow" };
|
|
11878
|
+
}
|
|
11879
|
+
const results = await Promise.all(
|
|
11880
|
+
matchingHooks.filter((hook) => hook.type === "command").map((hook) => executeCommandHook(hook, context))
|
|
11881
|
+
);
|
|
11882
|
+
for (const result of results) {
|
|
11883
|
+
if (result.decision === "deny" || result.decision === "block") {
|
|
11884
|
+
return result;
|
|
11885
|
+
}
|
|
11886
|
+
}
|
|
11887
|
+
let updatedInput;
|
|
11888
|
+
for (const result of results) {
|
|
11889
|
+
if (result.updatedInput) {
|
|
11890
|
+
updatedInput = { ...updatedInput, ...result.updatedInput };
|
|
11891
|
+
}
|
|
11892
|
+
}
|
|
11893
|
+
return { decision: "allow", updatedInput };
|
|
11894
|
+
}
|
|
11895
|
+
function buildHookContext(params) {
|
|
11896
|
+
return {
|
|
11897
|
+
session_id: params.sessionId,
|
|
11898
|
+
agent_name: params.agentName,
|
|
11899
|
+
cwd: params.cwd,
|
|
11900
|
+
hook_event_name: params.hookEventName,
|
|
11901
|
+
tool_name: params.toolName,
|
|
11902
|
+
tool_input: params.toolInput,
|
|
11903
|
+
tool_use_id: params.toolUseId,
|
|
11904
|
+
tool_result: params.toolResult,
|
|
11905
|
+
error: params.error
|
|
11906
|
+
};
|
|
11907
|
+
}
|
|
11908
|
+
|
|
11701
11909
|
// src/agents/SubagentOrchestrator.ts
|
|
11702
11910
|
var SubagentOrchestrator = class {
|
|
11703
11911
|
constructor(deps) {
|
|
@@ -11706,168 +11914,151 @@ var SubagentOrchestrator = class {
|
|
|
11706
11914
|
this.deps = deps;
|
|
11707
11915
|
}
|
|
11708
11916
|
/**
|
|
11709
|
-
* Set a callback to be invoked before each
|
|
11710
|
-
* Use this to subscribe to agent events (e.g.,
|
|
11917
|
+
* Set a callback to be invoked before each agent.run()
|
|
11918
|
+
* Use this to subscribe to agent events (e.g., agent.on('action', handler))
|
|
11711
11919
|
*/
|
|
11712
11920
|
setBeforeRunCallback(callback) {
|
|
11713
11921
|
this.beforeRunCallback = callback;
|
|
11714
11922
|
}
|
|
11715
11923
|
/**
|
|
11716
|
-
* Set a callback to be invoked after each
|
|
11717
|
-
* Use this to unsubscribe from agent events (e.g.,
|
|
11924
|
+
* Set a callback to be invoked after each agent.run()
|
|
11925
|
+
* Use this to unsubscribe from agent events (e.g., agent.off('action', handler))
|
|
11718
11926
|
*/
|
|
11719
11927
|
setAfterRunCallback(callback) {
|
|
11720
11928
|
this.afterRunCallback = callback;
|
|
11721
11929
|
}
|
|
11722
11930
|
/**
|
|
11723
|
-
* Delegate a task to
|
|
11931
|
+
* Delegate a task to an agent loaded from markdown definition
|
|
11724
11932
|
*
|
|
11725
|
-
* @param options - Configuration for
|
|
11726
|
-
* @returns
|
|
11933
|
+
* @param options - Configuration for agent execution
|
|
11934
|
+
* @returns Agent result with summary
|
|
11727
11935
|
*/
|
|
11728
|
-
async
|
|
11729
|
-
const { task,
|
|
11730
|
-
const
|
|
11731
|
-
if (!
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11936
|
+
async delegateToAgent(options) {
|
|
11937
|
+
const { task, agentName, thoroughness, variables, parentSessionId } = options;
|
|
11938
|
+
const agentDef = this.deps.agentStore.getAgent(agentName);
|
|
11939
|
+
if (!agentDef) {
|
|
11940
|
+
const available = this.deps.agentStore.getAgentNames().join(", ");
|
|
11941
|
+
throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
|
|
11942
|
+
}
|
|
11943
|
+
const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
|
|
11944
|
+
const maxIterations = agentDef.maxIterations[effectiveThoroughness];
|
|
11945
|
+
const effectiveVariables = {
|
|
11946
|
+
...agentDef.defaultVariables,
|
|
11947
|
+
...variables
|
|
11738
11948
|
};
|
|
11739
|
-
const
|
|
11740
|
-
const maxIterations = this.getMaxIterations(config, thoroughness);
|
|
11949
|
+
const systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
|
|
11741
11950
|
const toolFilter = {
|
|
11742
|
-
allowedTools:
|
|
11743
|
-
deniedTools:
|
|
11951
|
+
allowedTools: agentDef.allowedTools,
|
|
11952
|
+
deniedTools: [...agentDef.deniedTools || [], ...ALWAYS_DENIED_FOR_AGENTS]
|
|
11744
11953
|
};
|
|
11745
11954
|
const agentContext = {
|
|
11746
11955
|
currentAgent: null,
|
|
11747
11956
|
observationQueue: []
|
|
11748
11957
|
};
|
|
11749
|
-
const { tools:
|
|
11958
|
+
const { tools: allTools, agentContext: updatedContext } = generateCliTools(
|
|
11750
11959
|
this.deps.userId,
|
|
11751
11960
|
this.deps.llm,
|
|
11752
|
-
model,
|
|
11961
|
+
agentDef.model,
|
|
11753
11962
|
this.deps.permissionManager,
|
|
11754
11963
|
this.deps.showPermissionPrompt,
|
|
11755
11964
|
agentContext,
|
|
11756
11965
|
this.deps.configStore,
|
|
11757
|
-
this.deps.apiClient
|
|
11758
|
-
toolFilter
|
|
11966
|
+
this.deps.apiClient
|
|
11759
11967
|
);
|
|
11760
|
-
|
|
11761
|
-
|
|
11968
|
+
const filteredTools = filterToolsByPatterns(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
|
|
11969
|
+
this.deps.logger.debug(
|
|
11970
|
+
`Spawning "${agentName}" agent with ${filteredTools.length} tools, thoroughness: ${effectiveThoroughness}, max iterations: ${maxIterations}`
|
|
11971
|
+
);
|
|
11972
|
+
const agent = new ReActAgent({
|
|
11762
11973
|
userId: this.deps.userId,
|
|
11763
11974
|
logger: this.deps.logger,
|
|
11764
11975
|
llm: this.deps.llm,
|
|
11765
|
-
model,
|
|
11766
|
-
tools:
|
|
11976
|
+
model: agentDef.model,
|
|
11977
|
+
tools: filteredTools,
|
|
11767
11978
|
maxIterations,
|
|
11768
|
-
systemPrompt
|
|
11979
|
+
systemPrompt
|
|
11769
11980
|
});
|
|
11770
|
-
updatedContext.currentAgent =
|
|
11981
|
+
updatedContext.currentAgent = agent;
|
|
11982
|
+
const hookContext = {
|
|
11983
|
+
sessionId: parentSessionId,
|
|
11984
|
+
agentName,
|
|
11985
|
+
cwd: process.cwd()
|
|
11986
|
+
};
|
|
11771
11987
|
if (this.beforeRunCallback) {
|
|
11772
|
-
this.beforeRunCallback(
|
|
11988
|
+
this.beforeRunCallback(agent, agentName);
|
|
11773
11989
|
}
|
|
11774
11990
|
const startTime = Date.now();
|
|
11775
|
-
const result = await
|
|
11991
|
+
const result = await agent.run(task, {
|
|
11776
11992
|
maxIterations
|
|
11777
11993
|
});
|
|
11778
11994
|
const duration = Date.now() - startTime;
|
|
11995
|
+
if (agentDef.hooks?.Stop) {
|
|
11996
|
+
const stopResult = await executeHooks(
|
|
11997
|
+
agentDef.hooks.Stop,
|
|
11998
|
+
buildHookContext({
|
|
11999
|
+
...hookContext,
|
|
12000
|
+
hookEventName: "Stop"
|
|
12001
|
+
})
|
|
12002
|
+
);
|
|
12003
|
+
if (stopResult.decision === "block") {
|
|
12004
|
+
this.deps.logger.debug(`Stop hook blocked: ${stopResult.reason}`);
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
11779
12007
|
if (this.afterRunCallback) {
|
|
11780
|
-
this.afterRunCallback(
|
|
12008
|
+
this.afterRunCallback(agent, agentName);
|
|
11781
12009
|
}
|
|
11782
12010
|
this.deps.logger.debug(
|
|
11783
|
-
`
|
|
12011
|
+
`Agent "${agentName}" completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
|
|
11784
12012
|
);
|
|
11785
|
-
const summary = this.summarizeResult(result,
|
|
11786
|
-
|
|
12013
|
+
const summary = this.summarizeResult(result, agentDef);
|
|
12014
|
+
return {
|
|
11787
12015
|
...result,
|
|
11788
|
-
|
|
11789
|
-
thoroughness,
|
|
12016
|
+
agentName,
|
|
12017
|
+
thoroughness: effectiveThoroughness,
|
|
11790
12018
|
summary,
|
|
11791
12019
|
parentSessionId
|
|
11792
12020
|
};
|
|
11793
|
-
return subagentResult;
|
|
11794
|
-
}
|
|
11795
|
-
/**
|
|
11796
|
-
* Get max iterations based on thoroughness and config
|
|
11797
|
-
*/
|
|
11798
|
-
getMaxIterations(config, thoroughness) {
|
|
11799
|
-
const defaults = {
|
|
11800
|
-
quick: 2,
|
|
11801
|
-
medium: 5,
|
|
11802
|
-
very_thorough: 10
|
|
11803
|
-
};
|
|
11804
|
-
const configIterations = config.maxIterations || defaults;
|
|
11805
|
-
return configIterations[thoroughness];
|
|
11806
12021
|
}
|
|
11807
12022
|
/**
|
|
11808
|
-
*
|
|
12023
|
+
* Substitute variables in system prompt
|
|
12024
|
+
* Reserved: $TASK, $ARGUMENTS, $1, $2, etc.
|
|
11809
12025
|
*/
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
- Providing clear, concise summaries
|
|
11819
|
-
|
|
11820
|
-
You have read-only access. Use file_read, grep_search, and glob_files to explore.
|
|
11821
|
-
|
|
11822
|
-
When you find what you're looking for, provide a clear summary including:
|
|
11823
|
-
1. What you found (files, functions, patterns)
|
|
11824
|
-
2. Key insights or observations
|
|
11825
|
-
3. Relevant code locations
|
|
11826
|
-
|
|
11827
|
-
Be thorough but concise. Your summary will be used by the main agent.`;
|
|
11828
|
-
case "plan":
|
|
11829
|
-
return `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
|
|
11830
|
-
|
|
11831
|
-
Focus on:
|
|
11832
|
-
- Identifying dependencies and blockers
|
|
11833
|
-
- Creating logical sequence of steps
|
|
11834
|
-
- Estimating scope and priorities
|
|
11835
|
-
|
|
11836
|
-
Provide a structured plan that the main agent can execute.`;
|
|
11837
|
-
case "review":
|
|
11838
|
-
return `You are a code review specialist. Your job is to analyze code quality and identify issues.
|
|
11839
|
-
|
|
11840
|
-
Focus on:
|
|
11841
|
-
- Code quality and best practices
|
|
11842
|
-
- Potential bugs and edge cases
|
|
11843
|
-
- Performance and security considerations
|
|
11844
|
-
|
|
11845
|
-
Provide actionable feedback with specific file and line references.`;
|
|
11846
|
-
default:
|
|
11847
|
-
return "You are a helpful AI assistant.";
|
|
12026
|
+
substituteVariables(systemPrompt, task, variables) {
|
|
12027
|
+
let result = systemPrompt;
|
|
12028
|
+
result = result.replace(/\$TASK/g, task);
|
|
12029
|
+
if (variables) {
|
|
12030
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
12031
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12032
|
+
result = result.replace(new RegExp(`\\$${escapedKey}`, "g"), value);
|
|
12033
|
+
}
|
|
11848
12034
|
}
|
|
12035
|
+
return result;
|
|
11849
12036
|
}
|
|
11850
12037
|
/**
|
|
11851
|
-
* Summarize
|
|
12038
|
+
* Summarize agent result for parent agent
|
|
11852
12039
|
*/
|
|
11853
|
-
summarizeResult(result,
|
|
12040
|
+
summarizeResult(result, agentDef) {
|
|
11854
12041
|
const { finalAnswer, steps, completionInfo } = result;
|
|
11855
12042
|
const toolCalls = steps.filter((s) => s.type === "action");
|
|
11856
12043
|
const filesRead = toolCalls.filter((s) => s.metadata?.toolName === "file_read").length;
|
|
11857
12044
|
const searches = toolCalls.filter((s) => s.metadata?.toolName === "grep_search").length;
|
|
11858
12045
|
const globs = toolCalls.filter((s) => s.metadata?.toolName === "glob_files").length;
|
|
11859
|
-
|
|
12046
|
+
const capitalizedName = agentDef.name.charAt(0).toUpperCase() + agentDef.name.slice(1);
|
|
12047
|
+
let summary = `**${capitalizedName} Agent Results**
|
|
11860
12048
|
|
|
12049
|
+
`;
|
|
12050
|
+
summary += `*${agentDef.description}*
|
|
11861
12051
|
`;
|
|
11862
12052
|
summary += `*Execution: ${completionInfo.iterations} iterations, ${completionInfo.toolCalls} tool calls*
|
|
11863
12053
|
|
|
11864
12054
|
`;
|
|
11865
|
-
|
|
12055
|
+
const hasExplorationActivity = filesRead > 0 || searches > 0 || globs > 0;
|
|
12056
|
+
if (hasExplorationActivity) {
|
|
11866
12057
|
summary += `*Exploration: ${filesRead} files read, ${searches} searches, ${globs} glob patterns*
|
|
11867
12058
|
|
|
11868
12059
|
`;
|
|
11869
12060
|
}
|
|
11870
|
-
const maxLength =
|
|
12061
|
+
const maxLength = 1500;
|
|
11871
12062
|
if (finalAnswer.length > maxLength) {
|
|
11872
12063
|
summary += finalAnswer.slice(0, maxLength) + "\n\n...(truncated)";
|
|
11873
12064
|
} else {
|
|
@@ -11875,169 +12066,299 @@ Provide actionable feedback with specific file and line references.`;
|
|
|
11875
12066
|
}
|
|
11876
12067
|
return summary;
|
|
11877
12068
|
}
|
|
12069
|
+
/**
|
|
12070
|
+
* Get available agent names (for autocomplete/validation)
|
|
12071
|
+
*/
|
|
12072
|
+
getAvailableAgents() {
|
|
12073
|
+
return this.deps.agentStore.getAgentNames();
|
|
12074
|
+
}
|
|
12075
|
+
/**
|
|
12076
|
+
* Check if an agent exists
|
|
12077
|
+
*/
|
|
12078
|
+
hasAgent(name) {
|
|
12079
|
+
return this.deps.agentStore.hasAgent(name);
|
|
12080
|
+
}
|
|
11878
12081
|
};
|
|
11879
12082
|
|
|
11880
|
-
// src/agents/
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
}
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
}
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
12083
|
+
// src/agents/AgentStore.ts
|
|
12084
|
+
import fs13 from "fs/promises";
|
|
12085
|
+
import path16 from "path";
|
|
12086
|
+
import os3 from "os";
|
|
12087
|
+
import matter2 from "gray-matter";
|
|
12088
|
+
var AgentStore = class {
|
|
12089
|
+
/**
|
|
12090
|
+
* Creates a new AgentStore
|
|
12091
|
+
*
|
|
12092
|
+
* @param builtinDir - Directory containing built-in agent definitions
|
|
12093
|
+
* @param projectRoot - Root of the project (defaults to cwd)
|
|
12094
|
+
*/
|
|
12095
|
+
constructor(builtinDir, projectRoot) {
|
|
12096
|
+
this.agents = /* @__PURE__ */ new Map();
|
|
12097
|
+
this.builtinAgentsDir = builtinDir;
|
|
12098
|
+
this.globalAgentsDir = path16.join(os3.homedir(), ".bike4mind", "agents");
|
|
12099
|
+
this.projectAgentsDir = path16.join(projectRoot || process.cwd(), ".bike4mind", "agents");
|
|
12100
|
+
}
|
|
12101
|
+
/**
|
|
12102
|
+
* Load all agents from all directories
|
|
12103
|
+
* Precedence: project > global > builtin
|
|
12104
|
+
*/
|
|
12105
|
+
async loadAgents() {
|
|
12106
|
+
this.agents.clear();
|
|
12107
|
+
await this.loadAgentsFromDirectory(this.builtinAgentsDir, "builtin");
|
|
12108
|
+
await this.loadAgentsFromDirectory(this.globalAgentsDir, "global");
|
|
12109
|
+
await this.loadAgentsFromDirectory(this.projectAgentsDir, "project");
|
|
12110
|
+
}
|
|
12111
|
+
/**
|
|
12112
|
+
* Recursively load agents from a directory
|
|
12113
|
+
*/
|
|
12114
|
+
async loadAgentsFromDirectory(directory, source) {
|
|
12115
|
+
try {
|
|
12116
|
+
const stats = await fs13.stat(directory);
|
|
12117
|
+
if (!stats.isDirectory()) {
|
|
12118
|
+
return;
|
|
12119
|
+
}
|
|
12120
|
+
const files = await this.findAgentFiles(directory);
|
|
12121
|
+
for (const filePath of files) {
|
|
12122
|
+
try {
|
|
12123
|
+
const agent = await this.parseAgentFile(filePath, source);
|
|
12124
|
+
this.agents.set(agent.name, agent);
|
|
12125
|
+
} catch (error) {
|
|
12126
|
+
console.warn(
|
|
12127
|
+
`Failed to load agent from ${filePath}:`,
|
|
12128
|
+
error instanceof Error ? error.message : String(error)
|
|
12129
|
+
);
|
|
12130
|
+
}
|
|
12131
|
+
}
|
|
12132
|
+
} catch (error) {
|
|
12133
|
+
if (error.code !== "ENOENT") {
|
|
12134
|
+
console.warn(`Error accessing agents directory ${directory}`);
|
|
12135
|
+
}
|
|
12136
|
+
}
|
|
12137
|
+
}
|
|
12138
|
+
/**
|
|
12139
|
+
* Recursively find all .md files in directory
|
|
12140
|
+
*/
|
|
12141
|
+
async findAgentFiles(directory) {
|
|
12142
|
+
const files = [];
|
|
12143
|
+
try {
|
|
12144
|
+
const entries = await fs13.readdir(directory, { withFileTypes: true });
|
|
12145
|
+
for (const entry of entries) {
|
|
12146
|
+
const fullPath = path16.join(directory, entry.name);
|
|
12147
|
+
if (entry.isDirectory()) {
|
|
12148
|
+
const subFiles = await this.findAgentFiles(fullPath);
|
|
12149
|
+
files.push(...subFiles);
|
|
12150
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
12151
|
+
files.push(fullPath);
|
|
12152
|
+
}
|
|
12153
|
+
}
|
|
12154
|
+
} catch (error) {
|
|
12155
|
+
console.warn(`Error reading directory ${directory}:`, error instanceof Error ? error.message : String(error));
|
|
12156
|
+
}
|
|
12157
|
+
return files;
|
|
12158
|
+
}
|
|
12159
|
+
/**
|
|
12160
|
+
* Parse a single agent markdown file
|
|
12161
|
+
*/
|
|
12162
|
+
async parseAgentFile(filePath, source) {
|
|
12163
|
+
const content = await fs13.readFile(filePath, "utf-8");
|
|
12164
|
+
const { data: frontmatter, content: body } = matter2(content);
|
|
12165
|
+
const parsed = AgentFrontmatterSchema.parse(frontmatter);
|
|
12166
|
+
const name = path16.basename(filePath, ".md");
|
|
12167
|
+
return {
|
|
12168
|
+
name,
|
|
12169
|
+
description: parsed.description,
|
|
12170
|
+
model: parsed.model || DEFAULT_AGENT_MODEL,
|
|
12171
|
+
systemPrompt: body.trim(),
|
|
12172
|
+
allowedTools: parsed["allowed-tools"],
|
|
12173
|
+
deniedTools: parsed["denied-tools"],
|
|
12174
|
+
maxIterations: {
|
|
12175
|
+
quick: parsed["max-iterations"]?.quick ?? DEFAULT_MAX_ITERATIONS.quick,
|
|
12176
|
+
medium: parsed["max-iterations"]?.medium ?? DEFAULT_MAX_ITERATIONS.medium,
|
|
12177
|
+
very_thorough: parsed["max-iterations"]?.very_thorough ?? DEFAULT_MAX_ITERATIONS.very_thorough
|
|
12178
|
+
},
|
|
12179
|
+
defaultThoroughness: parsed["default-thoroughness"] || DEFAULT_THOROUGHNESS,
|
|
12180
|
+
defaultVariables: parsed.variables,
|
|
12181
|
+
hooks: parsed.hooks,
|
|
12182
|
+
source,
|
|
12183
|
+
filePath
|
|
12184
|
+
};
|
|
12185
|
+
}
|
|
12186
|
+
/**
|
|
12187
|
+
* Get an agent by name
|
|
12188
|
+
*/
|
|
12189
|
+
getAgent(name) {
|
|
12190
|
+
return this.agents.get(name);
|
|
12191
|
+
}
|
|
12192
|
+
/**
|
|
12193
|
+
* Get all loaded agents
|
|
12194
|
+
*/
|
|
12195
|
+
getAllAgents() {
|
|
12196
|
+
return Array.from(this.agents.values());
|
|
12197
|
+
}
|
|
12198
|
+
/**
|
|
12199
|
+
* Get available agent names (for autocomplete/validation)
|
|
12200
|
+
*/
|
|
12201
|
+
getAgentNames() {
|
|
12202
|
+
return Array.from(this.agents.keys());
|
|
12203
|
+
}
|
|
12204
|
+
/**
|
|
12205
|
+
* Get agents by source
|
|
12206
|
+
*/
|
|
12207
|
+
getAgentsBySource(source) {
|
|
12208
|
+
return this.getAllAgents().filter((agent) => agent.source === source);
|
|
12209
|
+
}
|
|
12210
|
+
/**
|
|
12211
|
+
* Check if an agent exists
|
|
12212
|
+
*/
|
|
12213
|
+
hasAgent(name) {
|
|
12214
|
+
return this.agents.has(name);
|
|
12215
|
+
}
|
|
12216
|
+
/**
|
|
12217
|
+
* Get the number of loaded agents
|
|
12218
|
+
*/
|
|
12219
|
+
getAgentCount() {
|
|
12220
|
+
return this.agents.size;
|
|
12221
|
+
}
|
|
12222
|
+
/**
|
|
12223
|
+
* Reload all agents
|
|
12224
|
+
*/
|
|
12225
|
+
async reloadAgents() {
|
|
12226
|
+
await this.loadAgents();
|
|
12227
|
+
}
|
|
12228
|
+
/**
|
|
12229
|
+
* Creates a new agent file from a template
|
|
12230
|
+
*
|
|
12231
|
+
* @param name - Agent name
|
|
12232
|
+
* @param isGlobal - If true, creates in global directory, otherwise project directory
|
|
12233
|
+
* @returns Path to the created file
|
|
12234
|
+
*/
|
|
12235
|
+
async createAgentFile(name, isGlobal = false) {
|
|
12236
|
+
const targetDir = isGlobal ? this.globalAgentsDir : this.projectAgentsDir;
|
|
12237
|
+
const filePath = path16.join(targetDir, `${name}.md`);
|
|
12238
|
+
try {
|
|
12239
|
+
await fs13.access(filePath);
|
|
12240
|
+
throw new Error(`Agent file already exists: ${filePath}`);
|
|
12241
|
+
} catch (error) {
|
|
12242
|
+
if (error.code !== "ENOENT") {
|
|
12243
|
+
throw error;
|
|
12244
|
+
}
|
|
12245
|
+
}
|
|
12246
|
+
await fs13.mkdir(targetDir, { recursive: true });
|
|
12247
|
+
const template = `---
|
|
12248
|
+
description: ${name} agent description
|
|
12249
|
+
model: claude-3-5-haiku-20241022
|
|
12250
|
+
allowed-tools:
|
|
12251
|
+
- file_read
|
|
12252
|
+
- grep_search
|
|
12253
|
+
- glob_files
|
|
12254
|
+
- bash_execute
|
|
12255
|
+
denied-tools:
|
|
12256
|
+
- create_file
|
|
12257
|
+
- edit_file
|
|
12258
|
+
- delete_file
|
|
12259
|
+
max-iterations:
|
|
12260
|
+
quick: 2
|
|
12261
|
+
medium: 5
|
|
12262
|
+
very_thorough: 10
|
|
12263
|
+
default-thoroughness: medium
|
|
12264
|
+
---
|
|
12265
|
+
|
|
12266
|
+
You are a ${name} specialist. Your job is to [describe primary task].
|
|
12267
|
+
|
|
12268
|
+
## Focus Areas
|
|
12269
|
+
- [Area 1]
|
|
12270
|
+
- [Area 2]
|
|
12271
|
+
- [Area 3]
|
|
12272
|
+
|
|
12273
|
+
## Instructions
|
|
12274
|
+
1. [Step 1]
|
|
12275
|
+
2. [Step 2]
|
|
12276
|
+
3. [Step 3]
|
|
12277
|
+
|
|
12278
|
+
## Output Format
|
|
12279
|
+
Describe the expected output format here.
|
|
12280
|
+
`;
|
|
12281
|
+
await fs13.writeFile(filePath, template, "utf-8");
|
|
12282
|
+
return filePath;
|
|
12283
|
+
}
|
|
12284
|
+
/**
|
|
12285
|
+
* Get summary of loaded agents by source (single-pass iteration)
|
|
12286
|
+
*/
|
|
12287
|
+
getSummary() {
|
|
12288
|
+
let builtin = 0;
|
|
12289
|
+
let global = 0;
|
|
12290
|
+
let project = 0;
|
|
12291
|
+
for (const agent of this.agents.values()) {
|
|
12292
|
+
if (agent.source === "builtin") builtin++;
|
|
12293
|
+
else if (agent.source === "global") global++;
|
|
12294
|
+
else project++;
|
|
12295
|
+
}
|
|
12296
|
+
return { builtin, global, project, total: this.agents.size };
|
|
12297
|
+
}
|
|
11978
12298
|
};
|
|
11979
|
-
function getDefaultSubagentConfigs() {
|
|
11980
|
-
return /* @__PURE__ */ new Map([
|
|
11981
|
-
["explore", EXPLORE_CONFIG],
|
|
11982
|
-
["plan", PLAN_CONFIG],
|
|
11983
|
-
["review", REVIEW_CONFIG]
|
|
11984
|
-
]);
|
|
11985
|
-
}
|
|
11986
12299
|
|
|
11987
12300
|
// src/agents/delegateTool.ts
|
|
11988
|
-
function
|
|
12301
|
+
function createAgentDelegateTool(orchestrator, agentStore, parentSessionId) {
|
|
12302
|
+
const agents = agentStore.getAllAgents();
|
|
12303
|
+
const agentDescriptions = agents.map((a) => `- **${a.name}**: ${a.description}`).join("\n");
|
|
12304
|
+
const agentNames = agentStore.getAgentNames();
|
|
11989
12305
|
return {
|
|
11990
12306
|
toolFn: async (args) => {
|
|
11991
12307
|
const params = args;
|
|
11992
12308
|
if (!params.task) {
|
|
11993
|
-
throw new Error("
|
|
12309
|
+
throw new Error("agent_delegate: task parameter is required");
|
|
12310
|
+
}
|
|
12311
|
+
if (!params.agent) {
|
|
12312
|
+
throw new Error("agent_delegate: agent parameter is required");
|
|
11994
12313
|
}
|
|
11995
|
-
if (!params.
|
|
11996
|
-
|
|
12314
|
+
if (!agentStore.hasAgent(params.agent)) {
|
|
12315
|
+
const available = agentNames.join(", ");
|
|
12316
|
+
throw new Error(`agent_delegate: unknown agent "${params.agent}". Available agents: ${available}`);
|
|
11997
12317
|
}
|
|
11998
|
-
const
|
|
11999
|
-
const type = params.type;
|
|
12000
|
-
const result = await orchestrator.delegateToSubagent({
|
|
12318
|
+
const result = await orchestrator.delegateToAgent({
|
|
12001
12319
|
task: params.task,
|
|
12002
|
-
|
|
12003
|
-
thoroughness,
|
|
12320
|
+
agentName: params.agent,
|
|
12321
|
+
thoroughness: params.thoroughness,
|
|
12322
|
+
variables: params.variables,
|
|
12004
12323
|
parentSessionId
|
|
12005
12324
|
});
|
|
12006
12325
|
return result.summary;
|
|
12007
12326
|
},
|
|
12008
12327
|
toolSchema: {
|
|
12009
|
-
name: "
|
|
12010
|
-
description: `Delegate a task to a specialized
|
|
12328
|
+
name: "agent_delegate",
|
|
12329
|
+
description: `Delegate a task to a specialized agent for focused work.
|
|
12330
|
+
|
|
12331
|
+
**Available Agents:**
|
|
12332
|
+
${agentDescriptions}
|
|
12011
12333
|
|
|
12012
12334
|
**When to use this tool:**
|
|
12013
|
-
- **
|
|
12014
|
-
- **
|
|
12015
|
-
- **
|
|
12335
|
+
- **explore**: When you need to search through the codebase, find files, or understand code structure (read-only)
|
|
12336
|
+
- **plan**: When you need to break down a complex task into actionable steps
|
|
12337
|
+
- **review**: When you need to analyze code quality and identify potential issues
|
|
12338
|
+
- Custom agents: Use for domain-specific tasks defined in project or global agent files
|
|
12016
12339
|
|
|
12017
12340
|
**Benefits:**
|
|
12018
12341
|
- Keeps main conversation focused and clean
|
|
12019
12342
|
- Uses specialized prompts optimized for each task type
|
|
12020
12343
|
- Faster execution with appropriate models (Haiku for explore/plan, Sonnet for review)
|
|
12344
|
+
- Supports custom variables for parameterized behavior
|
|
12021
12345
|
|
|
12022
12346
|
**Example uses:**
|
|
12023
|
-
- "Find all files that use the authentication system" \u2192 explore
|
|
12024
|
-
- "Search for components that handle user input" \u2192 explore
|
|
12025
|
-
- "Break down implementing a new feature into steps" \u2192 plan
|
|
12026
|
-
- "Review this module for potential bugs" \u2192 review`,
|
|
12347
|
+
- "Find all files that use the authentication system" \u2192 agent: explore
|
|
12348
|
+
- "Search for components that handle user input" \u2192 agent: explore
|
|
12349
|
+
- "Break down implementing a new feature into steps" \u2192 agent: plan
|
|
12350
|
+
- "Review this module for potential bugs" \u2192 agent: review`,
|
|
12027
12351
|
parameters: {
|
|
12028
12352
|
type: "object",
|
|
12029
12353
|
properties: {
|
|
12030
12354
|
task: {
|
|
12031
12355
|
type: "string",
|
|
12032
|
-
description: "Clear description of what you want the
|
|
12356
|
+
description: "Clear description of what you want the agent to do. Be specific about what you're looking for or what needs to be accomplished."
|
|
12033
12357
|
},
|
|
12034
|
-
|
|
12358
|
+
agent: {
|
|
12035
12359
|
type: "string",
|
|
12036
|
-
enum:
|
|
12037
|
-
description: `
|
|
12038
|
-
- explore: Read-only codebase exploration and search (fast, uses Haiku)
|
|
12039
|
-
- plan: Task breakdown and planning (uses Haiku)
|
|
12040
|
-
- review: Code quality analysis and review (uses Sonnet for better reasoning)`
|
|
12360
|
+
enum: agentNames,
|
|
12361
|
+
description: `Name of the agent to use for this task. Available: ${agentNames.join(", ")}`
|
|
12041
12362
|
},
|
|
12042
12363
|
thoroughness: {
|
|
12043
12364
|
type: "string",
|
|
@@ -12045,10 +12366,15 @@ function createSubagentDelegateTool(orchestrator, parentSessionId) {
|
|
|
12045
12366
|
description: `How thoroughly to execute:
|
|
12046
12367
|
- quick: Fast lookup, 1-2 iterations
|
|
12047
12368
|
- medium: Balanced exploration, 3-5 iterations (default)
|
|
12048
|
-
- very_thorough: Comprehensive analysis, 8-10 iterations`
|
|
12369
|
+
- very_thorough: Comprehensive analysis, 8-10+ iterations`
|
|
12370
|
+
},
|
|
12371
|
+
variables: {
|
|
12372
|
+
type: "object",
|
|
12373
|
+
additionalProperties: { type: "string" },
|
|
12374
|
+
description: 'Custom variables to substitute in agent system prompt. For example: { "DOMAIN": "authentication", "STRICTNESS": "high" }'
|
|
12049
12375
|
}
|
|
12050
12376
|
},
|
|
12051
|
-
required: ["task", "
|
|
12377
|
+
required: ["task", "agent"]
|
|
12052
12378
|
}
|
|
12053
12379
|
}
|
|
12054
12380
|
};
|
|
@@ -12211,6 +12537,7 @@ function CliApp() {
|
|
|
12211
12537
|
rewindSelector: null,
|
|
12212
12538
|
sessionSelector: null,
|
|
12213
12539
|
orchestrator: null,
|
|
12540
|
+
agentStore: null,
|
|
12214
12541
|
abortController: null
|
|
12215
12542
|
});
|
|
12216
12543
|
const [isInitialized, setIsInitialized] = useState8(false);
|
|
@@ -12442,21 +12769,14 @@ function CliApp() {
|
|
|
12442
12769
|
} else {
|
|
12443
12770
|
console.log(`\u{1F4E1} No MCP tools loaded`);
|
|
12444
12771
|
}
|
|
12445
|
-
const
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
subagentConfigs.set("plan", { ...currentConfig, ...config.subagents.plan });
|
|
12454
|
-
}
|
|
12455
|
-
if (config.subagents.review) {
|
|
12456
|
-
const currentConfig = subagentConfigs.get("review");
|
|
12457
|
-
subagentConfigs.set("review", { ...currentConfig, ...config.subagents.review });
|
|
12458
|
-
}
|
|
12459
|
-
}
|
|
12772
|
+
const builtinAgentsDir = new URL("./agents/defaults/", import.meta.url).pathname;
|
|
12773
|
+
const agentProjectDir = state.configStore.getProjectConfigDir();
|
|
12774
|
+
const agentStore = new AgentStore(builtinAgentsDir, agentProjectDir || process.cwd());
|
|
12775
|
+
await agentStore.loadAgents();
|
|
12776
|
+
const agentSummary = agentStore.getSummary();
|
|
12777
|
+
console.log(
|
|
12778
|
+
`\u{1F916} Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`
|
|
12779
|
+
);
|
|
12460
12780
|
const orchestrator = new SubagentOrchestrator({
|
|
12461
12781
|
userId: config.userId,
|
|
12462
12782
|
llm,
|
|
@@ -12465,13 +12785,12 @@ function CliApp() {
|
|
|
12465
12785
|
showPermissionPrompt: promptFn,
|
|
12466
12786
|
configStore: state.configStore,
|
|
12467
12787
|
apiClient,
|
|
12468
|
-
|
|
12788
|
+
agentStore
|
|
12469
12789
|
});
|
|
12470
|
-
const
|
|
12790
|
+
const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id);
|
|
12471
12791
|
const todoStore = createTodoStore();
|
|
12472
12792
|
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
12473
|
-
const allTools = [...b4mTools, ...mcpTools,
|
|
12474
|
-
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
12793
|
+
const allTools = [...b4mTools, ...mcpTools, agentDelegateTool, writeTodosTool];
|
|
12475
12794
|
logger.debug(
|
|
12476
12795
|
`Total tools available to agent: ${allTools.length} (${b4mTools.length} B4M + ${mcpTools.length} MCP + 2 CLI)`
|
|
12477
12796
|
);
|
|
@@ -12546,8 +12865,10 @@ ${contextResult.mergedContent}` : "";
|
|
|
12546
12865
|
// Store config for synchronous access
|
|
12547
12866
|
availableModels: models,
|
|
12548
12867
|
// Store models for ConfigEditor
|
|
12549
|
-
orchestrator
|
|
12868
|
+
orchestrator,
|
|
12550
12869
|
// Store orchestrator for step handler updates
|
|
12870
|
+
agentStore
|
|
12871
|
+
// Store agent store for agent management commands
|
|
12551
12872
|
}));
|
|
12552
12873
|
setStoreSession(newSession);
|
|
12553
12874
|
setIsInitialized(true);
|
|
@@ -12986,6 +13307,15 @@ ${output}` : output.trim() || "(no output)",
|
|
|
12986
13307
|
toolCallCount
|
|
12987
13308
|
};
|
|
12988
13309
|
};
|
|
13310
|
+
const displayAgentsBySource = (agents, label, emoji) => {
|
|
13311
|
+
if (agents.length > 0) {
|
|
13312
|
+
console.log(`${emoji} ${label}:`);
|
|
13313
|
+
agents.forEach((agent) => {
|
|
13314
|
+
console.log(` ${agent.name.padEnd(15)} - ${agent.description}`);
|
|
13315
|
+
});
|
|
13316
|
+
console.log("");
|
|
13317
|
+
}
|
|
13318
|
+
};
|
|
12989
13319
|
const handleCommand = async (command, args) => {
|
|
12990
13320
|
const customCommand = state.customCommandStore.getCommand(command);
|
|
12991
13321
|
if (customCommand) {
|
|
@@ -13002,6 +13332,25 @@ ${output}` : output.trim() || "(no output)",
|
|
|
13002
13332
|
console.log("\u{1F4DD} Expanded command:\n", substitutedBody);
|
|
13003
13333
|
}
|
|
13004
13334
|
const displayMessage = `/${command}${args.length > 0 ? " " + args.join(" ") : ""}`;
|
|
13335
|
+
if (customCommand.agent && state.orchestrator && state.agentStore) {
|
|
13336
|
+
if (!state.agentStore.hasAgent(customCommand.agent)) {
|
|
13337
|
+
const available = state.agentStore.getAgentNames().join(", ");
|
|
13338
|
+
console.error(`\u274C Unknown agent "${customCommand.agent}" specified in command.`);
|
|
13339
|
+
console.error(` Available agents: ${available}`);
|
|
13340
|
+
return;
|
|
13341
|
+
}
|
|
13342
|
+
console.log(`\u{1F916} Delegating to ${customCommand.agent} agent...
|
|
13343
|
+
`);
|
|
13344
|
+
const result = await state.orchestrator.delegateToAgent({
|
|
13345
|
+
task: substitutedBody,
|
|
13346
|
+
agentName: customCommand.agent,
|
|
13347
|
+
thoroughness: customCommand.thoroughness,
|
|
13348
|
+
variables: customCommand.variables,
|
|
13349
|
+
parentSessionId: state.session?.id || "unknown"
|
|
13350
|
+
});
|
|
13351
|
+
console.log("\n" + result.summary + "\n");
|
|
13352
|
+
return;
|
|
13353
|
+
}
|
|
13005
13354
|
if (customCommand.model && state.agent) {
|
|
13006
13355
|
console.log(`\u{1F504} Using model override: ${customCommand.model}`);
|
|
13007
13356
|
const originalModel = state.session?.model;
|
|
@@ -13623,6 +13972,78 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
13623
13972
|
setShowMcpViewer(true);
|
|
13624
13973
|
break;
|
|
13625
13974
|
}
|
|
13975
|
+
case "agents":
|
|
13976
|
+
case "agents:list": {
|
|
13977
|
+
if (!state.agentStore) {
|
|
13978
|
+
console.log("\u274C Agent store not initialized");
|
|
13979
|
+
break;
|
|
13980
|
+
}
|
|
13981
|
+
const summary = state.agentStore.getSummary();
|
|
13982
|
+
console.log("\n\u{1F916} Available Agents:\n");
|
|
13983
|
+
if (summary.total === 0) {
|
|
13984
|
+
console.log("No agents found.");
|
|
13985
|
+
console.log("\nTo create a custom agent:");
|
|
13986
|
+
console.log(" /agents:new <name> - Create a new agent");
|
|
13987
|
+
} else {
|
|
13988
|
+
displayAgentsBySource(state.agentStore.getAgentsBySource("builtin"), "Built-in Agents", "\u{1F4E6}");
|
|
13989
|
+
displayAgentsBySource(
|
|
13990
|
+
state.agentStore.getAgentsBySource("global"),
|
|
13991
|
+
"Global Agents (~/.bike4mind/agents/)",
|
|
13992
|
+
"\u{1F3E0}"
|
|
13993
|
+
);
|
|
13994
|
+
displayAgentsBySource(
|
|
13995
|
+
state.agentStore.getAgentsBySource("project"),
|
|
13996
|
+
"Project Agents (.bike4mind/agents/)",
|
|
13997
|
+
"\u{1F4C1}"
|
|
13998
|
+
);
|
|
13999
|
+
console.log(
|
|
14000
|
+
`Total: ${summary.total} agent${summary.total !== 1 ? "s" : ""} (${summary.builtin} built-in, ${summary.global} global, ${summary.project} project)`
|
|
14001
|
+
);
|
|
14002
|
+
}
|
|
14003
|
+
console.log("");
|
|
14004
|
+
break;
|
|
14005
|
+
}
|
|
14006
|
+
case "agents:new": {
|
|
14007
|
+
if (!state.agentStore) {
|
|
14008
|
+
console.log("\u274C Agent store not initialized");
|
|
14009
|
+
break;
|
|
14010
|
+
}
|
|
14011
|
+
const agentName = args[0];
|
|
14012
|
+
if (!agentName) {
|
|
14013
|
+
console.log("\u274C Please provide an agent name");
|
|
14014
|
+
console.log("Usage: /agents:new <name>");
|
|
14015
|
+
break;
|
|
14016
|
+
}
|
|
14017
|
+
console.log("\nWhere should this agent be stored?");
|
|
14018
|
+
console.log(" 1. \u{1F3E0} Global (~/.bike4mind/agents/) - available in all projects");
|
|
14019
|
+
console.log(" 2. \u{1F4C1} Project (.bike4mind/agents/) - team-shared");
|
|
14020
|
+
console.log("\nDefaulting to global. Creating agent file...");
|
|
14021
|
+
try {
|
|
14022
|
+
const filePath = await state.agentStore.createAgentFile(agentName, true);
|
|
14023
|
+
console.log(`\u2705 Created agent file: ${filePath}`);
|
|
14024
|
+
console.log("\nEdit this file to customize your agent.");
|
|
14025
|
+
console.log("Then run: /agents:reload to load it");
|
|
14026
|
+
} catch (error) {
|
|
14027
|
+
console.error("\u274C Failed to create agent file:", error instanceof Error ? error.message : String(error));
|
|
14028
|
+
}
|
|
14029
|
+
break;
|
|
14030
|
+
}
|
|
14031
|
+
case "agents:reload": {
|
|
14032
|
+
if (!state.agentStore) {
|
|
14033
|
+
console.log("\u274C Agent store not initialized");
|
|
14034
|
+
break;
|
|
14035
|
+
}
|
|
14036
|
+
try {
|
|
14037
|
+
await state.agentStore.reloadAgents();
|
|
14038
|
+
const summary = state.agentStore.getSummary();
|
|
14039
|
+
console.log(
|
|
14040
|
+
`\u2705 Reloaded ${summary.total} agent${summary.total !== 1 ? "s" : ""} (${summary.builtin} built-in, ${summary.global} global, ${summary.project} project)`
|
|
14041
|
+
);
|
|
14042
|
+
} catch (error) {
|
|
14043
|
+
console.error("\u274C Failed to reload agents:", error instanceof Error ? error.message : String(error));
|
|
14044
|
+
}
|
|
14045
|
+
break;
|
|
14046
|
+
}
|
|
13626
14047
|
default:
|
|
13627
14048
|
console.log(`Unknown command: /${command}`);
|
|
13628
14049
|
console.log("Type /help for available commands");
|