Use the CSV Processing Skill
maincsv skill allows you to analyze and transform CSV data using standard bash tools like awk, cut, sort, and grep. It provides a set of specialized scripts for common data manipulation tasks.repository·main·Indexed 20 days ago
https://github.com/vercel-labs/bash-toolA 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.
csv skill allows you to analyze and transform CSV data using standard bash tools like awk, cut, sort, and grep. It provides a set of specialized scripts for common data manipulation tasks.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.shimport {
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 },
});bash-tool is designed to provide sandboxed execution environments for AI agents.
Core Concepts:
just-bash (a simulated environment). For full VM capabilities (supporting Node.js, Python, etc.), you must provide a @vercel/sandbox instance.files option are written to the sandbox before the tools are returned to the agent.bash tool's description automatically includes the current working directory and a list of available files so the LLM has context./workspace. All files are written relative to the destination path.stop() method on the tools themselves.You can provide an AI agent with modular capabilities (skills) by combining createSkillTool and createBashTool.
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.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...",
});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",
});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 },
});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 doneTo run the provided skills-tool demonstration from the repository root, use npx tsx to execute the index file:
npx tsx examples/skills-tool/index.tsTo use the basic bash tool functionality, install bash-tool and just-bash via npm:
npm install bash-tool just-bashFor full VM support, install @vercel/sandbox or another sandbox product instead of just-bash.
A skill is represented as a directory within your designated skills/ folder. To create a new skill, follow this structure:
skills/my-new-skill/).SKILL.md file containing YAML frontmatter for metadata and Markdown for instructions.Example Directory Structure:
skills/
├── my-skill/
│ ├── SKILL.md # Instructions (YAML frontmatter + markdown)
│ ├── task1.sh # Bash script
│ └── task2.sh # Bash scriptTo 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/",
});To preserve files and state between different serverless function calls, use Sandbox.get to reconnect to an existing sandbox using its sandboxId.
sandboxId (e.g., in a database or session).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