Python has Bash(custom_builtins={...}) from #1315 but the JS bindings
(@everruns/bashkit) were missing this feature.
What was missing
Bash has persistent VFS across execute() calls but no way to
register JS callbacks as persistent builtins
ScriptedTool.addTool() has callbacks but VFS is ephemeral per call
- The issue described
JS bindings already have this via Bash.defineCommand()
but in reality defineCommand never existed
What was implemented
BashOptions.customBuiltins: Record<string, BuiltinCallback> — constructor-time
Bash.addBuiltin(name, callback) — post-construction
BashTool equivalent support
BuiltinContext with name, argv, stdin, env, cwd
- 10 tests covering pipes, VFS persistence, reset survival, pipelines
import { Bash } from '@everruns/bashkit';
// Needed API that was missing
const bash = new Bash({
customBuiltins: {
'get-order': (ctx) =>
JSON.stringify({ id: ctx.argv[0], status: 'shipped' }) + '\n',
},
});
await bash.execute('mkdir -p /scratch');
await bash.execute('get-order 42 > /scratch/order.json'); // → VFS
const result = await bash.execute('cat /scratch/order.json'); // persists across calls
Notes
executeSync() deadlocks with custom builtins (same limitation as
ScriptedTool). Use execute() (async).
- Custom builtins survive
reset() but are host-side config — pass
customBuiltins again on snapshot restore.
Python has
Bash(custom_builtins={...})from #1315 but the JS bindings(
@everruns/bashkit) were missing this feature.What was missing
Bashhas persistent VFS acrossexecute()calls but no way toregister JS callbacks as persistent builtins
ScriptedTool.addTool()has callbacks but VFS is ephemeral per callJS bindings already have this via Bash.defineCommand()but in reality
defineCommandnever existedWhat was implemented
BashOptions.customBuiltins: Record<string, BuiltinCallback>— constructor-timeBash.addBuiltin(name, callback)— post-constructionBashToolequivalent supportBuiltinContextwithname,argv,stdin,env,cwdNotes
executeSync()deadlocks with custom builtins (same limitation asScriptedTool). Useexecute()(async).reset()but are host-side config — passcustomBuiltinsagain on snapshot restore.