@bahulam/code 0.1.15 → 0.1.17

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.
@@ -59,7 +59,7 @@ import * as telemetry from '../telemetry/index.mjs';
59
59
  import { resolveBackendUrl } from '../core/backend-url.mjs';
60
60
  import { formatMessageWindow, lowWindowStatus, messagesRemaining } from '../core/rate-limit-display.mjs';
61
61
  import { formatAgentErrorGuidance } from '../core/error-guidance.mjs';
62
- import { BUILTIN_AGENTS, findBuiltinAgent, localAgentMatches, runAgent, runAgentDefinition } from './agents.mjs';
62
+ import { BUILTIN_AGENTS, runAgentDefinition } from './agents.mjs';
63
63
  import { SkillInstaller } from '../skills/installer.mjs';
64
64
  import { SkillsLoader } from '../skills/loader.mjs';
65
65
  import { openSkillsPicker, formatSkillsList } from './skills-picker.mjs';
@@ -921,23 +921,35 @@ function printAgentsUsage() {
921
921
  process.stderr.write(` /agents sync [name]\n`);
922
922
  }
923
923
 
924
- function printAgentsList() {
925
- const local = listLocalAgents(safeCwd());
926
- process.stderr.write(`\n ${c.bold('Built-in Agents')}\n`);
924
+ function printAgentsList(ctx = {}) {
925
+ const agents = ctx.toolExecutor?.filterAgents?.({}) || [
926
+ ...listLocalAgents(safeCwd()),
927
+ ...BUILTIN_AGENTS.map(agent => ({
928
+ slug: agent.command,
929
+ name: agent.name,
930
+ description: agent.description,
931
+ source_scope: 'platform',
932
+ })),
933
+ ];
934
+ const groups = new Map();
935
+ for (const agent of agents) {
936
+ const scope = agent.source_scope || 'unknown';
937
+ if (!groups.has(scope)) groups.set(scope, []);
938
+ groups.get(scope).push(agent);
939
+ }
940
+
941
+ process.stderr.write(`\n ${c.bold('Agents')} ${c.dim('platform + project + global + admitted plugin')}\n`);
927
942
  process.stderr.write(` ${c.gray('─'.repeat(44))}\n`);
928
- for (const agent of BUILTIN_AGENTS) {
929
- process.stderr.write(` ${c.brand(('/' + agent.command).padEnd(14))} ${agent.description}\n`);
930
- }
931
-
932
- process.stderr.write(`\n ${c.bold('Local Agents')} ${c.dim('.bahulam/agents + ~/.bahulam/agents')}\n`);
933
- process.stderr.write(` ${c.gray('─'.repeat(44))}\n`);
934
- if (!local.length) {
943
+ if (!agents.length) {
935
944
  process.stderr.write(` ${c.dim('(none)')}\n`);
936
945
  } else {
937
- for (const agent of local) {
938
- const scope = agent.source_scope === 'project' ? c.green('project') : c.dim(agent.source_scope);
939
- const model = agent.model ? c.dim(` · ${agent.model}`) : '';
940
- process.stderr.write(` ${c.brand(agent.slug.padEnd(18))} ${scope} ${agent.description || ''}${model}\n`);
946
+ for (const [scope, items] of groups) {
947
+ process.stderr.write(` ${c.dim(scope)}\n`);
948
+ for (const agent of items) {
949
+ const model = agent.model ? c.dim(` · ${agent.model}`) : '';
950
+ const desc = agent.description ? ` ${agent.description}` : '';
951
+ process.stderr.write(` ${c.brand(String(agent.slug || agent.command || agent.name).padEnd(18))}${desc}${model}\n`);
952
+ }
941
953
  }
942
954
  }
943
955
  process.stderr.write('\n');
@@ -949,7 +961,7 @@ async function handleAgentsCommand(rest = '', ctx) {
949
961
  const action = (parts.shift() || 'list').toLowerCase();
950
962
 
951
963
  if (action === 'list' || action === 'ls') {
952
- printAgentsList();
964
+ printAgentsList(ctx);
953
965
  return;
954
966
  }
955
967
 
@@ -3186,6 +3198,23 @@ async function prepareDirectAgentRunContext(ctx, instruction = '') {
3186
3198
  return execContext;
3187
3199
  }
3188
3200
 
3201
+ function makeDispatchContext(ctx) {
3202
+ const creds = ctx.auth?.loadCredentials?.() || {};
3203
+ return {
3204
+ toolExecutor: ctx.toolExecutor,
3205
+ listRunnables: () => ctx.toolExecutor?.listRunnables?.() || [],
3206
+ listLocalWorkflows: () => listLocalWorkflows(safeCwd()),
3207
+ renderEvent,
3208
+ sessionSubstrate: makeSessionSubstrate(ctx),
3209
+ auth: { token: creds.token || null },
3210
+ credentials: {
3211
+ apiKey: process.env.ANTHROPIC_API_KEY || creds.anthropicKey || null,
3212
+ openRouterKey: process.env.OPENROUTER_API_KEY || creds.openRouterKey || null,
3213
+ },
3214
+ cwd: safeCwd(),
3215
+ };
3216
+ }
3217
+
3189
3218
  function stripWrappingQuotes(value = '') {
3190
3219
  const text = String(value || '').trim();
3191
3220
  if (text.length >= 2) {
@@ -3215,10 +3244,7 @@ function printRunUsage(ctx) {
3215
3244
  process.stderr.write(` ${c.gray('Example: /run docker-analyzer Analyze all running Docker containers')}\n`);
3216
3245
 
3217
3246
  const targets = new Map();
3218
- for (const agent of listLocalAgents(safeCwd())) addRunTarget(targets, agent);
3219
- for (const agent of BUILTIN_AGENTS) addRunTarget(targets, agent);
3220
3247
  for (const agent of ctx.toolExecutor?.listRunnables?.() || []) addRunTarget(targets, agent);
3221
- for (const agent of pluginRegistry?.listAgents?.() || []) addRunTarget(targets, agent);
3222
3248
 
3223
3249
  const agents = [...targets.values()].filter(item => item.kind === 'agent').slice(0, 12);
3224
3250
  if (agents.length) {
@@ -3251,28 +3277,8 @@ async function handleRunCommand(rest = '', ctx) {
3251
3277
  return;
3252
3278
  }
3253
3279
 
3254
- const localAgent = listLocalAgents(safeCwd()).find(agent => localAgentMatches(agent, target));
3255
- const builtinAgent = findBuiltinAgent(target);
3256
- const registeredAgent = ctx.toolExecutor?.listRunnables?.()
3257
- ?.find(agent => localAgentMatches(agent, target));
3258
- const pluginAgent = pluginRegistry?.listAgents?.()
3259
- ?.find(agent => localAgentMatches(agent, target));
3260
- const runnableAgent = localAgent || builtinAgent || registeredAgent || pluginAgent;
3261
-
3262
- const creds = ctx.auth?.loadCredentials?.() || {};
3263
- const dispatchCtx = {
3264
- toolExecutor: ctx.toolExecutor,
3265
- listRunnables: () => ctx.toolExecutor?.listRunnables?.() || [],
3266
- listLocalWorkflows: () => listLocalWorkflows(safeCwd()),
3267
- renderEvent,
3268
- sessionSubstrate: makeSessionSubstrate(ctx),
3269
- auth: { token: creds.token || null },
3270
- credentials: {
3271
- apiKey: process.env.ANTHROPIC_API_KEY || creds.anthropicKey || null,
3272
- openRouterKey: process.env.OPENROUTER_API_KEY || creds.openRouterKey || null,
3273
- },
3274
- cwd: safeCwd(),
3275
- };
3280
+ const runnableAgent = ctx.toolExecutor?.findAgent?.(target) || null;
3281
+ const dispatchCtx = makeDispatchContext(ctx);
3276
3282
 
3277
3283
  try {
3278
3284
  if (!runnableAgent) process.stderr.write(` ${c.dim(`Running workflow '${target}'...`)}\n`);
@@ -4254,7 +4260,7 @@ async function handleCommand(input, ctx) {
4254
4260
  process.stderr.write(` ${c.gray(`Example: ${cmd} ${cmd === '/explore' ? 'how does authentication work?' : cmd === '/review' ? 'check src/core/ for bugs' : 'design a caching layer'}`)}\n`);
4255
4261
  return;
4256
4262
  }
4257
- return await runAgent(cmd.slice(1), rest, ctx, session, renderEvent);
4263
+ return await handleRunCommand(`${cmd.slice(1)} ${rest}`, ctx);
4258
4264
  }
4259
4265
 
4260
4266
  case '/logout': {
@@ -4335,6 +4341,18 @@ export async function startTerminalRepl() {
4335
4341
  return res;
4336
4342
  };
4337
4343
 
4344
+ async function runDelegateFromTool({ agent, slug, instruction, context = {}, options = {} }) {
4345
+ const dispatchCtx = makeDispatchContext(ctx);
4346
+ return await dispatch({
4347
+ type: 'invoke',
4348
+ source: 'tool:delegate',
4349
+ target: { kind: 'agent', slug: slug || agent?.slug, agent },
4350
+ params: { instruction, context },
4351
+ channel: 'local',
4352
+ signal: options.signal,
4353
+ }, dispatchCtx);
4354
+ }
4355
+
4338
4356
  function makeToolExecutor({ showIndexStatus = false } = {}) {
4339
4357
  const shouldShowIndexStatus = showIndexStatus && process.stderr.isTTY && !term().plain;
4340
4358
  let stopIndexSpinner = null;
@@ -4342,6 +4360,7 @@ export async function startTerminalRepl() {
4342
4360
  checkpoints,
4343
4361
  hookRunner,
4344
4362
  pluginRegistry,
4363
+ delegateRunner: runDelegateFromTool,
4345
4364
  interactionHandler: askUserInteraction,
4346
4365
  onAutoRegisterStart: shouldShowIndexStatus ? (root) => {
4347
4366
  const name = path.basename(root || safeCwd()) || root || 'project';
@@ -4378,20 +4397,7 @@ export async function startTerminalRepl() {
4378
4397
  // agent through the trigger funnel when they exit. The ctx builder runs
4379
4398
  // lazily at fire time so it sees the live tool executor.
4380
4399
  registerJobCompletionDispatch(() => {
4381
- const creds = ctx.auth?.loadCredentials?.() || {};
4382
- return {
4383
- toolExecutor: ctx.toolExecutor,
4384
- listRunnables: () => ctx.toolExecutor?.listRunnables?.() || [],
4385
- listLocalWorkflows: () => listLocalWorkflows(safeCwd()),
4386
- renderEvent,
4387
- sessionSubstrate: makeSessionSubstrate(ctx),
4388
- auth: { token: creds.token || null },
4389
- credentials: {
4390
- apiKey: process.env.ANTHROPIC_API_KEY || creds.anthropicKey || null,
4391
- openRouterKey: process.env.OPENROUTER_API_KEY || creds.openRouterKey || null,
4392
- },
4393
- cwd: safeCwd(),
4394
- };
4400
+ return makeDispatchContext(ctx);
4395
4401
  });
4396
4402
 
4397
4403
  let startupOutputRow = 1;
@@ -16,6 +16,15 @@ function stripAnsi(str) {
16
16
  return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
17
17
  }
18
18
 
19
+ function trimShellBackgroundOperator(command) {
20
+ const raw = String(command || '');
21
+ let i = raw.length - 1;
22
+ while (i >= 0 && /\s/.test(raw[i])) i -= 1;
23
+ if (raw[i] !== '&') return raw;
24
+ if (raw[i - 1] === '&' || raw[i - 1] === '\\') return raw;
25
+ return raw.slice(0, i).trimEnd();
26
+ }
27
+
19
28
  const MAX_OUTPUT_BYTES = 1024 * 1024; // 1MB
20
29
  const TIMEOUT_TAIL_BYTES = 64 * 1024;
21
30
  const TRUNCATION_MARKER = '\n[output truncated at 1MB]';
@@ -44,7 +53,7 @@ export const BashTool = {
44
53
  const abortSignal = input.signal || input._signal || null;
45
54
 
46
55
  if (input.run_in_background) {
47
- return runBackground(input.command, input.cwd);
56
+ return runBackground(trimShellBackgroundOperator(input.command), input.cwd);
48
57
  }
49
58
 
50
59
  if (abortSignal?.aborted) {
@@ -200,6 +209,12 @@ function killProcess(proc, signal) {
200
209
  try { proc.kill(signal); } catch { /* already exited */ }
201
210
  }
202
211
 
212
+ function unrefChildStdio(proc) {
213
+ for (const stream of [proc?.stdout, proc?.stderr]) {
214
+ try { stream?.unref?.(); } catch { /* best effort */ }
215
+ }
216
+ }
217
+
203
218
  // Background jobs store
204
219
  const backgroundJobs = new Map();
205
220
  let bgJobId = 0;
@@ -227,6 +242,7 @@ function runBackground(command, cwd) {
227
242
  });
228
243
 
229
244
  proc.unref();
245
+ unrefChildStdio(proc);
230
246
  return `Background job started: id=${id}, pid=${proc.pid}`;
231
247
  }
232
248