agent-cli-implementation
Overview
Create a CLI tool in packages/ to invoke agents defined in .claude/agents/ directory programmatically.
Agent File Structure
.claude/agents/{agent-name}.md:
---name: my-agentdescription: What this agent doesmodel: sonnet # or haiku, opustools: - Read - Write - Bash---
# Agent Instructions
Your agent's system prompt and behavior goes here.
## Capabilities- What it can do- How it should respondCLI Implementation Approaches
1. Direct Claude CLI Invocation
# One-shot executionclaude --dangerously-skip-permissions -p "$(cat .claude/agents/my-agent.md)"
# With additional promptclaude -p "$(cat .claude/agents/my-agent.md)\n\nTask: $1"2. Claude Agent SDK (TypeScript/Node.js)
The Claude Agent SDK provides a high-level interface for building autonomous agents with tool access.
import { query } from "@anthropic-ai/claude-agent-sdk";import { readFileSync } from "fs";import matter from "gray-matter";
async function runAgent(agentName: string, task: string) { const agentPath = `.claude/agents/${agentName}.md`; const fileContent = readFileSync(agentPath, "utf-8"); const { data: frontmatter, content: systemPrompt } = matter(fileContent);
// Map model shorthand to full model ID const modelMap: Record<string, string> = { haiku: "claude-haiku-4-20250514", sonnet: "claude-sonnet-4-20250514", opus: "claude-opus-4-20250514", }; const model = modelMap[frontmatter.model] || "claude-sonnet-4-20250514";
// Run agent using SDK query function const result = query({ prompt: task, options: { model, systemPrompt, tools: frontmatter.tools || ["Read", "Write", "Bash"], permissionMode: "bypassPermissions", allowDangerouslySkipPermissions: true, }, });
// Stream through messages and collect result let output = ""; for await (const message of result) { if (message.type === "result" && message.subtype === "success") { output = message.result; } } return output;}
// Usage: await runAgent("code-reviewer", "Review the auth module")Key SDK Features:
- Tool Access: Built-in support for Read, Write, Bash, Glob, Grep, etc.
- Autonomous Execution: Agent loops, makes decisions, and completes complex tasks
- Streaming: AsyncGenerator yields messages as they arrive
- Permission Control:
permissionModefor automated or interactive execution
3. Shell Script Wrapper
#!/bin/bashAGENT_NAME=$1TASK="${@:2}"
if [ -z "$AGENT_NAME" ]; then echo "Usage: agent <agent-name> <task>" exit 1fi
AGENT_FILE=".claude/agents/${AGENT_NAME}.md"
if [ ! -f "$AGENT_FILE" ]; then echo "Agent not found: $AGENT_FILE" exit 1fi
claude --dangerously-skip-permissions -p "$(cat $AGENT_FILE)
Task: $TASK"Package Structure
NestJS + nest-commander pattern (consistent with x-cli, google-cli):
packages/agent/├── package.json├── tsconfig.json├── bunfig.toml├── src/│ ├── main.ts # CommandFactory entry point│ ├── app.module.ts # Root module│ ├── commands/│ │ ├── index.ts│ │ ├── list.command.ts # List available agents│ │ └── run.command.ts # Run agent with task│ └── agent/│ ├── agent.module.ts│ ├── agent.service.ts # Load/parse agent files│ └── runner.service.ts # Claude Agent SDK invocationExample package.json
{ "name": "@research/agent", "version": "1.0.0", "type": "module", "bin": { "agent": "./dist/main.js" }, "scripts": { "build": "bun build src/main.ts --outdir dist --target node", "dev": "bun run src/main.ts" }, "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.1.0", "@nestjs/common": "^11.0.0", "@nestjs/core": "^11.0.0", "nest-commander": "^3.15.0", "gray-matter": "^4.0.3", "reflect-metadata": "^0.2.2" }}Usage
# List available agentsbun run agent list
# Run an agent with a taskbun run agent run code-reviewer "Review the changes in src/"
# Interactive modebun run agent chat code-reviewerReference
- Claude Agent SDK (npm) - Official SDK package
- Claude Agent SDK (GitHub) - Source and examples
- Building Agents with Claude Agent SDK - Engineering blog
- uptownhr/claude-code-agent - Docker/k8s deployment example
Related
- subagents-vs-skills.md - Sub-agent architecture details
- custom-slash-commands.md - Slash command patterns