@bahulam/code 0.1.16 → 0.1.18
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/package.json +1 -1
- package/src/agents/loader.mjs +37 -10
- package/src/agents/registry.mjs +240 -0
- package/src/commands/install.mjs +8 -8
- package/src/commands/plugin-manage.mjs +16 -15
- package/src/commands/plugin.mjs +15 -12
- package/src/core/background-tasks.mjs +29 -3
- package/src/core/headless.mjs +46 -2
- package/src/core/stream-client.mjs +1 -0
- package/src/core/tool-executor.mjs +80 -159
- package/src/local-service/agent-relay.mjs +56 -1
- package/src/local-service/server.mjs +2 -2
- package/src/onboarding/preflight.mjs +1 -1
- package/src/orchestration/dispatch.mjs +1 -1
- package/src/plugins/executor.mjs +2 -2
- package/src/plugins/loader.mjs +5 -5
- package/src/plugins/manifest.mjs +147 -24
- package/src/plugins/npm-install.mjs +13 -2
- package/src/plugins/pi-compat/scaffold.mjs +45 -29
- package/src/plugins/preflight.mjs +16 -7
- package/src/plugins/registry.mjs +6 -6
- package/src/plugins/state.mjs +141 -2
- package/src/terminal/repl.mjs +81 -64
- package/src/tools/bash.mjs +17 -1
- package/src/ui/input-dock.mjs +2 -1
package/src/terminal/repl.mjs
CHANGED
|
@@ -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,
|
|
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
|
|
926
|
-
|
|
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
|
-
|
|
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
|
|
938
|
-
|
|
939
|
-
const
|
|
940
|
-
|
|
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
|
|
|
@@ -1323,8 +1335,8 @@ async function _checkForUpgradeAndAnnounce() {
|
|
|
1323
1335
|
const [x, y, z] = asTuple(current);
|
|
1324
1336
|
const newer = (a > x) || (a === x && b > y) || (a === x && b === y && c1 > z);
|
|
1325
1337
|
if (!newer) return;
|
|
1326
|
-
process.stderr.write(` ${c.brand('◆')} ${c.dim('
|
|
1327
|
-
process.stderr.write(` ${c.dim('
|
|
1338
|
+
process.stderr.write(` ${c.brand('◆')} ${c.dim('Update available:')} ${c.bold(c.green(`${pkgName}@${latest}`))} ${c.dim(`(installed ${current})`)}\n`);
|
|
1339
|
+
process.stderr.write(` ${c.dim(' Install:')} ${c.dim('npm install -g ' + pkgName + '@latest')}\n\n`);
|
|
1328
1340
|
}
|
|
1329
1341
|
|
|
1330
1342
|
// ── Prompt Chrome ──
|
|
@@ -1373,6 +1385,8 @@ function buildContextStrip() {
|
|
|
1373
1385
|
// volume + elapsed. Historical rate calc was double-counting the cache tokens
|
|
1374
1386
|
// vs OpenRouter's convention (see computeCacheTotals) which was misleading.
|
|
1375
1387
|
const parts = [];
|
|
1388
|
+
const model = compactDockModel(activeDockModel());
|
|
1389
|
+
if (model) parts.push(c.dim(`model ${model}`));
|
|
1376
1390
|
// ctx: last turn's cumulative input tokens — approximates the CURRENT
|
|
1377
1391
|
// prompt size. Only shown once we've completed at least one turn (so
|
|
1378
1392
|
// the initial banner doesn't read '0 ctx'). This is the number the
|
|
@@ -1385,13 +1399,27 @@ function buildContextStrip() {
|
|
|
1385
1399
|
return parts.join(c.dim(' · '));
|
|
1386
1400
|
}
|
|
1387
1401
|
|
|
1388
|
-
// ── Dock meta line (
|
|
1402
|
+
// ── Dock meta line (cwd ⎇ branch · turn N) ─────────────────────────────
|
|
1389
1403
|
//
|
|
1390
1404
|
// The dock's meta row shows durable session context. Git branch is cached
|
|
1391
1405
|
// so we don't shell out on every keystroke; refreshed at most every 5s.
|
|
1392
1406
|
|
|
1393
1407
|
const _dockGitCache = { branch: null, at: 0, cwd: null };
|
|
1394
1408
|
|
|
1409
|
+
function activeDockModel() {
|
|
1410
|
+
return session.modelOverrides?.reasoning
|
|
1411
|
+
|| session.model
|
|
1412
|
+
|| session.modelLimits?.coder?.model
|
|
1413
|
+
|| session.user?.default_reasoning_model
|
|
1414
|
+
|| null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function compactDockModel(model) {
|
|
1418
|
+
const value = String(model || '').trim();
|
|
1419
|
+
if (!value) return '';
|
|
1420
|
+
return value.replace(/^(anthropic|openai|google|deepseek|xai|meta)\//, '');
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1395
1423
|
function _probeGitBranch(cwd) {
|
|
1396
1424
|
const now = Date.now();
|
|
1397
1425
|
if (_dockGitCache.cwd === cwd && (now - _dockGitCache.at) < 5000) {
|
|
@@ -1422,11 +1450,6 @@ function buildDockMeta() {
|
|
|
1422
1450
|
parts.push(`turn ${session.turns}`);
|
|
1423
1451
|
}
|
|
1424
1452
|
|
|
1425
|
-
const totalTokens = session.inputTokens + session.outputTokens;
|
|
1426
|
-
if (totalTokens > 0) {
|
|
1427
|
-
parts.push(`${formatTokens(totalTokens)} tok`);
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
1453
|
return parts.join(' · ');
|
|
1431
1454
|
}
|
|
1432
1455
|
|
|
@@ -3186,6 +3209,23 @@ async function prepareDirectAgentRunContext(ctx, instruction = '') {
|
|
|
3186
3209
|
return execContext;
|
|
3187
3210
|
}
|
|
3188
3211
|
|
|
3212
|
+
function makeDispatchContext(ctx) {
|
|
3213
|
+
const creds = ctx.auth?.loadCredentials?.() || {};
|
|
3214
|
+
return {
|
|
3215
|
+
toolExecutor: ctx.toolExecutor,
|
|
3216
|
+
listRunnables: () => ctx.toolExecutor?.listRunnables?.() || [],
|
|
3217
|
+
listLocalWorkflows: () => listLocalWorkflows(safeCwd()),
|
|
3218
|
+
renderEvent,
|
|
3219
|
+
sessionSubstrate: makeSessionSubstrate(ctx),
|
|
3220
|
+
auth: { token: creds.token || null },
|
|
3221
|
+
credentials: {
|
|
3222
|
+
apiKey: process.env.ANTHROPIC_API_KEY || creds.anthropicKey || null,
|
|
3223
|
+
openRouterKey: process.env.OPENROUTER_API_KEY || creds.openRouterKey || null,
|
|
3224
|
+
},
|
|
3225
|
+
cwd: safeCwd(),
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3189
3229
|
function stripWrappingQuotes(value = '') {
|
|
3190
3230
|
const text = String(value || '').trim();
|
|
3191
3231
|
if (text.length >= 2) {
|
|
@@ -3215,10 +3255,7 @@ function printRunUsage(ctx) {
|
|
|
3215
3255
|
process.stderr.write(` ${c.gray('Example: /run docker-analyzer Analyze all running Docker containers')}\n`);
|
|
3216
3256
|
|
|
3217
3257
|
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
3258
|
for (const agent of ctx.toolExecutor?.listRunnables?.() || []) addRunTarget(targets, agent);
|
|
3221
|
-
for (const agent of pluginRegistry?.listAgents?.() || []) addRunTarget(targets, agent);
|
|
3222
3259
|
|
|
3223
3260
|
const agents = [...targets.values()].filter(item => item.kind === 'agent').slice(0, 12);
|
|
3224
3261
|
if (agents.length) {
|
|
@@ -3251,28 +3288,8 @@ async function handleRunCommand(rest = '', ctx) {
|
|
|
3251
3288
|
return;
|
|
3252
3289
|
}
|
|
3253
3290
|
|
|
3254
|
-
const
|
|
3255
|
-
const
|
|
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
|
-
};
|
|
3291
|
+
const runnableAgent = ctx.toolExecutor?.findAgent?.(target) || null;
|
|
3292
|
+
const dispatchCtx = makeDispatchContext(ctx);
|
|
3276
3293
|
|
|
3277
3294
|
try {
|
|
3278
3295
|
if (!runnableAgent) process.stderr.write(` ${c.dim(`Running workflow '${target}'...`)}\n`);
|
|
@@ -4254,7 +4271,7 @@ async function handleCommand(input, ctx) {
|
|
|
4254
4271
|
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
4272
|
return;
|
|
4256
4273
|
}
|
|
4257
|
-
return await
|
|
4274
|
+
return await handleRunCommand(`${cmd.slice(1)} ${rest}`, ctx);
|
|
4258
4275
|
}
|
|
4259
4276
|
|
|
4260
4277
|
case '/logout': {
|
|
@@ -4335,6 +4352,18 @@ export async function startTerminalRepl() {
|
|
|
4335
4352
|
return res;
|
|
4336
4353
|
};
|
|
4337
4354
|
|
|
4355
|
+
async function runDelegateFromTool({ agent, slug, instruction, context = {}, options = {} }) {
|
|
4356
|
+
const dispatchCtx = makeDispatchContext(ctx);
|
|
4357
|
+
return await dispatch({
|
|
4358
|
+
type: 'invoke',
|
|
4359
|
+
source: 'tool:delegate',
|
|
4360
|
+
target: { kind: 'agent', slug: slug || agent?.slug, agent },
|
|
4361
|
+
params: { instruction, context },
|
|
4362
|
+
channel: 'local',
|
|
4363
|
+
signal: options.signal,
|
|
4364
|
+
}, dispatchCtx);
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4338
4367
|
function makeToolExecutor({ showIndexStatus = false } = {}) {
|
|
4339
4368
|
const shouldShowIndexStatus = showIndexStatus && process.stderr.isTTY && !term().plain;
|
|
4340
4369
|
let stopIndexSpinner = null;
|
|
@@ -4342,6 +4371,7 @@ export async function startTerminalRepl() {
|
|
|
4342
4371
|
checkpoints,
|
|
4343
4372
|
hookRunner,
|
|
4344
4373
|
pluginRegistry,
|
|
4374
|
+
delegateRunner: runDelegateFromTool,
|
|
4345
4375
|
interactionHandler: askUserInteraction,
|
|
4346
4376
|
onAutoRegisterStart: shouldShowIndexStatus ? (root) => {
|
|
4347
4377
|
const name = path.basename(root || safeCwd()) || root || 'project';
|
|
@@ -4378,20 +4408,7 @@ export async function startTerminalRepl() {
|
|
|
4378
4408
|
// agent through the trigger funnel when they exit. The ctx builder runs
|
|
4379
4409
|
// lazily at fire time so it sees the live tool executor.
|
|
4380
4410
|
registerJobCompletionDispatch(() => {
|
|
4381
|
-
|
|
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
|
-
};
|
|
4411
|
+
return makeDispatchContext(ctx);
|
|
4395
4412
|
});
|
|
4396
4413
|
|
|
4397
4414
|
let startupOutputRow = 1;
|
package/src/tools/bash.mjs
CHANGED
|
@@ -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
|
|
package/src/ui/input-dock.mjs
CHANGED
|
@@ -704,11 +704,12 @@ export function clearInputPrompt() {
|
|
|
704
704
|
export function renderDockInput(prefix, value, { context = '', tips = '', meta = '', cursor = null, fixedRows = null } = {}) {
|
|
705
705
|
if (!mounted) return false;
|
|
706
706
|
contentTrackingActive = false;
|
|
707
|
+
lastFrame = { ...lastFrame, context, tips, meta, prefix, value, cursor, overlayLines: null };
|
|
707
708
|
const requestedRows = fixedRows == null
|
|
708
709
|
? computeInputRowsForBuffer(prefix, value)
|
|
709
710
|
: Math.max(MIN_INPUT_ROWS, Math.min(inputRowsMax, Math.floor(Number(fixedRows) || MIN_INPUT_ROWS)));
|
|
710
711
|
setInputRowsTo(requestedRows);
|
|
711
|
-
renderFrame(
|
|
712
|
+
renderFrame(lastFrame);
|
|
712
713
|
const layout = layoutInput(prefix, value);
|
|
713
714
|
drawInputLines(layout.lines);
|
|
714
715
|
focusDockInput(prefix, value, cursor);
|