@bike4mind/cli 0.2.19-feat-unified-agent-system.17813 → 0.2.19-feat-cli-skill-tool.17811

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
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-AZ7I44K7.js";
8
8
  import {
9
9
  ConfigStore
10
- } from "./chunk-FFJX3FF3.js";
10
+ } from "./chunk-VFQF2JIT.js";
11
11
  import "./chunk-KZNSOBD6.js";
12
12
  import "./chunk-E4K7LPQC.js";
13
13
  import {
@@ -529,20 +529,6 @@ 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"
546
532
  }
547
533
  ];
548
534
  function getAllCommandNames() {
@@ -2412,17 +2398,42 @@ import os from "os";
2412
2398
  // src/utils/commandParser.ts
2413
2399
  import matter from "gray-matter";
2414
2400
  import { z } from "zod";
2401
+ var flexibleString = z.union([z.string(), z.array(z.string())]).transform((val) => Array.isArray(val) ? val.join(" ") : val).optional();
2402
+ function needsQuoting(value) {
2403
+ const firstChar = value.charAt(0);
2404
+ const isAlreadyQuoted = firstChar === '"' || firstChar === "'";
2405
+ const isStructured = firstChar === "[" || firstChar === "{";
2406
+ const containsColon = value.includes(":");
2407
+ return containsColon && !isAlreadyQuoted && !isStructured;
2408
+ }
2409
+ function preprocessFrontmatter(content) {
2410
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
2411
+ if (!frontmatterMatch) return content;
2412
+ const frontmatter = frontmatterMatch[1];
2413
+ const processedLines = frontmatter.split("\n").map((line) => {
2414
+ const trimmedLine = line.trim();
2415
+ if (!trimmedLine || trimmedLine.startsWith("#")) return line;
2416
+ const match = line.match(/^(\s*)([a-zA-Z-_]+):\s*(.+)$/);
2417
+ if (!match) return line;
2418
+ const [, indent, key, value] = match;
2419
+ const trimmedValue = value.trim();
2420
+ if (!needsQuoting(trimmedValue)) return line;
2421
+ const escapedValue = trimmedValue.replace(/"/g, '\\"');
2422
+ return `${indent}${key}: "${escapedValue}"`;
2423
+ });
2424
+ const processedFrontmatter = processedLines.join("\n");
2425
+ return content.replace(/^---\r?\n[\s\S]*?\r?\n---/, `---
2426
+ ${processedFrontmatter}
2427
+ ---`);
2428
+ }
2415
2429
  var FrontmatterSchema = z.object({
2416
- description: z.string().optional(),
2417
- "argument-hint": z.string().optional(),
2430
+ description: flexibleString,
2431
+ "argument-hint": flexibleString,
2418
2432
  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(),
2423
2433
  // Future fields (ignored for now):
2424
2434
  "allowed-tools": z.any().optional(),
2425
2435
  context: z.any().optional(),
2436
+ agent: z.any().optional(),
2426
2437
  "disable-model-invocation": z.any().optional(),
2427
2438
  hooks: z.any().optional()
2428
2439
  });
@@ -2438,7 +2449,8 @@ function extractDescriptionFromBody(body) {
2438
2449
  }
2439
2450
  function parseCommandFile(fileContent, filePath, commandName, source) {
2440
2451
  try {
2441
- const { data: frontmatter, content: body } = matter(fileContent);
2452
+ const processedContent = preprocessFrontmatter(fileContent);
2453
+ const { data: frontmatter, content: body } = matter(processedContent);
2442
2454
  const validationResult = FrontmatterSchema.safeParse(frontmatter);
2443
2455
  if (!validationResult.success) {
2444
2456
  console.warn(
@@ -2455,10 +2467,7 @@ function parseCommandFile(fileContent, filePath, commandName, source) {
2455
2467
  model: validFrontmatter.model,
2456
2468
  body: body.trim(),
2457
2469
  source,
2458
- filePath,
2459
- agent: validFrontmatter.agent,
2460
- thoroughness: validFrontmatter.thoroughness,
2461
- variables: validFrontmatter.variables
2470
+ filePath
2462
2471
  };
2463
2472
  } catch (error) {
2464
2473
  throw new Error(
@@ -2488,17 +2497,32 @@ function extractCommandName(filename) {
2488
2497
  var CustomCommandStore = class {
2489
2498
  constructor(projectRoot) {
2490
2499
  this.commands = /* @__PURE__ */ new Map();
2491
- this.globalCommandsDir = path5.join(os.homedir(), ".bike4mind", "commands");
2492
- this.projectCommandsDir = path5.join(projectRoot || process.cwd(), ".bike4mind", "commands");
2500
+ const home = os.homedir();
2501
+ const root = projectRoot || process.cwd();
2502
+ this.globalCommandsDirs = [
2503
+ path5.join(home, ".bike4mind", "commands"),
2504
+ path5.join(home, ".claude", "commands"),
2505
+ path5.join(home, ".claude", "skills")
2506
+ ];
2507
+ this.projectCommandsDirs = [
2508
+ path5.join(root, ".bike4mind", "commands"),
2509
+ path5.join(root, ".claude", "commands"),
2510
+ path5.join(root, ".claude", "skills")
2511
+ ];
2493
2512
  }
2494
2513
  /**
2495
2514
  * Loads all custom commands from both global and project directories
2496
2515
  * Project commands override global commands with the same name
2516
+ * Later directories in each category override earlier ones with same name
2497
2517
  */
2498
2518
  async loadCommands() {
2499
2519
  this.commands.clear();
2500
- await this.loadCommandsFromDirectory(this.globalCommandsDir, "global");
2501
- await this.loadCommandsFromDirectory(this.projectCommandsDir, "project");
2520
+ for (const dir of this.globalCommandsDirs) {
2521
+ await this.loadCommandsFromDirectory(dir, "global");
2522
+ }
2523
+ for (const dir of this.projectCommandsDirs) {
2524
+ await this.loadCommandsFromDirectory(dir, "project");
2525
+ }
2502
2526
  }
2503
2527
  /**
2504
2528
  * Recursively scans a directory for .md files and loads them as commands
@@ -2564,19 +2588,27 @@ var CustomCommandStore = class {
2564
2588
  */
2565
2589
  async loadCommandFile(filePath, source) {
2566
2590
  const filename = path5.basename(filePath);
2567
- const commandName = extractCommandName(filename);
2591
+ const isSkillFile = filename.toLowerCase() === "skill.md";
2592
+ const commandName = isSkillFile ? this.extractSkillName(filePath) : extractCommandName(filename);
2568
2593
  if (!commandName) {
2569
2594
  console.warn(`Invalid command filename: ${filename} (must end with .md and have valid name)`);
2570
2595
  return;
2571
2596
  }
2572
2597
  const fileContent = await fs5.readFile(filePath, "utf-8");
2573
2598
  const command = parseCommandFile(fileContent, filePath, commandName, source);
2574
- const existing = this.commands.get(commandName);
2575
- if (existing && existing.source === "project" && source === "global") {
2576
- return;
2577
- }
2578
2599
  this.commands.set(commandName, command);
2579
2600
  }
2601
+ /**
2602
+ * Extracts skill name from a SKILL.md file path
2603
+ * Uses the parent directory name as the skill name
2604
+ *
2605
+ * @param filePath - Full path to the SKILL.md file
2606
+ * @returns Skill name or null if invalid
2607
+ */
2608
+ extractSkillName(filePath) {
2609
+ const parentDir = path5.basename(path5.dirname(filePath));
2610
+ return parentDir && parentDir !== "skills" ? parentDir : null;
2611
+ }
2580
2612
  /**
2581
2613
  * Gets a command by name
2582
2614
  *
@@ -2635,15 +2667,14 @@ var CustomCommandStore = class {
2635
2667
  * @returns Path to the created file
2636
2668
  */
2637
2669
  async createCommandFile(name, isGlobal = false) {
2638
- const targetDir = isGlobal ? this.globalCommandsDir : this.projectCommandsDir;
2670
+ const targetDir = isGlobal ? this.globalCommandsDirs[0] : this.projectCommandsDirs[0];
2639
2671
  const filePath = path5.join(targetDir, `${name}.md`);
2640
- try {
2641
- await fs5.access(filePath);
2672
+ const fileExists = await fs5.access(filePath).then(
2673
+ () => true,
2674
+ () => false
2675
+ );
2676
+ if (fileExists) {
2642
2677
  throw new Error(`Command file already exists: ${filePath}`);
2643
- } catch (error) {
2644
- if (error.code !== "ENOENT") {
2645
- throw error;
2646
- }
2647
2678
  }
2648
2679
  await fs5.mkdir(targetDir, { recursive: true });
2649
2680
  const template = `---
@@ -5235,8 +5266,8 @@ async function processAndStoreImages(images, context) {
5235
5266
  const buffer = await downloadImage(image);
5236
5267
  const fileType = await fileTypeFromBuffer2(buffer);
5237
5268
  const filename = `${uuidv45()}.${fileType?.ext}`;
5238
- const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
5239
- return path17;
5269
+ const path16 = await context.imageGenerateStorage.upload(buffer, filename, {});
5270
+ return path16;
5240
5271
  }));
5241
5272
  }
5242
5273
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -6448,8 +6479,8 @@ async function processAndStoreImage(imageUrl, context) {
6448
6479
  const buffer = await downloadImage2(imageUrl);
6449
6480
  const fileType = await fileTypeFromBuffer3(buffer);
6450
6481
  const filename = `${uuidv46()}.${fileType?.ext}`;
6451
- const path17 = await context.imageGenerateStorage.upload(buffer, filename, {});
6452
- return path17;
6482
+ const path16 = await context.imageGenerateStorage.upload(buffer, filename, {});
6483
+ return path16;
6453
6484
  }
6454
6485
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
6455
6486
  await context.onFinish?.("edit_image", storedImagePath);
@@ -7978,8 +8009,8 @@ var getHeliocentricCoords = (planet, T) => {
7978
8009
  const sinNode = Math.sin(longNode);
7979
8010
  const x = r * (cosNode * cosOmega - sinNode * sinOmega * cosI);
7980
8011
  const y = r * (sinNode * cosOmega + cosNode * sinOmega * cosI);
7981
- const z145 = r * sinOmega * sinI;
7982
- return { x, y, z: z145, r };
8012
+ const z144 = r * sinOmega * sinI;
8013
+ return { x, y, z: z144, r };
7983
8014
  };
7984
8015
  var getGeocentricEcliptic = (planet, earth) => {
7985
8016
  const dx = planet.x - earth.x;
@@ -9702,7 +9733,7 @@ async function generateFileDeletePreview(args) {
9702
9733
  if (!existsSync7(args.path)) {
9703
9734
  return `[File does not exist: ${args.path}]`;
9704
9735
  }
9705
- const stats = await import("fs/promises").then((fs14) => fs14.stat(args.path));
9736
+ const stats = await import("fs/promises").then((fs13) => fs13.stat(args.path));
9706
9737
  return `[File will be deleted]
9707
9738
 
9708
9739
  Path: ${args.path}
@@ -10047,198 +10078,26 @@ async function executeTool(toolName, input, apiClient, localToolFn) {
10047
10078
  }
10048
10079
  }
10049
10080
 
10050
- // src/agents/hookExecutor.ts
10051
- import { spawn as spawn2 } from "child_process";
10052
- var DEFAULT_HOOK_TIMEOUT_SECONDS = 60;
10053
- async function executeCommandHook(hook, context) {
10054
- if (!hook.command) {
10055
- return { decision: "allow" };
10056
- }
10057
- const timeoutSeconds = hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS;
10058
- const timeoutMs = timeoutSeconds * 1e3;
10059
- return new Promise((resolve3) => {
10060
- const child = spawn2("bash", ["-c", hook.command], {
10061
- cwd: context.cwd,
10062
- env: {
10063
- ...process.env,
10064
- B4M_PROJECT_DIR: context.cwd,
10065
- B4M_AGENT_NAME: context.agent_name,
10066
- B4M_SESSION_ID: context.session_id
10067
- },
10068
- stdio: ["pipe", "pipe", "pipe"]
10069
- });
10070
- let stdout = "";
10071
- let stderr = "";
10072
- child.stdout.on("data", (data) => {
10073
- stdout += data;
10074
- });
10075
- child.stderr.on("data", (data) => {
10076
- stderr += data;
10077
- });
10078
- const timer = setTimeout(() => {
10079
- child.kill("SIGTERM");
10080
- resolve3({
10081
- decision: "allow",
10082
- reason: `Hook timed out after ${timeoutSeconds}s`
10083
- });
10084
- }, timeoutMs);
10085
- child.on("close", (code) => {
10086
- clearTimeout(timer);
10087
- if (code === 2) {
10088
- resolve3({
10089
- decision: "deny",
10090
- reason: stderr.trim() || "Hook blocked execution"
10091
- });
10092
- return;
10093
- }
10094
- if (code !== 0) {
10095
- console.warn(`Hook exited with code ${code}: ${stderr.trim()}`);
10096
- resolve3({ decision: "allow" });
10097
- return;
10098
- }
10099
- try {
10100
- const result = JSON.parse(stdout.trim());
10101
- resolve3({
10102
- decision: result.decision || "allow",
10103
- reason: result.reason,
10104
- updatedInput: result.updatedInput
10105
- });
10106
- } catch {
10107
- resolve3({ decision: "allow" });
10108
- }
10109
- });
10110
- child.on("error", (error) => {
10111
- clearTimeout(timer);
10112
- console.warn(`Hook execution error: ${error.message}`);
10113
- resolve3({ decision: "allow" });
10114
- });
10115
- child.stdin.write(JSON.stringify(context));
10116
- child.stdin.end();
10117
- });
10118
- }
10119
- function matchesToolPattern(toolName, pattern) {
10120
- try {
10121
- const regex = new RegExp(`^${pattern}$`);
10122
- return regex.test(toolName);
10123
- } catch {
10124
- return false;
10125
- }
10126
- }
10127
- async function executeHooks(hooks, context) {
10128
- if (!hooks || hooks.length === 0) {
10129
- return { decision: "allow" };
10130
- }
10131
- const matchingHooks = [];
10132
- for (const matcher of hooks) {
10133
- const shouldMatch = !matcher.matcher || !context.tool_name || matchesToolPattern(context.tool_name, matcher.matcher);
10134
- if (shouldMatch) {
10135
- matchingHooks.push(...matcher.hooks);
10136
- }
10137
- }
10138
- if (matchingHooks.length === 0) {
10139
- return { decision: "allow" };
10140
- }
10141
- const results = await Promise.all(
10142
- matchingHooks.filter((hook) => hook.type === "command").map((hook) => executeCommandHook(hook, context))
10143
- );
10144
- for (const result of results) {
10145
- if (result.decision === "deny" || result.decision === "block") {
10146
- return result;
10147
- }
10148
- }
10149
- let updatedInput;
10150
- for (const result of results) {
10151
- if (result.updatedInput) {
10152
- updatedInput = { ...updatedInput, ...result.updatedInput };
10153
- }
10154
- }
10155
- return { decision: "allow", updatedInput };
10156
- }
10157
- function buildHookContext(params) {
10158
- return {
10159
- session_id: params.sessionId,
10160
- agent_name: params.agentName,
10161
- cwd: params.cwd,
10162
- hook_event_name: params.hookEventName,
10163
- tool_name: params.toolName,
10164
- tool_input: params.toolInput,
10165
- tool_use_id: params.toolUseId,
10166
- tool_result: params.toolResult,
10167
- error: params.error
10168
- };
10169
- }
10170
-
10171
- // src/agents/types.ts
10172
- import { z as z143 } from "zod";
10173
- var HookBlockedError = class extends Error {
10174
- constructor(toolName, reason) {
10175
- super(`Hook blocked execution of ${toolName}: ${reason || "No reason provided"}`);
10176
- this.name = "HookBlockedError";
10177
- this.toolName = toolName;
10178
- }
10179
- };
10180
- var ALWAYS_DENIED_FOR_AGENTS = [
10181
- "agent_delegate"
10182
- // No agent chaining
10183
- ];
10184
- var HookDefinitionSchema = z143.object({
10185
- type: z143.enum(["command", "prompt"]),
10186
- command: z143.string().optional(),
10187
- prompt: z143.string().optional(),
10188
- timeout: z143.number().optional()
10189
- });
10190
- var HookMatcherSchema = z143.object({
10191
- matcher: z143.string().optional(),
10192
- hooks: z143.array(HookDefinitionSchema)
10193
- });
10194
- var AgentHooksSchema = z143.object({
10195
- PreToolUse: z143.array(HookMatcherSchema).optional(),
10196
- PostToolUse: z143.array(HookMatcherSchema).optional(),
10197
- PostToolUseFailure: z143.array(HookMatcherSchema).optional(),
10198
- Stop: z143.array(HookMatcherSchema).optional()
10199
- }).optional();
10200
- var AgentFrontmatterSchema = z143.object({
10201
- description: z143.string().min(1, "Agent description is required"),
10202
- model: z143.string().optional(),
10203
- "allowed-tools": z143.array(z143.string()).optional(),
10204
- "denied-tools": z143.array(z143.string()).optional(),
10205
- "max-iterations": z143.object({
10206
- quick: z143.number().int().positive().optional(),
10207
- medium: z143.number().int().positive().optional(),
10208
- very_thorough: z143.number().int().positive().optional()
10209
- }).optional(),
10210
- "default-thoroughness": z143.enum(["quick", "medium", "very_thorough"]).optional(),
10211
- variables: z143.record(z143.string()).optional(),
10212
- hooks: AgentHooksSchema
10213
- });
10214
- var DEFAULT_MAX_ITERATIONS = {
10215
- quick: 2,
10216
- medium: 5,
10217
- very_thorough: 10
10218
- };
10219
- var DEFAULT_AGENT_MODEL = "claude-3-5-haiku-20241022";
10220
- var DEFAULT_THOROUGHNESS = "medium";
10221
-
10222
10081
  // src/utils/toolsAdapter.ts
10223
10082
  var NoOpStorage = class extends BaseStorage {
10224
10083
  async upload(input, destination, options) {
10225
10084
  return `/tmp/${destination}`;
10226
10085
  }
10227
- async download(path17) {
10086
+ async download(path16) {
10228
10087
  throw new Error("Download not supported in CLI");
10229
10088
  }
10230
- async delete(path17) {
10089
+ async delete(path16) {
10231
10090
  }
10232
- async getSignedUrl(path17) {
10233
- return `/tmp/${path17}`;
10091
+ async getSignedUrl(path16) {
10092
+ return `/tmp/${path16}`;
10234
10093
  }
10235
- getPublicUrl(path17) {
10236
- return `/tmp/${path17}`;
10094
+ getPublicUrl(path16) {
10095
+ return `/tmp/${path16}`;
10237
10096
  }
10238
- async getPreview(path17) {
10239
- return `/tmp/${path17}`;
10097
+ async getPreview(path16) {
10098
+ return `/tmp/${path16}`;
10240
10099
  }
10241
- async getMetadata(path17) {
10100
+ async getMetadata(path16) {
10242
10101
  return { size: 0, contentType: "application/octet-stream" };
10243
10102
  }
10244
10103
  };
@@ -10333,75 +10192,6 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
10333
10192
  }
10334
10193
  };
10335
10194
  }
10336
- function wrapToolWithHooks(tool, hooks, hookContext) {
10337
- const hasToolHooks = hooks?.PreToolUse || hooks?.PostToolUse || hooks?.PostToolUseFailure;
10338
- if (!hasToolHooks) {
10339
- return tool;
10340
- }
10341
- const originalFn = tool.toolFn;
10342
- const toolName = tool.toolSchema.name;
10343
- return {
10344
- ...tool,
10345
- toolFn: async (args) => {
10346
- let finalArgs = args;
10347
- if (hooks.PreToolUse) {
10348
- const preResult = await executeHooks(
10349
- hooks.PreToolUse,
10350
- buildHookContext({
10351
- ...hookContext,
10352
- hookEventName: "PreToolUse",
10353
- toolName,
10354
- toolInput: args
10355
- })
10356
- );
10357
- if (preResult.decision === "deny") {
10358
- return `Tool execution denied by hook: ${preResult.reason || "No reason provided"}`;
10359
- }
10360
- if (preResult.decision === "block") {
10361
- throw new HookBlockedError(toolName, preResult.reason);
10362
- }
10363
- if (preResult.updatedInput) {
10364
- finalArgs = { ...args, ...preResult.updatedInput };
10365
- }
10366
- }
10367
- let observation;
10368
- try {
10369
- observation = await originalFn(finalArgs);
10370
- } catch (err) {
10371
- if (hooks.PostToolUseFailure) {
10372
- const error = err;
10373
- await executeHooks(
10374
- hooks.PostToolUseFailure,
10375
- buildHookContext({
10376
- ...hookContext,
10377
- hookEventName: "PostToolUseFailure",
10378
- toolName,
10379
- toolInput: finalArgs,
10380
- error: error.message
10381
- })
10382
- );
10383
- }
10384
- throw err;
10385
- }
10386
- if (hooks.PostToolUse) {
10387
- const postResult = await executeHooks(
10388
- hooks.PostToolUse,
10389
- buildHookContext({
10390
- ...hookContext,
10391
- hookEventName: "PostToolUse",
10392
- toolName,
10393
- toolInput: finalArgs,
10394
- toolResult: observation
10395
- })
10396
- );
10397
- if (postResult.decision === "block") {
10398
- throw new HookBlockedError(toolName, postResult.reason);
10399
- }
10400
- }
10401
- return observation;
10402
- }
10403
- };
10404
- }
10405
10195
  var TOOL_NAME_MAPPING = {
10406
10196
  // Claude Code -> B4M
10407
10197
  read: "file_read",
@@ -10519,8 +10309,8 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
10519
10309
  }
10520
10310
 
10521
10311
  // src/config/toolSafety.ts
10522
- import { z as z144 } from "zod";
10523
- var ToolCategorySchema = z144.enum([
10312
+ import { z as z143 } from "zod";
10313
+ var ToolCategorySchema = z143.enum([
10524
10314
  "auto_approve",
10525
10315
  // Safe tools that run automatically without permission
10526
10316
  "prompt_always",
@@ -10528,9 +10318,9 @@ var ToolCategorySchema = z144.enum([
10528
10318
  "prompt_default"
10529
10319
  // Tools that prompt by default but can be trusted
10530
10320
  ]);
10531
- var ToolSafetyConfigSchema = z144.object({
10532
- categories: z144.record(z144.string(), ToolCategorySchema),
10533
- trustedTools: z144.array(z144.string())
10321
+ var ToolSafetyConfigSchema = z143.object({
10322
+ categories: z143.record(z143.string(), ToolCategorySchema),
10323
+ trustedTools: z143.array(z143.string())
10534
10324
  });
10535
10325
  var DEFAULT_TOOL_CATEGORIES = {
10536
10326
  // ===== AUTO APPROVE (Safe tools) =====
@@ -11824,7 +11614,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
11824
11614
  // package.json
11825
11615
  var package_default = {
11826
11616
  name: "@bike4mind/cli",
11827
- version: "0.2.19-feat-unified-agent-system.17813+f0daae7c8",
11617
+ version: "0.2.19-feat-cli-skill-tool.17811+8e53e9310",
11828
11618
  type: "module",
11829
11619
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
11830
11620
  license: "UNLICENSED",
@@ -11931,10 +11721,10 @@ var package_default = {
11931
11721
  },
11932
11722
  devDependencies: {
11933
11723
  "@bike4mind/agents": "0.1.0",
11934
- "@bike4mind/common": "2.43.1-feat-unified-agent-system.17813+f0daae7c8",
11935
- "@bike4mind/mcp": "1.23.1-feat-unified-agent-system.17813+f0daae7c8",
11936
- "@bike4mind/services": "2.41.2-feat-unified-agent-system.17813+f0daae7c8",
11937
- "@bike4mind/utils": "2.2.1-feat-unified-agent-system.17813+f0daae7c8",
11724
+ "@bike4mind/common": "2.43.1-feat-cli-skill-tool.17811+8e53e9310",
11725
+ "@bike4mind/mcp": "1.23.1-feat-cli-skill-tool.17811+8e53e9310",
11726
+ "@bike4mind/services": "2.41.2-feat-cli-skill-tool.17811+8e53e9310",
11727
+ "@bike4mind/utils": "2.2.1-feat-cli-skill-tool.17811+8e53e9310",
11938
11728
  "@types/better-sqlite3": "^7.6.13",
11939
11729
  "@types/diff": "^5.0.9",
11940
11730
  "@types/jsonwebtoken": "^9.0.4",
@@ -11951,7 +11741,7 @@ var package_default = {
11951
11741
  optionalDependencies: {
11952
11742
  "@vscode/ripgrep": "^1.17.0"
11953
11743
  },
11954
- gitHead: "f0daae7c8a8ee4961bfdece2cc62628cdfe3cf01"
11744
+ gitHead: "8e53e9310a7e9eaeb0ef443047a638dccb93382e"
11955
11745
  };
11956
11746
 
11957
11747
  // src/config/constants.ts
@@ -11959,29 +11749,6 @@ var USAGE_DAYS = 30;
11959
11749
  var MODEL_NAME_COLUMN_WIDTH = 18;
11960
11750
  var USAGE_CACHE_TTL = 5 * 60 * 1e3;
11961
11751
 
11962
- // src/agents/toolFilter.ts
11963
- function matchesToolPattern2(toolName, pattern) {
11964
- const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
11965
- return new RegExp(`^${regexPattern}$`).test(toolName);
11966
- }
11967
- function matchesAnyPattern(toolName, patterns) {
11968
- return patterns.some((pattern) => matchesToolPattern2(toolName, pattern));
11969
- }
11970
- function filterToolsByPatterns(allTools, allowedPatterns, deniedPatterns) {
11971
- return allTools.filter((tool) => {
11972
- const toolName = tool.toolSchema.name;
11973
- if (deniedPatterns && deniedPatterns.length > 0) {
11974
- if (matchesAnyPattern(toolName, deniedPatterns)) {
11975
- return false;
11976
- }
11977
- }
11978
- if (!allowedPatterns || allowedPatterns.length === 0) {
11979
- return true;
11980
- }
11981
- return matchesAnyPattern(toolName, allowedPatterns);
11982
- });
11983
- }
11984
-
11985
11752
  // src/agents/SubagentOrchestrator.ts
11986
11753
  var SubagentOrchestrator = class {
11987
11754
  constructor(deps) {
@@ -11990,176 +11757,168 @@ var SubagentOrchestrator = class {
11990
11757
  this.deps = deps;
11991
11758
  }
11992
11759
  /**
11993
- * Set a callback to be invoked before each agent.run()
11994
- * Use this to subscribe to agent events (e.g., agent.on('action', handler))
11760
+ * Set a callback to be invoked before each subagent.run()
11761
+ * Use this to subscribe to agent events (e.g., subagent.on('action', handler))
11995
11762
  */
11996
11763
  setBeforeRunCallback(callback) {
11997
11764
  this.beforeRunCallback = callback;
11998
11765
  }
11999
11766
  /**
12000
- * Set a callback to be invoked after each agent.run()
12001
- * Use this to unsubscribe from agent events (e.g., agent.off('action', handler))
11767
+ * Set a callback to be invoked after each subagent.run()
11768
+ * Use this to unsubscribe from agent events (e.g., subagent.off('action', handler))
12002
11769
  */
12003
11770
  setAfterRunCallback(callback) {
12004
11771
  this.afterRunCallback = callback;
12005
11772
  }
12006
11773
  /**
12007
- * Delegate a task to an agent loaded from markdown definition
11774
+ * Delegate a task to a specialized subagent
12008
11775
  *
12009
- * @param options - Configuration for agent execution
12010
- * @returns Agent result with summary
11776
+ * @param options - Configuration for subagent execution
11777
+ * @returns Subagent result with summary
12011
11778
  */
12012
- async delegateToAgent(options) {
12013
- const { task, agentName, thoroughness, variables, parentSessionId } = options;
12014
- const agentDef = this.deps.agentStore.getAgent(agentName);
12015
- if (!agentDef) {
12016
- const available = this.deps.agentStore.getAgentNames().join(", ");
12017
- throw new Error(`Unknown agent: "${agentName}". Available agents: ${available}`);
12018
- }
12019
- const effectiveThoroughness = thoroughness || agentDef.defaultThoroughness;
12020
- const maxIterations = agentDef.maxIterations[effectiveThoroughness];
12021
- const effectiveVariables = {
12022
- ...agentDef.defaultVariables,
12023
- ...variables
11779
+ async delegateToSubagent(options) {
11780
+ const { task, type, thoroughness, parentSessionId, config: configOverride } = options;
11781
+ const baseConfig = this.deps.subagentConfigs.get(type);
11782
+ if (!baseConfig) {
11783
+ throw new Error(`No configuration found for subagent type: ${type}`);
11784
+ }
11785
+ const config = {
11786
+ ...baseConfig,
11787
+ ...configOverride,
11788
+ type
12024
11789
  };
12025
- const systemPrompt = this.substituteVariables(agentDef.systemPrompt, task, effectiveVariables);
11790
+ const model = config.model || "claude-3-5-haiku-20241022";
11791
+ const maxIterations = this.getMaxIterations(config, thoroughness);
12026
11792
  const toolFilter = {
12027
- allowedTools: agentDef.allowedTools,
12028
- deniedTools: [...agentDef.deniedTools || [], ...ALWAYS_DENIED_FOR_AGENTS]
11793
+ allowedTools: config.allowedTools,
11794
+ deniedTools: config.deniedTools
12029
11795
  };
12030
11796
  const agentContext = {
12031
11797
  currentAgent: null,
12032
11798
  observationQueue: []
12033
11799
  };
12034
- const { tools: allTools, agentContext: updatedContext } = generateCliTools(
11800
+ const { tools: tools2, agentContext: updatedContext } = generateCliTools(
12035
11801
  this.deps.userId,
12036
11802
  this.deps.llm,
12037
- agentDef.model,
11803
+ model,
12038
11804
  this.deps.permissionManager,
12039
11805
  this.deps.showPermissionPrompt,
12040
11806
  agentContext,
12041
11807
  this.deps.configStore,
12042
- this.deps.apiClient
12043
- );
12044
- const filteredTools = filterToolsByPatterns(allTools, toolFilter.allowedTools, toolFilter.deniedTools);
12045
- const hookWrapperContext = {
12046
- sessionId: parentSessionId,
12047
- agentName,
12048
- cwd: process.cwd()
12049
- };
12050
- const hookedTools = filteredTools.map((tool) => wrapToolWithHooks(tool, agentDef.hooks, hookWrapperContext));
12051
- this.deps.logger.debug(
12052
- `Spawning "${agentName}" agent with ${hookedTools.length} tools, thoroughness: ${effectiveThoroughness}, max iterations: ${maxIterations}`
11808
+ this.deps.apiClient,
11809
+ toolFilter
12053
11810
  );
12054
- const agent = new ReActAgent({
11811
+ this.deps.logger.debug(`Spawning ${type} subagent with ${tools2.length} tools, thoroughness: ${thoroughness}`);
11812
+ const subagent = new ReActAgent({
12055
11813
  userId: this.deps.userId,
12056
11814
  logger: this.deps.logger,
12057
11815
  llm: this.deps.llm,
12058
- model: agentDef.model,
12059
- tools: hookedTools,
11816
+ model,
11817
+ tools: tools2,
12060
11818
  maxIterations,
12061
- systemPrompt
11819
+ systemPrompt: config.systemPrompt || this.getDefaultSystemPrompt(type)
12062
11820
  });
12063
- updatedContext.currentAgent = agent;
11821
+ updatedContext.currentAgent = subagent;
12064
11822
  if (this.beforeRunCallback) {
12065
- this.beforeRunCallback(agent, agentName);
11823
+ this.beforeRunCallback(subagent, type);
12066
11824
  }
12067
11825
  const startTime = Date.now();
12068
- let result;
12069
- try {
12070
- result = await agent.run(task, {
12071
- maxIterations
12072
- });
12073
- } catch (error) {
12074
- if (error instanceof HookBlockedError) {
12075
- if (this.afterRunCallback) {
12076
- this.afterRunCallback(agent, agentName);
12077
- }
12078
- return {
12079
- agentName,
12080
- thoroughness: effectiveThoroughness,
12081
- summary: `Agent blocked: ${error.message}`,
12082
- parentSessionId,
12083
- finalAnswer: error.message,
12084
- steps: [],
12085
- completionInfo: {
12086
- totalTokens: 0,
12087
- iterations: 0,
12088
- toolCalls: 0,
12089
- reachedMaxIterations: false
12090
- }
12091
- };
12092
- }
12093
- throw error;
12094
- }
11826
+ const result = await subagent.run(task, {
11827
+ maxIterations
11828
+ });
12095
11829
  const duration = Date.now() - startTime;
12096
- if (agentDef.hooks?.Stop) {
12097
- const stopResult = await executeHooks(
12098
- agentDef.hooks.Stop,
12099
- buildHookContext({
12100
- ...hookWrapperContext,
12101
- hookEventName: "Stop"
12102
- })
12103
- );
12104
- if (stopResult.decision === "block") {
12105
- this.deps.logger.debug(`Stop hook blocked: ${stopResult.reason}`);
12106
- }
12107
- }
12108
11830
  if (this.afterRunCallback) {
12109
- this.afterRunCallback(agent, agentName);
11831
+ this.afterRunCallback(subagent, type);
12110
11832
  }
12111
11833
  this.deps.logger.debug(
12112
- `Agent "${agentName}" completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
11834
+ `Subagent completed in ${duration}ms, ${result.completionInfo.iterations} iterations, ${result.completionInfo.totalTokens} tokens`
12113
11835
  );
12114
- const summary = this.summarizeResult(result, agentDef);
12115
- return {
11836
+ const summary = this.summarizeResult(result, type);
11837
+ const subagentResult = {
12116
11838
  ...result,
12117
- agentName,
12118
- thoroughness: effectiveThoroughness,
11839
+ subagentType: type,
11840
+ thoroughness,
12119
11841
  summary,
12120
11842
  parentSessionId
12121
11843
  };
11844
+ return subagentResult;
12122
11845
  }
12123
11846
  /**
12124
- * Substitute variables in system prompt
12125
- * Reserved: $TASK, $ARGUMENTS, $1, $2, etc.
11847
+ * Get max iterations based on thoroughness and config
12126
11848
  */
12127
- substituteVariables(systemPrompt, task, variables) {
12128
- let result = systemPrompt;
12129
- result = result.replace(/\$TASK/g, task);
12130
- if (variables) {
12131
- for (const [key, value] of Object.entries(variables)) {
12132
- const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12133
- result = result.replace(new RegExp(`\\$${escapedKey}`, "g"), value);
12134
- }
11849
+ getMaxIterations(config, thoroughness) {
11850
+ const defaults = {
11851
+ quick: 2,
11852
+ medium: 5,
11853
+ very_thorough: 10
11854
+ };
11855
+ const configIterations = config.maxIterations || defaults;
11856
+ return configIterations[thoroughness];
11857
+ }
11858
+ /**
11859
+ * Get default system prompt for subagent type
11860
+ */
11861
+ getDefaultSystemPrompt(type) {
11862
+ switch (type) {
11863
+ case "explore":
11864
+ return `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
11865
+
11866
+ Focus on:
11867
+ - Finding relevant files and functions
11868
+ - Understanding code structure and patterns
11869
+ - Providing clear, concise summaries
11870
+
11871
+ You have read-only access. Use file_read, grep_search, and glob_files to explore.
11872
+
11873
+ When you find what you're looking for, provide a clear summary including:
11874
+ 1. What you found (files, functions, patterns)
11875
+ 2. Key insights or observations
11876
+ 3. Relevant code locations
11877
+
11878
+ Be thorough but concise. Your summary will be used by the main agent.`;
11879
+ case "plan":
11880
+ return `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
11881
+
11882
+ Focus on:
11883
+ - Identifying dependencies and blockers
11884
+ - Creating logical sequence of steps
11885
+ - Estimating scope and priorities
11886
+
11887
+ Provide a structured plan that the main agent can execute.`;
11888
+ case "review":
11889
+ return `You are a code review specialist. Your job is to analyze code quality and identify issues.
11890
+
11891
+ Focus on:
11892
+ - Code quality and best practices
11893
+ - Potential bugs and edge cases
11894
+ - Performance and security considerations
11895
+
11896
+ Provide actionable feedback with specific file and line references.`;
11897
+ default:
11898
+ return "You are a helpful AI assistant.";
12135
11899
  }
12136
- return result;
12137
11900
  }
12138
11901
  /**
12139
- * Summarize agent result for parent agent
11902
+ * Summarize subagent result for parent agent
12140
11903
  */
12141
- summarizeResult(result, agentDef) {
11904
+ summarizeResult(result, type) {
12142
11905
  const { finalAnswer, steps, completionInfo } = result;
12143
11906
  const toolCalls = steps.filter((s) => s.type === "action");
12144
11907
  const filesRead = toolCalls.filter((s) => s.metadata?.toolName === "file_read").length;
12145
11908
  const searches = toolCalls.filter((s) => s.metadata?.toolName === "grep_search").length;
12146
11909
  const globs = toolCalls.filter((s) => s.metadata?.toolName === "glob_files").length;
12147
- const capitalizedName = agentDef.name.charAt(0).toUpperCase() + agentDef.name.slice(1);
12148
- let summary = `**${capitalizedName} Agent Results**
11910
+ let summary = `**${type.charAt(0).toUpperCase() + type.slice(1)} Subagent Results**
12149
11911
 
12150
- `;
12151
- summary += `*${agentDef.description}*
12152
11912
  `;
12153
11913
  summary += `*Execution: ${completionInfo.iterations} iterations, ${completionInfo.toolCalls} tool calls*
12154
11914
 
12155
11915
  `;
12156
- const hasExplorationActivity = filesRead > 0 || searches > 0 || globs > 0;
12157
- if (hasExplorationActivity) {
11916
+ if (type === "explore") {
12158
11917
  summary += `*Exploration: ${filesRead} files read, ${searches} searches, ${globs} glob patterns*
12159
11918
 
12160
11919
  `;
12161
11920
  }
12162
- const maxLength = 1500;
11921
+ const maxLength = 1e3;
12163
11922
  if (finalAnswer.length > maxLength) {
12164
11923
  summary += finalAnswer.slice(0, maxLength) + "\n\n...(truncated)";
12165
11924
  } else {
@@ -12167,311 +11926,169 @@ var SubagentOrchestrator = class {
12167
11926
  }
12168
11927
  return summary;
12169
11928
  }
12170
- /**
12171
- * Get available agent names (for autocomplete/validation)
12172
- */
12173
- getAvailableAgents() {
12174
- return this.deps.agentStore.getAgentNames();
12175
- }
12176
- /**
12177
- * Check if an agent exists
12178
- */
12179
- hasAgent(name) {
12180
- return this.deps.agentStore.hasAgent(name);
12181
- }
12182
11929
  };
12183
11930
 
12184
- // src/agents/AgentStore.ts
12185
- import fs13 from "fs/promises";
12186
- import path16 from "path";
12187
- import os3 from "os";
12188
- import matter2 from "gray-matter";
12189
- var AgentStore = class {
12190
- /**
12191
- * Creates a new AgentStore
12192
- *
12193
- * @param builtinDir - Directory containing built-in agent definitions
12194
- * @param projectRoot - Root of the project (defaults to cwd)
12195
- */
12196
- constructor(builtinDir, projectRoot) {
12197
- this.agents = /* @__PURE__ */ new Map();
12198
- const root = projectRoot || process.cwd();
12199
- const home = os3.homedir();
12200
- this.builtinAgentsDir = builtinDir;
12201
- this.globalB4MAgentsDir = path16.join(home, ".bike4mind", "agents");
12202
- this.globalClaudeAgentsDir = path16.join(home, ".claude", "agents");
12203
- this.projectB4MAgentsDir = path16.join(root, ".bike4mind", "agents");
12204
- this.projectClaudeAgentsDir = path16.join(root, ".claude", "agents");
12205
- }
12206
- /**
12207
- * Load all agents from all directories
12208
- * Precedence (lowest to highest):
12209
- * 1. builtin
12210
- * 2. global ~/.bike4mind/agents/
12211
- * 3. global ~/.claude/agents/
12212
- * 4. project .bike4mind/agents/
12213
- * 5. project .claude/agents/
12214
- */
12215
- async loadAgents() {
12216
- this.agents.clear();
12217
- await this.loadAgentsFromDirectory(this.builtinAgentsDir, "builtin");
12218
- await this.loadAgentsFromDirectory(this.globalB4MAgentsDir, "global");
12219
- await this.loadAgentsFromDirectory(this.globalClaudeAgentsDir, "global");
12220
- await this.loadAgentsFromDirectory(this.projectB4MAgentsDir, "project");
12221
- await this.loadAgentsFromDirectory(this.projectClaudeAgentsDir, "project");
12222
- }
12223
- /**
12224
- * Recursively load agents from a directory
12225
- */
12226
- async loadAgentsFromDirectory(directory, source) {
12227
- try {
12228
- const stats = await fs13.stat(directory);
12229
- if (!stats.isDirectory()) {
12230
- return;
12231
- }
12232
- const files = await this.findAgentFiles(directory);
12233
- for (const filePath of files) {
12234
- try {
12235
- const agent = await this.parseAgentFile(filePath, source);
12236
- this.agents.set(agent.name, agent);
12237
- } catch (error) {
12238
- console.warn(
12239
- `Failed to load agent from ${filePath}:`,
12240
- error instanceof Error ? error.message : String(error)
12241
- );
12242
- }
12243
- }
12244
- } catch (error) {
12245
- if (error.code !== "ENOENT") {
12246
- console.warn(`Error accessing agents directory ${directory}`);
12247
- }
12248
- }
12249
- }
12250
- /**
12251
- * Recursively find all .md files in directory
12252
- */
12253
- async findAgentFiles(directory) {
12254
- const files = [];
12255
- try {
12256
- const entries = await fs13.readdir(directory, { withFileTypes: true });
12257
- for (const entry of entries) {
12258
- const fullPath = path16.join(directory, entry.name);
12259
- if (entry.isDirectory()) {
12260
- const subFiles = await this.findAgentFiles(fullPath);
12261
- files.push(...subFiles);
12262
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
12263
- files.push(fullPath);
12264
- }
12265
- }
12266
- } catch (error) {
12267
- console.warn(`Error reading directory ${directory}:`, error instanceof Error ? error.message : String(error));
12268
- }
12269
- return files;
12270
- }
12271
- /**
12272
- * Parse a single agent markdown file
12273
- */
12274
- async parseAgentFile(filePath, source) {
12275
- const content = await fs13.readFile(filePath, "utf-8");
12276
- const { data: frontmatter, content: body } = matter2(content);
12277
- const parsed = AgentFrontmatterSchema.parse(frontmatter);
12278
- const name = path16.basename(filePath, ".md");
12279
- return {
12280
- name,
12281
- description: parsed.description,
12282
- model: parsed.model || DEFAULT_AGENT_MODEL,
12283
- systemPrompt: body.trim(),
12284
- allowedTools: parsed["allowed-tools"],
12285
- deniedTools: parsed["denied-tools"],
12286
- maxIterations: {
12287
- quick: parsed["max-iterations"]?.quick ?? DEFAULT_MAX_ITERATIONS.quick,
12288
- medium: parsed["max-iterations"]?.medium ?? DEFAULT_MAX_ITERATIONS.medium,
12289
- very_thorough: parsed["max-iterations"]?.very_thorough ?? DEFAULT_MAX_ITERATIONS.very_thorough
12290
- },
12291
- defaultThoroughness: parsed["default-thoroughness"] || DEFAULT_THOROUGHNESS,
12292
- defaultVariables: parsed.variables,
12293
- hooks: parsed.hooks,
12294
- source,
12295
- filePath
12296
- };
12297
- }
12298
- /**
12299
- * Get an agent by name
12300
- */
12301
- getAgent(name) {
12302
- return this.agents.get(name);
12303
- }
12304
- /**
12305
- * Get all loaded agents
12306
- */
12307
- getAllAgents() {
12308
- return Array.from(this.agents.values());
12309
- }
12310
- /**
12311
- * Get available agent names (for autocomplete/validation)
12312
- */
12313
- getAgentNames() {
12314
- return Array.from(this.agents.keys());
12315
- }
12316
- /**
12317
- * Get agents by source
12318
- */
12319
- getAgentsBySource(source) {
12320
- return this.getAllAgents().filter((agent) => agent.source === source);
12321
- }
12322
- /**
12323
- * Check if an agent exists
12324
- */
12325
- hasAgent(name) {
12326
- return this.agents.has(name);
12327
- }
12328
- /**
12329
- * Get the number of loaded agents
12330
- */
12331
- getAgentCount() {
12332
- return this.agents.size;
12333
- }
12334
- /**
12335
- * Reload all agents
12336
- */
12337
- async reloadAgents() {
12338
- await this.loadAgents();
12339
- }
12340
- /**
12341
- * Creates a new agent file from a template
12342
- *
12343
- * @param name - Agent name
12344
- * @param isGlobal - If true, creates in global directory, otherwise project directory
12345
- * @param useClaude - If true, uses .claude/agents/ convention, otherwise .bike4mind/agents/
12346
- * @returns Path to the created file
12347
- */
12348
- async createAgentFile(name, isGlobal = false, useClaude = true) {
12349
- const targetDir = isGlobal ? useClaude ? this.globalClaudeAgentsDir : this.globalB4MAgentsDir : useClaude ? this.projectClaudeAgentsDir : this.projectB4MAgentsDir;
12350
- const filePath = path16.join(targetDir, `${name}.md`);
12351
- try {
12352
- await fs13.access(filePath);
12353
- throw new Error(`Agent file already exists: ${filePath}`);
12354
- } catch (error) {
12355
- if (error.code !== "ENOENT") {
12356
- throw error;
12357
- }
12358
- }
12359
- await fs13.mkdir(targetDir, { recursive: true });
12360
- const template = `---
12361
- description: ${name} agent description
12362
- model: claude-3-5-haiku-20241022
12363
- allowed-tools:
12364
- - file_read
12365
- - grep_search
12366
- - glob_files
12367
- - bash_execute
12368
- denied-tools:
12369
- - create_file
12370
- - edit_file
12371
- - delete_file
12372
- max-iterations:
12373
- quick: 2
12374
- medium: 5
12375
- very_thorough: 10
12376
- default-thoroughness: medium
12377
- ---
12378
-
12379
- You are a ${name} specialist. Your job is to [describe primary task].
12380
-
12381
- ## Focus Areas
12382
- - [Area 1]
12383
- - [Area 2]
12384
- - [Area 3]
12385
-
12386
- ## Instructions
12387
- 1. [Step 1]
12388
- 2. [Step 2]
12389
- 3. [Step 3]
12390
-
12391
- ## Output Format
12392
- Describe the expected output format here.
12393
- `;
12394
- await fs13.writeFile(filePath, template, "utf-8");
12395
- return filePath;
12396
- }
12397
- /**
12398
- * Get summary of loaded agents by source (single-pass iteration)
12399
- */
12400
- getSummary() {
12401
- let builtin = 0;
12402
- let global = 0;
12403
- let project = 0;
12404
- for (const agent of this.agents.values()) {
12405
- if (agent.source === "builtin") builtin++;
12406
- else if (agent.source === "global") global++;
12407
- else project++;
12408
- }
12409
- return { builtin, global, project, total: this.agents.size };
12410
- }
11931
+ // src/agents/configs.ts
11932
+ var EXPLORE_CONFIG = {
11933
+ type: "explore",
11934
+ model: "claude-3-5-haiku-20241022",
11935
+ systemPrompt: `You are a code exploration specialist. Your job is to search and analyze codebases efficiently.
11936
+
11937
+ Focus on:
11938
+ - Finding relevant files and functions
11939
+ - Understanding code structure and patterns
11940
+ - Providing clear, concise summaries
11941
+
11942
+ You have read-only access. Use file_read, grep_search, and glob_files to explore.
11943
+
11944
+ CODE SEARCH EFFICIENCY:
11945
+ 1. Start narrow with glob_files using specific patterns (e.g., "**/*Auth*.ts")
11946
+ 2. Then use grep_search with precise regex on narrowed results
11947
+ 3. Finally use file_read only on relevant files
11948
+ 4. Leverage git log to find recent changes
11949
+ 5. Check test files first to understand feature usage
11950
+ 6. Batch glob operations instead of multiple separate calls
11951
+
11952
+ When you find what you're looking for, provide a clear summary including:
11953
+ 1. What you found (files, functions, patterns)
11954
+ 2. Key insights or observations
11955
+ 3. Relevant code locations
11956
+
11957
+ Be thorough but concise. Your summary will be used by the main agent.`,
11958
+ allowedTools: [
11959
+ "file_read",
11960
+ "grep_search",
11961
+ "glob_files",
11962
+ "bash_execute",
11963
+ // Only for read-only commands like ls, cat, find
11964
+ "current_datetime",
11965
+ "math_evaluate"
11966
+ ],
11967
+ deniedTools: [
11968
+ "create_file",
11969
+ "edit_file",
11970
+ "delete_file",
11971
+ "web_search",
11972
+ "weather_info",
11973
+ "blog_publish",
11974
+ "blog_edit",
11975
+ "blog_draft"
11976
+ ],
11977
+ maxIterations: {
11978
+ quick: 2,
11979
+ medium: 5,
11980
+ very_thorough: 10
11981
+ },
11982
+ defaultThoroughness: "medium"
11983
+ };
11984
+ var PLAN_CONFIG = {
11985
+ type: "plan",
11986
+ model: "claude-3-5-haiku-20241022",
11987
+ systemPrompt: `You are a task planning specialist. Your job is to break down complex tasks into clear, actionable steps.
11988
+
11989
+ Focus on:
11990
+ - Identifying dependencies and blockers
11991
+ - Creating logical sequence of steps
11992
+ - Estimating scope and priorities
11993
+
11994
+ You have read-only access to analyze code.
11995
+ You can explore the codebase to understand the current architecture before planning.
11996
+
11997
+ Provide a structured plan that the main agent can execute.`,
11998
+ allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime", "math_evaluate"],
11999
+ deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
12000
+ maxIterations: {
12001
+ quick: 3,
12002
+ medium: 7,
12003
+ very_thorough: 12
12004
+ },
12005
+ defaultThoroughness: "medium"
12411
12006
  };
12007
+ var REVIEW_CONFIG = {
12008
+ type: "review",
12009
+ model: "claude-sonnet-4-5-20250929",
12010
+ // Use latest Sonnet for better reasoning
12011
+ systemPrompt: `You are a code review specialist. Your job is to analyze code quality and identify issues.
12012
+
12013
+ Focus on:
12014
+ - Code quality and best practices
12015
+ - Potential bugs and edge cases
12016
+ - Performance and security considerations
12017
+
12018
+ You have read-only access to analyze code.
12019
+
12020
+ Provide actionable feedback with specific file and line references.`,
12021
+ allowedTools: ["file_read", "grep_search", "glob_files", "bash_execute", "current_datetime"],
12022
+ deniedTools: ["create_file", "edit_file", "delete_file", "web_search", "weather_info"],
12023
+ maxIterations: {
12024
+ quick: 3,
12025
+ medium: 8,
12026
+ very_thorough: 15
12027
+ },
12028
+ defaultThoroughness: "medium"
12029
+ };
12030
+ function getDefaultSubagentConfigs() {
12031
+ return /* @__PURE__ */ new Map([
12032
+ ["explore", EXPLORE_CONFIG],
12033
+ ["plan", PLAN_CONFIG],
12034
+ ["review", REVIEW_CONFIG]
12035
+ ]);
12036
+ }
12412
12037
 
12413
12038
  // src/agents/delegateTool.ts
12414
- function createAgentDelegateTool(orchestrator, agentStore, parentSessionId) {
12415
- const agents = agentStore.getAllAgents();
12416
- const agentDescriptions = agents.map((a) => `- **${a.name}**: ${a.description}`).join("\n");
12417
- const agentNames = agentStore.getAgentNames();
12039
+ function createSubagentDelegateTool(orchestrator, parentSessionId) {
12418
12040
  return {
12419
12041
  toolFn: async (args) => {
12420
12042
  const params = args;
12421
12043
  if (!params.task) {
12422
- throw new Error("agent_delegate: task parameter is required");
12423
- }
12424
- if (!params.agent) {
12425
- throw new Error("agent_delegate: agent parameter is required");
12044
+ throw new Error("subagent_delegate: task parameter is required");
12426
12045
  }
12427
- if (!agentStore.hasAgent(params.agent)) {
12428
- const available = agentNames.join(", ");
12429
- throw new Error(`agent_delegate: unknown agent "${params.agent}". Available agents: ${available}`);
12046
+ if (!params.type) {
12047
+ throw new Error("subagent_delegate: type parameter is required");
12430
12048
  }
12431
- const result = await orchestrator.delegateToAgent({
12049
+ const thoroughness = params.thoroughness || "medium";
12050
+ const type = params.type;
12051
+ const result = await orchestrator.delegateToSubagent({
12432
12052
  task: params.task,
12433
- agentName: params.agent,
12434
- thoroughness: params.thoroughness,
12435
- variables: params.variables,
12053
+ type,
12054
+ thoroughness,
12436
12055
  parentSessionId
12437
12056
  });
12438
12057
  return result.summary;
12439
12058
  },
12440
12059
  toolSchema: {
12441
- name: "agent_delegate",
12442
- description: `Delegate a task to a specialized agent for focused work.
12443
-
12444
- **Available Agents:**
12445
- ${agentDescriptions}
12060
+ name: "subagent_delegate",
12061
+ description: `Delegate a task to a specialized subagent for focused work.
12446
12062
 
12447
12063
  **When to use this tool:**
12448
- - **explore**: When you need to search through the codebase, find files, or understand code structure (read-only)
12449
- - **plan**: When you need to break down a complex task into actionable steps
12450
- - **review**: When you need to analyze code quality and identify potential issues
12451
- - Custom agents: Use for domain-specific tasks defined in project or global agent files
12064
+ - **Explore**: When you need to search through the codebase, find files, or understand code structure (read-only)
12065
+ - **Plan**: When you need to break down a complex task into actionable steps
12066
+ - **Review**: When you need to analyze code quality and identify potential issues
12452
12067
 
12453
12068
  **Benefits:**
12454
12069
  - Keeps main conversation focused and clean
12455
12070
  - Uses specialized prompts optimized for each task type
12456
12071
  - Faster execution with appropriate models (Haiku for explore/plan, Sonnet for review)
12457
- - Supports custom variables for parameterized behavior
12458
12072
 
12459
12073
  **Example uses:**
12460
- - "Find all files that use the authentication system" \u2192 agent: explore
12461
- - "Search for components that handle user input" \u2192 agent: explore
12462
- - "Break down implementing a new feature into steps" \u2192 agent: plan
12463
- - "Review this module for potential bugs" \u2192 agent: review`,
12074
+ - "Find all files that use the authentication system" \u2192 explore
12075
+ - "Search for components that handle user input" \u2192 explore
12076
+ - "Break down implementing a new feature into steps" \u2192 plan
12077
+ - "Review this module for potential bugs" \u2192 review`,
12464
12078
  parameters: {
12465
12079
  type: "object",
12466
12080
  properties: {
12467
12081
  task: {
12468
12082
  type: "string",
12469
- 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."
12083
+ 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."
12470
12084
  },
12471
- agent: {
12085
+ type: {
12472
12086
  type: "string",
12473
- enum: agentNames,
12474
- description: `Name of the agent to use for this task. Available: ${agentNames.join(", ")}`
12087
+ enum: ["explore", "plan", "review"],
12088
+ description: `Type of subagent to use:
12089
+ - explore: Read-only codebase exploration and search (fast, uses Haiku)
12090
+ - plan: Task breakdown and planning (uses Haiku)
12091
+ - review: Code quality analysis and review (uses Sonnet for better reasoning)`
12475
12092
  },
12476
12093
  thoroughness: {
12477
12094
  type: "string",
@@ -12479,15 +12096,10 @@ ${agentDescriptions}
12479
12096
  description: `How thoroughly to execute:
12480
12097
  - quick: Fast lookup, 1-2 iterations
12481
12098
  - medium: Balanced exploration, 3-5 iterations (default)
12482
- - very_thorough: Comprehensive analysis, 8-10+ iterations`
12483
- },
12484
- variables: {
12485
- type: "object",
12486
- additionalProperties: { type: "string" },
12487
- description: 'Custom variables to substitute in agent system prompt. For example: { "DOMAIN": "authentication", "STRICTNESS": "high" }'
12099
+ - very_thorough: Comprehensive analysis, 8-10 iterations`
12488
12100
  }
12489
12101
  },
12490
- required: ["task", "agent"]
12102
+ required: ["task", "type"]
12491
12103
  }
12492
12104
  }
12493
12105
  };
@@ -12618,6 +12230,134 @@ function createTodoStore(onUpdate) {
12618
12230
  };
12619
12231
  }
12620
12232
 
12233
+ // src/tools/skillTool.ts
12234
+ function parseArguments(argsString) {
12235
+ const args = [];
12236
+ let current = "";
12237
+ let inQuotes = false;
12238
+ let quoteChar = "";
12239
+ for (let i = 0; i < argsString.length; i++) {
12240
+ const char = argsString[i];
12241
+ if (!inQuotes && (char === '"' || char === "'")) {
12242
+ inQuotes = true;
12243
+ quoteChar = char;
12244
+ } else if (inQuotes && char === quoteChar) {
12245
+ inQuotes = false;
12246
+ quoteChar = "";
12247
+ } else if (!inQuotes && char === " ") {
12248
+ if (current.length > 0) {
12249
+ args.push(current);
12250
+ current = "";
12251
+ }
12252
+ } else {
12253
+ current += char;
12254
+ }
12255
+ }
12256
+ if (current.length > 0) {
12257
+ args.push(current);
12258
+ }
12259
+ return args;
12260
+ }
12261
+ function createSkillTool(customCommandStore) {
12262
+ return {
12263
+ toolFn: async (args) => {
12264
+ const params = args;
12265
+ if (!params.skill || typeof params.skill !== "string") {
12266
+ throw new Error("skill: skill parameter is required");
12267
+ }
12268
+ const skillName = params.skill.replace(/^\//, "");
12269
+ const command = customCommandStore.getCommand(skillName);
12270
+ if (!command) {
12271
+ const available = customCommandStore.getAllCommands().map((c) => c.name).join(", ");
12272
+ throw new Error(`skill: "${skillName}" not found. Available skills: ${available || "none"}`);
12273
+ }
12274
+ const argsArray = params.args ? parseArguments(params.args) : [];
12275
+ let expandedBody = substituteArguments(command.body, argsArray);
12276
+ const processed = await processFileReferences(expandedBody);
12277
+ expandedBody = processed.content;
12278
+ if (processed.errors.length > 0) {
12279
+ expandedBody += `
12280
+
12281
+ **File reference errors:**
12282
+ ${processed.errors.map((e) => `- ${e}`).join("\n")}`;
12283
+ }
12284
+ return `## Skill Loaded: /${skillName}
12285
+
12286
+ ${expandedBody}
12287
+
12288
+ ---
12289
+ *Follow the instructions above. This skill was invoked programmatically.*`;
12290
+ },
12291
+ toolSchema: {
12292
+ name: "skill",
12293
+ description: `Execute a skill (custom slash command) within the conversation.
12294
+
12295
+ **When to use this tool:**
12296
+ - When a user asks you to use a skill by name (e.g., "use the review-pr skill")
12297
+ - When a skill would help accomplish the user's request
12298
+ - When you see /<skill-name> syntax in user messages
12299
+
12300
+ **How it works:**
12301
+ 1. Skills are loaded from markdown files in .bike4mind/commands/
12302
+ 2. The skill template is expanded with argument substitution ($1, $2, $ARGUMENTS)
12303
+ 3. File references (@filename) are resolved and content is injected
12304
+ 4. The expanded template is returned for you to follow
12305
+
12306
+ **Example invocations:**
12307
+ - skill({ skill: "commit" }) - invoke commit skill
12308
+ - skill({ skill: "review-pr", args: "123" }) - review PR #123
12309
+ - skill({ skill: "feature-code-map", args: "authentication" }) - generate code map
12310
+
12311
+ **Important:**
12312
+ - Skill names can be with or without leading slash: "commit" or "/commit"
12313
+ - Arguments are space-separated; use quotes for arguments with spaces
12314
+ - The tool returns instructions to follow, not a final answer`,
12315
+ parameters: {
12316
+ type: "object",
12317
+ properties: {
12318
+ skill: {
12319
+ type: "string",
12320
+ description: 'Name of the skill to invoke (e.g., "commit", "review-pr")'
12321
+ },
12322
+ args: {
12323
+ type: "string",
12324
+ description: `Optional arguments as space-separated string. Use quotes for arguments containing spaces (e.g., '123 "bug fix"').`
12325
+ }
12326
+ },
12327
+ required: ["skill"]
12328
+ }
12329
+ }
12330
+ };
12331
+ }
12332
+
12333
+ // src/core/skillsPrompt.ts
12334
+ function formatSkillEntry(cmd) {
12335
+ const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
12336
+ return `- **${cmd.name}**${argHint}: ${cmd.description}
12337
+ `;
12338
+ }
12339
+ function formatSkillGroup(heading, commands) {
12340
+ if (commands.length === 0) {
12341
+ return "";
12342
+ }
12343
+ return `
12344
+ ### ${heading}
12345
+ ${commands.map(formatSkillEntry).join("")}`;
12346
+ }
12347
+ function buildSkillsPromptSection(commands) {
12348
+ if (commands.length === 0) {
12349
+ return "";
12350
+ }
12351
+ const projectSkills = commands.filter((c) => c.source === "project");
12352
+ const globalSkills = commands.filter((c) => c.source === "global");
12353
+ return `
12354
+
12355
+ ## Available Skills
12356
+
12357
+ Use the \`skill\` tool to invoke these. Example: skill({ skill: "commit" })
12358
+ ` + formatSkillGroup("Project Skills", projectSkills) + formatSkillGroup("Global Skills", globalSkills);
12359
+ }
12360
+
12621
12361
  // src/index.tsx
12622
12362
  process.removeAllListeners("warning");
12623
12363
  process.on("warning", (warning) => {
@@ -12650,7 +12390,6 @@ function CliApp() {
12650
12390
  rewindSelector: null,
12651
12391
  sessionSelector: null,
12652
12392
  orchestrator: null,
12653
- agentStore: null,
12654
12393
  abortController: null
12655
12394
  });
12656
12395
  const [isInitialized, setIsInitialized] = useState8(false);
@@ -12882,14 +12621,21 @@ function CliApp() {
12882
12621
  } else {
12883
12622
  console.log(`\u{1F4E1} No MCP tools loaded`);
12884
12623
  }
12885
- const builtinAgentsDir = new URL("./agents/defaults/", import.meta.url).pathname;
12886
- const agentProjectDir = state.configStore.getProjectConfigDir();
12887
- const agentStore = new AgentStore(builtinAgentsDir, agentProjectDir || process.cwd());
12888
- await agentStore.loadAgents();
12889
- const agentSummary = agentStore.getSummary();
12890
- console.log(
12891
- `\u{1F916} Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`
12892
- );
12624
+ const subagentConfigs = getDefaultSubagentConfigs();
12625
+ if (config.subagents) {
12626
+ if (config.subagents.explore) {
12627
+ const currentConfig = subagentConfigs.get("explore");
12628
+ subagentConfigs.set("explore", { ...currentConfig, ...config.subagents.explore });
12629
+ }
12630
+ if (config.subagents.plan) {
12631
+ const currentConfig = subagentConfigs.get("plan");
12632
+ subagentConfigs.set("plan", { ...currentConfig, ...config.subagents.plan });
12633
+ }
12634
+ if (config.subagents.review) {
12635
+ const currentConfig = subagentConfigs.get("review");
12636
+ subagentConfigs.set("review", { ...currentConfig, ...config.subagents.review });
12637
+ }
12638
+ }
12893
12639
  const orchestrator = new SubagentOrchestrator({
12894
12640
  userId: config.userId,
12895
12641
  llm,
@@ -12898,14 +12644,28 @@ function CliApp() {
12898
12644
  showPermissionPrompt: promptFn,
12899
12645
  configStore: state.configStore,
12900
12646
  apiClient,
12901
- agentStore
12647
+ subagentConfigs
12902
12648
  });
12903
- const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id);
12649
+ const subagentDelegateTool = createSubagentDelegateTool(orchestrator, newSession.id);
12904
12650
  const todoStore = createTodoStore();
12905
12651
  const writeTodosTool = createWriteTodosTool(todoStore);
12906
- const allTools = [...b4mTools, ...mcpTools, agentDelegateTool, writeTodosTool];
12652
+ const enableSkillTool = config.preferences.enableSkillTool !== false;
12653
+ const skillTool = enableSkillTool ? createSkillTool(state.customCommandStore) : null;
12654
+ const cliTools = [subagentDelegateTool, writeTodosTool];
12655
+ if (skillTool) {
12656
+ cliTools.push(skillTool);
12657
+ }
12658
+ const allTools = [...b4mTools, ...mcpTools, ...cliTools];
12659
+ console.log(`\u{1F4C2} Working directory: ${process.cwd()}`);
12660
+ console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
12661
+ if (skillTool) {
12662
+ const skillCount = state.customCommandStore.getCommandCount();
12663
+ if (skillCount > 0) {
12664
+ console.log(`\u{1F6E0}\uFE0F Skill tool enabled (${skillCount} skills available)`);
12665
+ }
12666
+ }
12907
12667
  logger.debug(
12908
- `Total tools available to agent: ${allTools.length} (${b4mTools.length} B4M + ${mcpTools.length} MCP + 2 CLI)`
12668
+ `Total tools available to agent: ${allTools.length} (${b4mTools.length} B4M + ${mcpTools.length} MCP + ${cliTools.length} CLI)`
12909
12669
  );
12910
12670
  const projectDir = state.configStore.getProjectConfigDir();
12911
12671
  const contextResult = await loadContextFiles(projectDir);
@@ -12918,13 +12678,17 @@ function CliApp() {
12918
12678
  for (const error of contextResult.errors) {
12919
12679
  console.log(`\u26A0\uFE0F Context file error: ${error}`);
12920
12680
  }
12921
- const contextSection = contextResult.mergedContent ? `
12681
+ let contextSection = contextResult.mergedContent ? `
12922
12682
 
12923
12683
  ## Project Context
12924
12684
 
12925
12685
  Follow these project-specific instructions:
12926
12686
 
12927
12687
  ${contextResult.mergedContent}` : "";
12688
+ if (enableSkillTool) {
12689
+ const commands = state.customCommandStore.getAllCommands();
12690
+ contextSection += buildSkillsPromptSection(commands);
12691
+ }
12928
12692
  const cliSystemPrompt = buildCoreSystemPrompt(contextSection);
12929
12693
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
12930
12694
  const agent = new ReActAgent({
@@ -12978,10 +12742,8 @@ ${contextResult.mergedContent}` : "";
12978
12742
  // Store config for synchronous access
12979
12743
  availableModels: models,
12980
12744
  // Store models for ConfigEditor
12981
- orchestrator,
12745
+ orchestrator
12982
12746
  // Store orchestrator for step handler updates
12983
- agentStore
12984
- // Store agent store for agent management commands
12985
12747
  }));
12986
12748
  setStoreSession(newSession);
12987
12749
  setIsInitialized(true);
@@ -13420,20 +13182,6 @@ ${output}` : output.trim() || "(no output)",
13420
13182
  toolCallCount
13421
13183
  };
13422
13184
  };
13423
- const displayAgentsBySource = (agents, label, emoji) => {
13424
- if (agents.length > 0) {
13425
- console.log(`${emoji} ${label}:`);
13426
- const terminalWidth = process.stdout.columns || 80;
13427
- const nameWidth = 25;
13428
- const prefixWidth = 2 + nameWidth + 3;
13429
- const maxDescLength = Math.max(20, terminalWidth - prefixWidth);
13430
- agents.forEach((agent) => {
13431
- const desc = agent.description.length > maxDescLength ? agent.description.slice(0, maxDescLength - 3) + "..." : agent.description;
13432
- console.log(` ${agent.name.padEnd(nameWidth)} - ${desc}`);
13433
- });
13434
- console.log("");
13435
- }
13436
- };
13437
13185
  const handleCommand = async (command, args) => {
13438
13186
  const customCommand = state.customCommandStore.getCommand(command);
13439
13187
  if (customCommand) {
@@ -13450,25 +13198,6 @@ ${output}` : output.trim() || "(no output)",
13450
13198
  console.log("\u{1F4DD} Expanded command:\n", substitutedBody);
13451
13199
  }
13452
13200
  const displayMessage = `/${command}${args.length > 0 ? " " + args.join(" ") : ""}`;
13453
- if (customCommand.agent && state.orchestrator && state.agentStore) {
13454
- if (!state.agentStore.hasAgent(customCommand.agent)) {
13455
- const available = state.agentStore.getAgentNames().join(", ");
13456
- console.error(`\u274C Unknown agent "${customCommand.agent}" specified in command.`);
13457
- console.error(` Available agents: ${available}`);
13458
- return;
13459
- }
13460
- console.log(`\u{1F916} Delegating to ${customCommand.agent} agent...
13461
- `);
13462
- const result = await state.orchestrator.delegateToAgent({
13463
- task: substitutedBody,
13464
- agentName: customCommand.agent,
13465
- thoroughness: customCommand.thoroughness,
13466
- variables: customCommand.variables,
13467
- parentSessionId: state.session?.id || "unknown"
13468
- });
13469
- console.log("\n" + result.summary + "\n");
13470
- return;
13471
- }
13472
13201
  if (customCommand.model && state.agent) {
13473
13202
  console.log(`\u{1F504} Using model override: ${customCommand.model}`);
13474
13203
  const originalModel = state.session?.model;
@@ -14033,11 +13762,19 @@ No usage data available for the last ${USAGE_DAYS} days.`);
14033
13762
  console.log(" \u{1F3E0} Global: ~/.bike4mind/commands/ (available in all projects)");
14034
13763
  console.log(" \u{1F4C1} Project: .bike4mind/commands/ (team-shared, committed to git)");
14035
13764
  } else {
13765
+ const termWidth = process.stdout.columns || 80;
13766
+ const truncateDescription = (desc, prefixLen) => {
13767
+ const maxDescLen = termWidth - prefixLen - 5;
13768
+ if (maxDescLen < 20) return desc;
13769
+ return desc.length > maxDescLen ? desc.slice(0, maxDescLen) + "..." : desc;
13770
+ };
14036
13771
  if (globalCommands.length > 0) {
14037
13772
  console.log("\u{1F3E0} Global Commands (~/.bike4mind/commands/):");
14038
13773
  globalCommands.forEach((cmd) => {
14039
13774
  const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
14040
- console.log(` /${cmd.name}${argHint} - ${cmd.description}`);
13775
+ const prefix = ` /${cmd.name}${argHint}`;
13776
+ const desc = truncateDescription(cmd.description, prefix.length);
13777
+ console.log(`${prefix} - ${desc}`);
14041
13778
  });
14042
13779
  console.log("");
14043
13780
  }
@@ -14045,7 +13782,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
14045
13782
  console.log("\u{1F4C1} Project Commands (.bike4mind/commands/):");
14046
13783
  projectCommands.forEach((cmd) => {
14047
13784
  const argHint = cmd.argumentHint ? ` ${cmd.argumentHint}` : "";
14048
- console.log(` /${cmd.name}${argHint} - ${cmd.description}`);
13785
+ const prefix = ` /${cmd.name}${argHint}`;
13786
+ const desc = truncateDescription(cmd.description, prefix.length);
13787
+ console.log(`${prefix} - ${desc}`);
14049
13788
  });
14050
13789
  console.log("");
14051
13790
  }
@@ -14090,78 +13829,6 @@ No usage data available for the last ${USAGE_DAYS} days.`);
14090
13829
  setShowMcpViewer(true);
14091
13830
  break;
14092
13831
  }
14093
- case "agents":
14094
- case "agents:list": {
14095
- if (!state.agentStore) {
14096
- console.log("\u274C Agent store not initialized");
14097
- break;
14098
- }
14099
- const summary = state.agentStore.getSummary();
14100
- console.log("\n\u{1F916} Available Agents:\n");
14101
- if (summary.total === 0) {
14102
- console.log("No agents found.");
14103
- console.log("\nTo create a custom agent:");
14104
- console.log(" /agents:new <name> - Create a new agent");
14105
- } else {
14106
- displayAgentsBySource(state.agentStore.getAgentsBySource("builtin"), "Built-in Agents", "\u{1F4E6}");
14107
- displayAgentsBySource(
14108
- state.agentStore.getAgentsBySource("global"),
14109
- "Global Agents (~/.claude/agents/ or ~/.bike4mind/agents/)",
14110
- "\u{1F3E0}"
14111
- );
14112
- displayAgentsBySource(
14113
- state.agentStore.getAgentsBySource("project"),
14114
- "Project Agents (.claude/agents/ or .bike4mind/agents/)",
14115
- "\u{1F4C1}"
14116
- );
14117
- console.log(
14118
- `Total: ${summary.total} agent${summary.total !== 1 ? "s" : ""} (${summary.builtin} built-in, ${summary.global} global, ${summary.project} project)`
14119
- );
14120
- }
14121
- console.log("");
14122
- break;
14123
- }
14124
- case "agents:new": {
14125
- if (!state.agentStore) {
14126
- console.log("\u274C Agent store not initialized");
14127
- break;
14128
- }
14129
- const agentName = args[0];
14130
- if (!agentName) {
14131
- console.log("\u274C Please provide an agent name");
14132
- console.log("Usage: /agents:new <name>");
14133
- break;
14134
- }
14135
- console.log("\nWhere should this agent be stored?");
14136
- console.log(" 1. \u{1F3E0} Global (~/.claude/agents/) - available in all projects");
14137
- console.log(" 2. \u{1F4C1} Project (.claude/agents/) - team-shared");
14138
- console.log("\nDefaulting to global (~/.claude/agents/). Creating agent file...");
14139
- try {
14140
- const filePath = await state.agentStore.createAgentFile(agentName, true);
14141
- console.log(`\u2705 Created agent file: ${filePath}`);
14142
- console.log("\nEdit this file to customize your agent.");
14143
- console.log("Then run: /agents:reload to load it");
14144
- } catch (error) {
14145
- console.error("\u274C Failed to create agent file:", error instanceof Error ? error.message : String(error));
14146
- }
14147
- break;
14148
- }
14149
- case "agents:reload": {
14150
- if (!state.agentStore) {
14151
- console.log("\u274C Agent store not initialized");
14152
- break;
14153
- }
14154
- try {
14155
- await state.agentStore.reloadAgents();
14156
- const summary = state.agentStore.getSummary();
14157
- console.log(
14158
- `\u2705 Reloaded ${summary.total} agent${summary.total !== 1 ? "s" : ""} (${summary.builtin} built-in, ${summary.global} global, ${summary.project} project)`
14159
- );
14160
- } catch (error) {
14161
- console.error("\u274C Failed to reload agents:", error instanceof Error ? error.message : String(error));
14162
- }
14163
- break;
14164
- }
14165
13832
  default:
14166
13833
  console.log(`Unknown command: /${command}`);
14167
13834
  console.log("Type /help for available commands");