bash-tool

repository·main·Indexed 20 days ago

https://github.com/vercel-labs/bash-tool

A generic bash execution tool for AI agents compatible with the AI SDK. It provides a sandboxed environment for agents to execute commands, read files, and write files. Features include support for @vercel/sandbox for full VM capabilities, custom sandbox interfaces, and an experimental skills system using createSkillTool to implement modular AI capabilities via SKILL.md and bash scripts.

Tokens
14.5K
Snippets
51
Records
63
Agent score
67%

What's inside bash-tool

  1. Use experimental Skills to extend agent capabilities

    main

    Skills are modular capabilities defined in a directory. Each skill contains a SKILL.md file (with instructions and YAML frontmatter) and optional scripts.

    To use them, use experimental_createSkillTool to discover the skill, its files, and instructions, then pass them to createBashTool and your agent.

    Skill Directory Structure:

    skills/
    ├── csv/
    │   ├── SKILL.md         # Required: instructions with YAML frontmatter
    │   └── scripts/
    │       ├── analyze.sh
    │       └── filter.sh
    └── text/
    │   ├── SKILL.md
    │   └── scripts/
    │       └── search.sh
    import {
      experimental_createSkillTool as createSkillTool,
      createBashTool,
    } from "bash-tool";
    import { ToolLoopAgent } from "ai";
    
    // Discover skills and get files to upload
    const { skill, files, instructions } = await createSkillTool({
      skillsDirectory: "./skills",
    });
    
    const { tools } = await createBashTool({
      files,
      extraInstructions: instructions,
    });
    
    // Use both tools with an agent
    const agent = new ToolLoopAgent({
      model,
      tools: { skill, ...tools },
    });
    import {
      experimental_createSkillTool as createSkillTool,
      createBashTool,
    } from "bash-tool";
    import { ToolLoopAgent } from "ai";
    
    // Discover skills and get files to upload
    const { skill, files, instructions } = await createSkillTool({
      skillsDirectory: "./skills",
    });
    
    // Providing a bash tool with skills is optional if your skill only has a SKILL.md file
    // and no further files and scripts.
    const { tools } = await createBashTool({
      files,
      extraInstructions: instructions,
    });
    
    // Use both tools with an agent
    const agent = new ToolLoopAgent({
      model,
      tools: { skill, ...tools },
    });
  2. How bash-tool works and its key behaviors

    main

    bash-tool is designed to provide sandboxed execution environments for AI agents.

    Core Concepts:

    • Sandboxing: By default, it uses just-bash (a simulated environment). For full VM capabilities (supporting Node.js, Python, etc.), you must provide a @vercel/sandbox instance.
    • Pre-population: Files provided in the files option are written to the sandbox before the tools are returned to the agent.
    • Contextual Awareness: The bash tool's description automatically includes the current working directory and a list of available files so the LLM has context.
    • Working Directory: The default working directory is /workspace. All files are written relative to the destination path.
    • Lifecycle: The sandbox lifecycle is managed externally; there is no stop() method on the tools themselves.
  3. How to implement modular AI skills with createSkillTool and createBashTool

    main

    You can provide an AI agent with modular capabilities (skills) by combining createSkillTool and createBashTool.

    1. createSkillTool discovers skill directories and returns their files, names, and instructions. It implements a 'progressive disclosure' pattern where the agent initially only sees skill names via a loadSkill tool, and then calls loadSkill(name) to retrieve detailed instructions and script availability.
    2. createBashTool takes the files discovered by createSkillTool and uploads them to a sandbox, providing a bash tool that the agent can use to execute the skill's scripts.

    This pattern allows for highly composable agents that can handle complex tasks like CSV analysis or text processing using standard Unix tools.

    import { ToolLoopAgent } from "ai";
    import {
      experimental_createSkillTool as createSkillTool,
      createBashTool,
    } from "bash-tool";
    
    // 1. Discover skills and get files
    const { loadSkill, skills, files, instructions } = await createSkillTool({
      skillsDirectory: "./skills",
    });
    
    // 2. Create bash tool with skill files
    const { tools } = await createBashTool({
      files,
      extraInstructions: instructions,
    });
    
    // 3. Create agent with both tools
    const agent = new ToolLoopAgent({
      model: "anthropic/claude-haiku-4.5",
      tools: {
        loadSkill,
        bash: tools.bash,
      ,
    });
    
    // 4. Run the agent
    const result = await agent.generate({
      prompt: "Analyze this CSV data...",
    });
  4. Quickstart: Integrate bash-tool with AI SDK ToolLoopAgent

    main

    You can initialize a bash tool with a set of files and pass it to an AI SDK ToolLoopAgent. This allows the agent to execute commands, read, and write files within the provided sandbox environment.

    Note: If you are using AI SDK 6, import stepCountIs instead of isStepCount for the stopWhen option.

    import { createBashTool } from "bash-tool";
    import { isStepCount, ToolLoopAgent } from "ai";
    
    const { tools } = await createBashTool({
      files: {
        "src/index.ts": "export const hello = 'world';",
        "package.json": '{"name": "my-project"}',
      },
    });
    
    const agent = new ToolLoopAgent({
      model: yourModel,
      tools,
      // Or use just the bash tool as tools: {bash: tools.bash}
      stopWhen: isStepCount(20),
    });
    
    const result = await agent.generate({
      prompt: "Analyze the project and create a summary report",
    });
  5. Use instruction-only skills without bash

    main

    If a skill only requires providing knowledge, style guides, or process documentation to the AI without executing code, you can use createSkillTool standalone. This is useful for providing domain knowledge, formatting rules, or best practices.

    For these skills, the SKILL.md file should contain only the YAML frontmatter and Markdown instructions, with no accompanying .sh files.

    import { experimental_createSkillTool as createSkillTool } from "bash-tool";
    
    // Discover instruction-only skills
    const { skill, skills } = await createSkillTool({
      skillsDirectory: "./knowledge",
    });
    
    // Use just the skill tool - no bash needed
    const agent = new ToolLoopAgent({
      model: "anthropic/claude-haiku-4.5",
      tools: { skill },
    });
  6. Use @vercel/sandbox for a full VM

    main

    Since the default just-bash is a simulation that cannot run binaries like Python or Node.js, use @vercel/sandbox to provide a full virtual machine environment.

    import { Sandbox } from "@vercel/sandbox";
    const vm = await Sandbox.create();
    const { tools } = await createBashTool({ sandbox: vm });
    // Call vm.stop() when done
  7. Install bash-tool and just-bash

    main

    To use the basic bash tool functionality, install bash-tool and just-bash via npm:

    npm install bash-tool just-bash

    For full VM support, install @vercel/sandbox or another sandbox product instead of just-bash.

  8. Create a new skill directory and structure

    main

    A skill is represented as a directory within your designated skills/ folder. To create a new skill, follow this structure:

    1. Create a new directory (e.g., skills/my-new-skill/).
    2. Add a SKILL.md file containing YAML frontmatter for metadata and Markdown for instructions.
    3. Add one or more bash scripts that the AI can execute to perform the skill's tasks.

    Example Directory Structure:

    skills/
    ├── my-skill/
    │   ├── SKILL.md      # Instructions (YAML frontmatter + markdown)
    │   ├── task1.sh      # Bash script
    │   └── task2.sh      # Bash script
  9. Initialize bash-tool for AI agents

    main

    To provide an AI agent with filesystem and shell capabilities, use createBashTool. This function returns a tools object containing bash, readFile, and writeFile tools. You can pre-populate the sandbox with files using the files option.

    Note: If you are using AI SDK 6, use stepCountIs instead of isStepCount for the stopWhen option in ToolLoopAgent.

    import { createBashTool } from "bash-tool";
    import { isStepCount, ToolLoopAgent } from "ai";
    
    const { tools } = await createBashTool({
      files: {
        "src/index.ts": "export const x = 1;",
        "package.json": '{"name": "test"}',
      },
    });
    
    const agent = new ToolLoopAgent({
      model,
      tools,
      stopWhen: isStepCount(20),
    });
    
    const result = await agent.generate({
      prompt: "List files in src/",
    });
  10. Maintain a persistent sandbox across serverless invocations

    main

    To preserve files and state between different serverless function calls, use Sandbox.get to reconnect to an existing sandbox using its sandboxId.

    1. First invocation: Create a sandbox and store its sandboxId (e.g., in a database or session).
    2. Subsequent invocations: Reconnect using Sandbox.get({ sandboxId }) and pass the resulting sandbox instance to createBashTool.
    import { Sandbox } from "@vercel/sandbox";
    
    // First invocation: create sandbox and store the ID
    const newSandbox = await Sandbox.create();
    const sandboxId = newSandbox.sandboxId;
    
    // Subsequent invocations: reconnect to existing sandbox
    const existingSandbox = await Sandbox.get({ sandboxId });
    const { tools } = await createBashTool({ sandbox: existingSandbox });
    import { Sandbox } from "@vercel/sandbox";
    
    // First invocation: create sandbox and store the ID
    const newSandbox = await Sandbox.create();
    const sandboxId = newSandbox.sandboxId;
    // Store sandboxId in database, session, or return to client
    
    // Subsequent invocations: reconnect to existing sandbox
    const existingSandbox = await Sandbox.get({ sandboxId });
    const { tools } = await createBashTool({ sandbox: existingSandbox });
    // All previous files and state are preserved