Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

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>;
}
FieldDescription
usageUsage text shown in the generated help, for example run <db> "SELECT ..."
descriptionOne-line help text shown beside the usage
repairOptional pre-parse repair for malformed LLM output
handlerCommand 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'); // unclosed

repairQuotedArg(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(''); // null

Use 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 ...".

Subcommand handlers compose cleanly with two other sandbox APIs:

  • useBashMeta() attaches host-only metadata or a model-visible reminder to the current tool result.
  • BashException lets a handler fail with a structured CommandResult instead of throwing a generic error.

See Sandbox for those helpers and for the broader createBashTool() wrapper behavior.