Subcommand Builders
Build just-bash command groups with dispatch, quote repair, and structured bash metadata
Use these helpers when a just-bash-backed sandbox needs a command surface such
as sql run, marker remind, or tool validate. They target
createVirtualSandbox() or any custom Bash instance that accepts
customCommands.
If you need the same workflow inside Docker or Agent OS, ship a real CLI or
script into that environment instead. defineSubcommandGroup() is a
just-bash command builder, not a cross-backend routing layer.
Quick Start
import { InMemoryFs } from 'just-bash';
import {
type SubcommandDefinition,
buildSubcommandRepair,
createBashTool,
createVirtualSandbox,
defineSubcommandGroup,
} from '@deepagents/context';
const subcommands = {
hello: {
usage: 'hello <name>',
description: 'Say hello to someone',
handler: (args) => ({
stdout: `Hello, ${args.join(' ') || 'friend'}!\n`,
stderr: '',
exitCode: 0,
}),
},
goodbye: {
usage: 'goodbye <name>',
description: 'Say goodbye',
handler: (args) => ({
stdout: `Goodbye, ${args.join(' ') || 'friend'}!\n`,
stderr: '',
exitCode: 0,
}),
},
} satisfies Record<string, SubcommandDefinition>;
const greet = defineSubcommandGroup('greet', subcommands);
const repair = buildSubcommandRepair('greet', subcommands);
const sandbox = await createBashTool({
sandbox: await createVirtualSandbox({
fs: new InMemoryFs(),
customCommands: [greet],
}),
onBeforeBashCall: ({ command }) => ({ command: repair(command) }),
});The tool can now execute greet hello world or greet goodbye alice. Missing
or unknown subcommands return exitCode: 1 with an auto-generated usage
message, which is usually enough feedback for the model to retry correctly.
SubcommandDefinition
Each subcommand definition describes one leaf command:
import type { CommandContext, ExecResult } from 'just-bash';
interface SubcommandDefinition {
usage: string;
description: string;
repair?: (rawArgs: string) => string | null;
handler: (
args: string[],
ctx: CommandContext,
) => ExecResult | Promise<ExecResult>;
}| Field | Description |
|---|---|
usage | Usage text shown in the generated help, for example run <db> "SELECT ..." |
description | One-line help text shown beside the usage |
repair | Optional pre-parse repair for malformed LLM output |
handler | Command implementation. Receives the remaining args after the subcommand name |
defineSubcommandGroup(name, subcommands)
defineSubcommandGroup() returns a just-bash Command that dispatches on the
first positional argument.
const sandbox = await createVirtualSandbox({
fs: new InMemoryFs(),
customCommands: [defineSubcommandGroup('greet', subcommands)],
});
await sandbox.executeCommand('greet hello world');
// stdout: "Hello, world!\n"
await sandbox.executeCommand('greet');
// exitCode: 1
// stderr includes:
// greet: missing subcommand
//
// Usage:
// greet hello <name> ...
// greet goodbye <name> ...buildSubcommandRepair(name, subcommands)
LLMs often produce malformed quoting. buildSubcommandRepair() lets you attach
per-subcommand repair logic that only runs when the raw command fails to parse.
const repair = buildSubcommandRepair('sql', {
run: {
usage: 'run <db> "SELECT ..."',
description: 'Execute query',
repair: repairDbAndPayload,
handler: async (args) => ({
stdout: args.join(' '),
stderr: '',
exitCode: 0,
}),
},
});
await createBashTool({
sandbox: await createVirtualSandbox({ fs: new InMemoryFs() }),
onBeforeBashCall: ({ command }) => ({ command: repair(command) }),
});If the repaired command still fails to parse, the original input is preserved and just-bash reports the normal parse error.
Quote Helpers
stripQuoteArtifacts(raw)
Strips one outer layer of matching quotes and trims whitespace:
stripQuoteArtifacts('"SELECT 1"'); // SELECT 1
stripQuoteArtifacts("'SELECT 1'"); // SELECT 1
stripQuoteArtifacts('"unclosed'); // unclosedrepairQuotedArg(rawArgs)
Repairs a single quoted payload by stripping outer quotes, escaping inner
single quotes with the POSIX '\'' pattern, and re-wrapping the result:
repairQuotedArg('SELECT 1'); // 'SELECT 1'
repairQuotedArg(`don't`); // 'don'\''t'
repairQuotedArg(''); // nullUse it directly when the payload is one quoted argument, or wrap it in a small
helper when your command has a selector before the quoted body, such as
sql run <db> "SELECT ...".
Related Sandbox Helpers
Subcommand handlers compose cleanly with two other sandbox APIs:
useBashMeta()attaches host-only metadata or a model-visible reminder to the current tool result.BashExceptionlets a handler fail with a structuredCommandResultinstead of throwing a generic error.
See Sandbox for those helpers and for the broader
createBashTool() wrapper behavior.