@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/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 path16 = await context.imageGenerateStorage.upload(buffer, filename, {});
5219
- return path16;
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 path16 = await context.imageGenerateStorage.upload(buffer, filename, {});
6432
- return path16;
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 z144 = r * sinOmega * sinI;
7962
- return { x, y, z: z144, r };
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((fs13) => fs13.stat(args.path));
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(path16) {
10055
+ async download(path17) {
10036
10056
  throw new Error("Download not supported in CLI");
10037
10057
  }
10038
- async delete(path16) {
10058
+ async delete(path17) {
10039
10059
  }
10040
- async getSignedUrl(path16) {
10041
- return `/tmp/${path16}`;
10060
+ async getSignedUrl(path17) {
10061
+ return `/tmp/${path17}`;
10042
10062
  }
10043
- getPublicUrl(path16) {
10044
- return `/tmp/${path16}`;
10063
+ getPublicUrl(path17) {
10064
+ return `/tmp/${path17}`;
10045
10065
  }
10046
- async getPreview(path16) {
10047
- return `/tmp/${path16}`;
10066
+ async getPreview(path17) {
10067
+ return `/tmp/${path17}`;
10048
10068
  }
10049
- async getMetadata(path16) {
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-github-webhook-endpoint.17809+f7b8becbd",
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-github-webhook-endpoint.17809+f7b8becbd",
11674
- "@bike4mind/mcp": "1.23.1-feat-github-webhook-endpoint.17809+f7b8becbd",
11675
- "@bike4mind/services": "2.41.2-feat-github-webhook-endpoint.17809+f7b8becbd",
11676
- "@bike4mind/utils": "2.2.1-feat-github-webhook-endpoint.17809+f7b8becbd",
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: "f7b8becbd1ea101ceb0a0fe5463a370a6bb35ab5"
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 subagent.run()
11710
- * Use this to subscribe to agent events (e.g., subagent.on('action', handler))
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 subagent.run()
11717
- * Use this to unsubscribe from agent events (e.g., subagent.off('action', handler))
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 a specialized subagent
11931
+ * Delegate a task to an agent loaded from markdown definition
11724
11932
  *
11725
- * @param options - Configuration for subagent execution
11726
- * @returns Subagent result with summary
11933
+ * @param options - Configuration for agent execution
11934
+ * @returns Agent result with summary
11727
11935
  */
11728
- async delegateToSubagent(options) {
11729
- const { task, type, thoroughness, parentSessionId, config: configOverride } = options;
11730
- const baseConfig = this.deps.subagentConfigs.get(type);
11731
- if (!baseConfig) {
11732
- throw new Error(`No configuration found for subagent type: ${type}`);
11733
- }
11734
- const config = {
11735
- ...baseConfig,
11736
- ...configOverride,
11737
- type
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 model = config.model || "claude-3-5-haiku-20241022";
11740
- const maxIterations = this.getMaxIterations(config, thoroughness);
11949
+ const systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
11741
11950
  const toolFilter = {
11742
- allowedTools: config.allowedTools,
11743
- deniedTools: config.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: tools2, agentContext: updatedContext } = generateCliTools(
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
- this.deps.logger.debug(`Spawning ${type} subagent with ${tools2.length} tools, thoroughness: ${thoroughness}`);
11761
- const subagent = new ReActAgent({
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: tools2,
11976
+ model: agentDef.model,
11977
+ tools: filteredTools,
11767
11978
  maxIterations,
11768
- systemPrompt: config.systemPrompt || this.getDefaultSystemPrompt(type)
11979
+ systemPrompt
11769
11980
  });
11770
- updatedContext.currentAgent = subagent;
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(subagent, type);
11988
+ this.beforeRunCallback(agent, agentName);
11773
11989
  }
11774
11990
  const startTime = Date.now();
11775
- const result = await subagent.run(task, {
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(subagent, type);
12008
+ this.afterRunCallback(agent, agentName);
11781
12009
  }
11782
12010
  this.deps.logger.debug(
11783
- `Subagent completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
12011
+ `Agent "${agentName}" completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
11784
12012
  );
11785
- const summary = this.summarizeResult(result, type);
11786
- const subagentResult = {
12013
+ const summary = this.summarizeResult(result, agentDef);
12014
+ return {
11787
12015
  ...result,
11788
- subagentType: type,
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
- * Get default system prompt for subagent type
12023
+ * Substitute variables in system prompt
12024
+ * Reserved: $TASK, $ARGUMENTS, $1, $2, etc.
11809
12025
  */
11810
- getDefaultSystemPrompt(type) {
11811
- switch (type) {
11812
- case "explore":
11813
- return `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
11814
-
11815
- Focus on:
11816
- - Finding relevant files and functions
11817
- - Understanding code structure and patterns
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 subagent result for parent agent
12038
+ * Summarize agent result for parent agent
11852
12039
  */
11853
- summarizeResult(result, type) {
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
- let summary = `**${type.charAt(0).toUpperCase() + type.slice(1)} Subagent Results**
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
- if (type === "explore") {
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 = 1e3;
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/configs.ts
11881
- var EXPLORE_CONFIG = {
11882
- type: "explore",
11883
- model: "claude-3-5-haiku-20241022",
11884
- systemPrompt: `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
11885
-
11886
- Focus on:
11887
- - Finding relevant files and functions
11888
- - Understanding code structure and patterns
11889
- - Providing clear, concise summaries
11890
-
11891
- You have read-only access. Use file_read, grep_search, and glob_files to explore.
11892
-
11893
- CODE SEARCH EFFICIENCY:
11894
- 1. Start narrow with glob_files using specific patterns (e.g., "**/*Auth*.ts")
11895
- 2. Then use grep_search with precise regex on narrowed results
11896
- 3. Finally use file_read only on relevant files
11897
- 4. Leverage git log to find recent changes
11898
- 5. Check test files first to understand feature usage
11899
- 6. Batch glob operations instead of multiple separate calls
11900
-
11901
- When you find what you're looking for, provide a clear summary including:
11902
- 1. What you found (files, functions, patterns)
11903
- 2. Key insights or observations
11904
- 3. Relevant code locations
11905
-
11906
- Be thorough but concise. Your summary will be used by the main agent.`,
11907
- allowedTools: [
11908
- "file_read",
11909
- "grep_search",
11910
- "glob_files",
11911
- "bash_execute",
11912
- // Only for read-only commands like ls, cat, find
11913
- "current_datetime",
11914
- "math_evaluate"
11915
- ],
11916
- deniedTools: [
11917
- "create_file",
11918
- "edit_file",
11919
- "delete_file",
11920
- "web_search",
11921
- "weather_info",
11922
- "blog_publish",
11923
- "blog_edit",
11924
- "blog_draft"
11925
- ],
11926
- maxIterations: {
11927
- quick: 2,
11928
- medium: 5,
11929
- very_thorough: 10
11930
- },
11931
- defaultThoroughness: "medium"
11932
- };
11933
- var PLAN_CONFIG = {
11934
- type: "plan",
11935
- model: "claude-3-5-haiku-20241022",
11936
- systemPrompt: `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
11937
-
11938
- Focus on:
11939
- - Identifying dependencies and blockers
11940
- - Creating logical sequence of steps
11941
- - Estimating scope and priorities
11942
-
11943
- You have read-only access to analyze code.
11944
- You can explore the codebase to understand the current architecture before planning.
11945
-
11946
- Provide a structured plan that the main agent can execute.`,
11947
- allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime", "math_evaluate"],
11948
- deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
11949
- maxIterations: {
11950
- quick: 3,
11951
- medium: 7,
11952
- very_thorough: 12
11953
- },
11954
- defaultThoroughness: "medium"
11955
- };
11956
- var REVIEW_CONFIG = {
11957
- type: "review",
11958
- model: "claude-sonnet-4-5-20250929",
11959
- // Use latest Sonnet for better reasoning
11960
- systemPrompt: `You are a code review specialist. Your job is to analyze code quality and identify issues.
11961
-
11962
- Focus on:
11963
- - Code quality and best practices
11964
- - Potential bugs and edge cases
11965
- - Performance and security considerations
11966
-
11967
- You have read-only access to analyze code.
11968
-
11969
- Provide actionable feedback with specific file and line references.`,
11970
- allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime"],
11971
- deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
11972
- maxIterations: {
11973
- quick: 3,
11974
- medium: 8,
11975
- very_thorough: 15
11976
- },
11977
- defaultThoroughness: "medium"
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 createSubagentDelegateTool(orchestrator, parentSessionId) {
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("subagent_delegate: task parameter is required");
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.type) {
11996
- throw new Error("subagent_delegate: type parameter is required");
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 thoroughness = params.thoroughness || "medium";
11999
- const type = params.type;
12000
- const result = await orchestrator.delegateToSubagent({
12318
+ const result = await orchestrator.delegateToAgent({
12001
12319
  task: params.task,
12002
- type,
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: "subagent_delegate",
12010
- description: `Delegate a task to a specialized subagent for focused work.
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
- - **Explore**: When you need to search through the codebase, find files, or understand code structure (read-only)
12014
- - **Plan**: When you need to break down a complex task into actionable steps
12015
- - **Review**: When you need to analyze code quality and identify potential issues
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 subagent to do. Be specific about what you're looking for or what needs to be accomplished."
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
- type: {
12358
+ agent: {
12035
12359
  type: "string",
12036
- enum: ["explore", "plan", "review"],
12037
- description: `Type of subagent to use:
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", "type"]
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 subagentConfigs = getDefaultSubagentConfigs();
12446
- if (config.subagents) {
12447
- if (config.subagents.explore) {
12448
- const currentConfig = subagentConfigs.get("explore");
12449
- subagentConfigs.set("explore", { ...currentConfig, ...config.subagents.explore });
12450
- }
12451
- if (config.subagents.plan) {
12452
- const currentConfig = subagentConfigs.get("plan");
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
- subagentConfigs
12788
+ agentStore
12469
12789
  });
12470
- const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
12790
+ const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id);
12471
12791
  const todoStore = createTodoStore();
12472
12792
  const writeTodosTool = createWriteTodosTool(todoStore);
12473
- const allTools = [...b4mTools, ...mcpTools, subagentDelegateTool, writeTodosTool];
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");