@compilr-dev/agents
v0.1.0
Published
Lightweight multi-LLM agent library for building CLI AI assistants
Maintainers
Readme
@compilr-dev/agents
Lightweight multi-LLM agent library for building CLI AI assistants.
Features
- Multi-LLM Support: Abstract provider interface supporting Claude, OpenAI, Gemini, and local models
- Tool System: Type-safe tool definitions with JSON Schema validation
- Event Streaming: Real-time execution monitoring with typed events
- Abort Support: Cancel running agents with
AbortSignal - Built-in Tools: Ready-to-use file and bash tools
- Anchors: Critical information that survives context compaction
- Guardrails: Pattern-based safety checks (warn/confirm/block dangerous operations)
- Permissions: Tool-level permission management (always/session/once/deny)
- Project Memory: Load project instructions from CLAUDE.md, GEMINI.md, etc.
- TypeScript First: Full type safety throughout
Installation
npm install @compilr-dev/agentsFor Claude support:
npm install @compilr-dev/agents @anthropic-ai/sdkQuick Start
import { Agent, MockProvider } from '@compilr-dev/agents';
// Create an agent with a provider
const provider = new MockProvider();
provider.addResponse('Hello! How can I help you today?');
const agent = new Agent({
provider,
systemPrompt: 'You are a helpful assistant.',
});
// Run the agent
const result = await agent.run('Hello!');
console.log(result.response); // "Hello! How can I help you today?"
console.log(result.iterations); // 1With Claude
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
systemPrompt: 'You are a helpful coding assistant.',
maxIterations: 10,
});
const result = await agent.run('Explain async/await in JavaScript');
console.log(result.response);Core Concepts
Agent
The Agent class orchestrates LLM interactions and tool execution:
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: 'your-key' }),
systemPrompt: 'You are a helpful assistant.',
maxIterations: 10, // Default: 10
onEvent: (event) => {
// Real-time execution monitoring
console.log(event.type);
},
});Messages
The library provides utilities for working with conversation messages:
import {
createUserMessage,
createAssistantMessage,
createToolResultMessage,
getTextContent
} from '@compilr-dev/agents';
// Create messages
const userMsg = createUserMessage('Hello!');
const assistantMsg = createAssistantMessage('Hi there!');
// Extract text from message with multiple content blocks
const text = getTextContent(assistantMsg);Tools
Define tools that agents can use:
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
const calculatorTool = defineTool({
name: 'calculator',
description: 'Perform basic math operations',
inputSchema: {
type: 'object',
properties: {
operation: { type: 'string', description: 'add, subtract, multiply, divide' },
a: { type: 'number' },
b: { type: 'number' },
},
required: ['operation', 'a', 'b'],
},
execute: async ({ operation, a, b }) => {
switch (operation) {
case 'add': return createSuccessResult(a + b);
case 'subtract': return createSuccessResult(a - b);
case 'multiply': return createSuccessResult(a * b);
case 'divide':
if (b === 0) return createErrorResult('Division by zero');
return createSuccessResult(a / b);
default:
return createErrorResult(`Unknown operation: ${operation}`);
}
},
});
// Register with agent
agent.registerTool(calculatorTool);Tool Registry
Manage tools with the registry:
import { DefaultToolRegistry, defineTool } from '@compilr-dev/agents';
const registry = new DefaultToolRegistry();
// Register tools
registry.register(myTool);
// Get tool definitions for LLM
const definitions = registry.getDefinitions();
// Execute a tool
const result = await registry.execute('tool_name', { arg: 'value' });Built-in Tools
File Operations
import { readFileTool, writeFileTool } from '@compilr-dev/agents';
agent.registerTool(readFileTool);
agent.registerTool(writeFileTool);Bash Execution
import { createBashTool } from '@compilr-dev/agents';
const bashTool = createBashTool({
workingDir: '/path/to/project',
timeout: 30000, // 30 seconds
allowedCommands: ['git', 'npm', 'ls'], // Optional whitelist
});
agent.registerTool(bashTool);Event System
Monitor agent execution in real-time:
import type { AgentEvent } from '@compilr-dev/agents';
const agent = new Agent({
provider,
onEvent: (event: AgentEvent) => {
switch (event.type) {
case 'iteration_start':
console.log(`Starting iteration ${event.iteration}`);
break;
case 'llm_start':
console.log('LLM thinking...');
break;
case 'llm_end':
console.log(`LLM done, tool uses: ${event.hasToolUses}`);
break;
case 'tool_start':
console.log(`Calling tool: ${event.name}`);
break;
case 'tool_end':
console.log(`Tool result: ${event.result.success}`);
break;
case 'done':
console.log(`Final: ${event.response}`);
break;
}
},
});Streaming Events
For CLI applications, stream events as they happen:
for await (const event of agent.stream('Hello!')) {
if (event.type === 'llm_chunk') {
process.stdout.write(event.chunk.text || '');
}
}Abort Support
Cancel running agents:
const controller = new AbortController();
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const result = await agent.run('Complex task...', {
signal: controller.signal,
});
} catch (error) {
if (error instanceof AbortError) {
console.log('Agent was cancelled');
}
}Error Handling
The library provides specific error types:
import {
AgentError,
ProviderError,
ToolError,
ValidationError,
MaxIterationsError,
AbortError,
isAgentError,
isProviderError,
isToolError,
wrapError,
} from '@compilr-dev/agents';
try {
await agent.run('...');
} catch (error) {
if (isProviderError(error)) {
console.log(`Provider ${error.provider} failed: ${error.message}`);
} else if (isToolError(error)) {
console.log(`Tool ${error.toolName} failed: ${error.message}`);
} else if (error instanceof MaxIterationsError) {
console.log(`Reached max iterations: ${error.maxIterations}`);
}
}Providers
ClaudeProvider
import { ClaudeProvider } from '@compilr-dev/agents';
const provider = new ClaudeProvider({
apiKey: process.env.ANTHROPIC_API_KEY,
model: 'claude-sonnet-4-20250514', // Default
maxTokens: 4096,
});MockProvider (for testing)
import { MockProvider } from '@compilr-dev/agents';
const provider = new MockProvider();
// Queue responses
provider.addResponse('Simple text response');
provider.addResponse({
text: 'Using a tool...',
toolCalls: [{ id: 'call_1', name: 'my_tool', input: { arg: 'value' } }],
});
// Queue an error
provider.addError(new Error('API failed'));
// Add delay
provider.addResponse('Delayed response', 1000);
// Access history
console.log(provider.history); // All chat() callsCustom Providers
Implement the LLMProvider interface:
import type { LLMProvider, Message, ChatOptions, StreamChunk } from '@compilr-dev/agents';
class MyProvider implements LLMProvider {
readonly name = 'my-provider';
async *chat(
messages: Message[],
options?: ChatOptions
): AsyncIterable<StreamChunk> {
// Your implementation
yield { type: 'text', text: 'Response text' };
yield { type: 'end', stopReason: 'end_turn' };
}
async countTokens(messages: Message[]): Promise<number> {
// Optional token counting
return messages.length * 100;
}
}API Reference
Agent
class Agent {
constructor(options: AgentOptions);
// Run agent and return final result
run(userMessage: string, options?: RunOptions): Promise<AgentResult>;
// Stream events during execution
stream(userMessage: string, options?: RunOptions): AsyncIterable<AgentEvent>;
// Tool management
registerTool(tool: Tool): void;
registerTools(tools: Tool[]): void;
getToolDefinitions(): ToolDefinition[];
}Types
interface AgentOptions {
provider: LLMProvider;
systemPrompt?: string;
maxIterations?: number;
onEvent?: (event: AgentEvent) => void;
}
interface RunOptions {
signal?: AbortSignal;
}
interface AgentResult {
response: string;
iterations: number;
toolCalls: ToolCallInfo[];
}
type AgentEvent =
| { type: 'iteration_start'; iteration: number }
| { type: 'llm_start' }
| { type: 'llm_chunk'; chunk: StreamChunk }
| { type: 'llm_end'; text: string; hasToolUses: boolean }
| { type: 'tool_start'; name: string; input: Record<string, unknown> }
| { type: 'tool_end'; name: string; result: ToolExecutionResult }
| { type: 'iteration_end'; iteration: number }
| { type: 'done'; response: string };Context Management
Manage context windows in long-running agent sessions:
import { ContextManager, DEFAULT_CONTEXT_CONFIG } from '@compilr-dev/agents';
const contextManager = new ContextManager({
provider,
config: {
maxContextTokens: 200000, // Claude's limit
compaction: {
triggerThreshold: 0.5, // Compact at 50% utilization
preserveRecentTurns: 10, // Keep last 10 turns
},
summarization: {
triggerThreshold: 0.9, // Summarize at 90%
preserveRecentMessages: 6,
},
},
onEvent: (event) => {
if (event.type === 'context_warning') {
console.log(`Context at ${event.utilization * 100}%`);
}
},
});
// Track token usage
await contextManager.updateTokenCount(messages);
console.log(`Utilization: ${contextManager.getUtilization() * 100}%`);
// Check if action needed
if (contextManager.needsSummarization()) {
const { messages: summarized } = await contextManager.summarize(
messages,
async (msgs) => 'Summary of conversation...' // Your LLM call
);
}
// Filter large content before adding
const { content, filtered } = contextManager.filterContent(
largeToolResult,
'tool_result'
);Context Strategies
| Strategy | Trigger | Reversible | Description | |----------|---------|------------|-------------| | Filtering | Before add | N/A | Truncate large content (>80K tokens) | | Compaction | 50% or 20 turns | Yes | Save old results to files | | Summarization | 90% | No | Compress entire history |
Anchors (Critical Information Persistence)
Anchors are critical pieces of information that survive context compaction. They're automatically re-injected into every LLM call:
import { Agent, AnchorManager, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
systemPrompt: 'You are a helpful assistant.',
anchors: {
maxAnchors: 20, // Maximum number of anchors
maxTokens: 2000, // Token budget for anchors
includeDefaults: true, // Include safety anchors (git, file operations)
},
});
// Add a session anchor (survives until session ends)
agent.addAnchor({
content: 'User prefers TypeScript with strict mode',
priority: 'critical',
scope: 'session',
});
// Add a persistent anchor (saved to disk)
agent.addAnchor({
content: 'Never modify files in /etc',
priority: 'safety',
scope: 'persistent',
tags: ['filesystem'],
});
// Add a temporary anchor (expires after a time)
agent.addAnchor({
content: 'Currently debugging the auth module',
priority: 'info',
scope: 'temporary',
expiresAt: new Date(Date.now() + 3600000), // 1 hour
});Anchor Priorities
| Priority | Description | Format in LLM |
|----------|-------------|---------------|
| critical | Must remember | ### CRITICAL (Must Remember) |
| safety | Check before acting | ### SAFETY (Check Before Acting) |
| info | Contextual information | ### INFO |
Standalone AnchorManager
import { AnchorManager } from '@compilr-dev/agents';
const anchors = new AnchorManager({
maxAnchors: 20,
maxTokens: 2000,
persistPath: '~/.myapp/anchors.json', // Optional persistence
includeDefaults: true,
});
// Add anchors
anchors.add({ content: 'Important info', priority: 'critical', scope: 'session' });
// Query anchors
const critical = anchors.getAll({ priority: 'critical' });
const tagged = anchors.getAll({ tags: ['git'] });
// Format for LLM injection
const formatted = anchors.format();
// Returns:
// ### CRITICAL (Must Remember)
// - Important info
// Monitor token usage
console.log(`Utilization: ${anchors.getUtilization() * 100}%`);Guardrails (Safety Checks)
Guardrails are pattern-based safety checks that run before tool execution. They can warn, require confirmation, or block dangerous operations:
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
systemPrompt: 'You are a helpful assistant.',
guardrails: {
enabled: true,
includeDefaults: true, // 15 built-in patterns for git, rm, DROP TABLE, etc.
custom: [
{
id: 'no-prod-db',
name: 'Production Database Protection',
description: 'Prevent operations on production database',
patterns: [/prod.*db/i, /production.*database/i],
action: 'block', // 'warn' | 'confirm' | 'block'
message: 'Operations on production database are blocked',
scope: ['bash'], // Optional: limit to specific tools
tags: ['database', 'production'],
},
],
onTriggered: async (result, context) => {
// Handle 'confirm' actions
if (result.action === 'confirm') {
return await askUserConfirmation(result.guardrail.message);
}
return result.action !== 'block';
},
},
});Built-in Guardrails
| ID | Action | Pattern | Description |
|----|--------|---------|-------------|
| git-reset-hard | confirm | git reset --hard | Prevents accidental loss of uncommitted changes |
| git-push-force | confirm | git push --force | Prevents force pushing to branches |
| rm-rf | confirm | rm -rf | Prevents recursive deletion |
| rm-root | block | rm -rf / | Blocks deletion of root filesystem |
| drop-table | confirm | DROP TABLE | Prevents accidental table deletion |
| truncate-table | confirm | TRUNCATE | Prevents data loss |
| delete-where-all | warn | DELETE FROM without WHERE | Warns about deleting all rows |
| secrets-env | warn | API keys, passwords | Warns about exposing secrets |
| chmod-777 | warn | chmod 777 | Warns about insecure permissions |
| curl-pipe-bash | block | curl | bash | Blocks unsafe remote execution |
| eval-code | warn | eval() patterns | Warns about code injection risks |
| sudo-rm | confirm | sudo rm | Confirms privileged deletion |
| git-clean | confirm | git clean -fd | Confirms removing untracked files |
| format-disk | block | mkfs, dd | Blocks disk formatting |
| kill-all | confirm | killall, pkill | Confirms killing processes |
Standalone GuardrailManager
import { GuardrailManager, getGuardrailsByTag } from '@compilr-dev/agents';
const guardrails = new GuardrailManager({
enabled: true,
includeDefaults: true,
});
// Check before tool execution
const result = guardrails.check('bash', { command: 'rm -rf /tmp/test' });
if (result.triggered) {
console.log(`Guardrail triggered: ${result.guardrail.name}`);
console.log(`Action: ${result.action}`); // 'warn' | 'confirm' | 'block'
console.log(`Message: ${result.guardrail.message}`);
}
// Check and handle (with confirmation callback)
const { proceed, result } = await guardrails.checkAndHandle('bash', input);
if (!proceed) {
console.log('Operation blocked by guardrail');
}
// Get guardrails by tag
const gitGuardrails = getGuardrailsByTag('git');
const destructiveGuardrails = getGuardrailsByTag('destructive');
// Enable/disable individual guardrails
guardrails.disable('rm-rf');
guardrails.enable('rm-rf');Guardrail Events
Monitor guardrail activity:
guardrails.onEvent((event) => {
switch (event.type) {
case 'guardrail:triggered':
console.log(`Guardrail ${event.result.guardrail.name} triggered`);
break;
case 'guardrail:blocked':
console.log('Operation blocked');
break;
case 'guardrail:warning':
console.log('Warning issued');
break;
case 'guardrail:confirmed':
console.log('User confirmed dangerous operation');
break;
case 'guardrail:cancelled':
console.log('User cancelled operation');
break;
}
});Permissions (Tool-level Access Control)
Permissions provide fine-grained control over which tools can execute and when user approval is required:
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
systemPrompt: 'You are a helpful assistant.',
permissions: {
enabled: true,
defaultLevel: 'always', // Default for tools without rules
includeDefaults: true, // Include default rules for bash, write_file, edit
rules: [
{ toolName: 'bash', level: 'once', description: 'Shell commands' },
{ toolName: 'write_file', level: 'session', description: 'File writes' },
{ toolName: 'delete_*', level: 'deny', description: 'Delete operations' },
],
onPermissionRequest: async (request) => {
// Handle permission requests (e.g., prompt user)
console.log(`Tool: ${request.toolName}`);
console.log(`Preview: ${request.preview}`);
return await askUser(`Allow ${request.toolName}?`);
},
},
});Permission Levels
| Level | Description | Behavior |
|-------|-------------|----------|
| always | No approval needed | Tool executes immediately |
| session | Ask once per session | After approval, tool runs freely |
| once | Ask every time | User must approve each execution |
| deny | Always blocked | Tool cannot execute |
Wildcard Patterns
Permission rules support wildcards for flexible matching:
// Match all tools starting with "delete_"
{ toolName: 'delete_*', level: 'deny' }
// Match all tools ending with "_file"
{ toolName: '*_file', level: 'session' }
// Match tools containing "dangerous"
{ toolName: '*dangerous*', level: 'once' }Standalone PermissionManager
import { PermissionManager } from '@compilr-dev/agents';
const permissions = new PermissionManager({
enabled: true,
defaultLevel: 'always',
onPermissionRequest: async (request) => {
return await promptUser(request.preview);
},
});
// Add rules dynamically
permissions.addRule({
toolName: 'bash',
level: 'once',
description: 'Shell commands',
tags: ['shell', 'dangerous'],
});
// Check permission before execution
const result = await permissions.check('bash', { command: 'rm -rf /tmp/test' });
if (result.allowed) {
// Proceed with execution
} else {
console.log(`Denied: ${result.reason}`);
}
// Grant session-level permission programmatically
permissions.grantSession('write_file');
// Check if tool has session grant
if (permissions.hasSessionGrant('write_file')) {
console.log('write_file approved for this session');
}Permission Events
Monitor permission activity:
permissions.onEvent((event) => {
switch (event.type) {
case 'permission:granted':
console.log(`${event.toolName} allowed`);
break;
case 'permission:denied':
console.log(`${event.toolName} denied`);
break;
case 'permission:asked':
console.log(`Asking about ${event.toolName}`);
break;
case 'permission:session_granted':
console.log(`${event.toolName} approved for session`);
break;
}
});Permissions vs Guardrails
Both systems provide safety, but serve different purposes:
| Aspect | Permissions | Guardrails | |--------|-------------|------------| | Purpose | Access control | Content safety | | Scope | Tool-level | Input patterns | | Timing | Before tool call | Before execution | | Typical Use | "Can this tool run?" | "Is this input safe?" | | User Interaction | Session/once approval | Confirm dangerous ops |
Use them together for comprehensive safety:
- Permissions control which tools can be used
- Guardrails check what inputs are safe
Project Memory (CLAUDE.md, GEMINI.md, etc.)
Project Memory automatically discovers and loads project-specific instructions from markdown files. It supports various LLM-specific naming conventions:
| Provider | Files Searched |
|----------|----------------|
| Claude | CLAUDE.md, .claude.md, .claude/instructions.md |
| Gemini | GEMINI.md, .gemini.md, .gemini/instructions.md |
| OpenAI/GPT | OPENAI.md, GPT.md, CHATGPT.md |
| Copilot | COPILOT.md, .github/copilot-instructions.md |
| Cursor | CURSOR.md, .cursorrules, .cursor/rules |
| Generic | PROJECT.md, INSTRUCTIONS.md, AI.md, CONTEXT.md |
Using with Agent
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
// Option 1: Factory method (recommended)
const agent = await Agent.createWithMemory(
{
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
systemPrompt: 'You are a helpful assistant.',
},
{ providers: 'claude', includeGeneric: true },
'/path/to/project' // defaults to cwd
);
// Access loaded memory
const memory = agent.getProjectMemory();
console.log(`Loaded ${memory?.files.length} instruction files`);
// Option 2: Pre-load memory manually
import { ProjectMemoryLoader } from '@compilr-dev/agents';
const loader = new ProjectMemoryLoader({
providers: ['claude', 'gemini'],
includeGeneric: true,
});
const memory = await loader.load('/path/to/project');
const agent = new Agent({
provider,
systemPrompt: 'You are a helpful assistant.',
projectMemory: memory,
});Multiple Providers
Search for instructions from multiple LLM providers:
const agent = await Agent.createWithMemory(
{ provider },
{
providers: ['claude', 'gemini', 'openai'],
includeGeneric: true, // Also search PROJECT.md, AI.md, etc.
combineStrategy: 'concat', // Combine all found files
}
);ProjectMemoryLoader Options
const loader = new ProjectMemoryLoader({
// Provider(s) to search for
providers: 'claude', // or ['claude', 'gemini']
// Include generic patterns (PROJECT.md, AI.md, etc.)
includeGeneric: true,
// Search parent directories up to git root
searchParents: true,
stopAtGitRoot: true,
maxParentDepth: 10,
// How to combine multiple files
combineStrategy: 'concat', // 'concat' | 'priority' | 'dedupe'
// Content limits
maxContentSize: 100000, // 100KB max
// Headers in combined output
includeHeaders: true,
headerFormat: '# From: {relativePath}\n\n',
});Combine Strategies
| Strategy | Description |
|----------|-------------|
| concat | Concatenate all files with separators (default) |
| priority | Use only the highest priority file |
| dedupe | Concatenate but remove duplicate content |
Custom Patterns
Add custom file patterns for your own naming conventions:
const loader = new ProjectMemoryLoader({
providers: 'claude',
customPatterns: [
{ pattern: 'TEAM-RULES.md', priority: 0, description: 'Team rules' },
{ pattern: '.ai-config.md', priority: 1, description: 'AI config' },
],
});Standalone Usage
Use the loader without an Agent:
import {
loadProjectMemory,
hasProjectMemory,
getSupportedProviders,
getProviderPatterns,
} from '@compilr-dev/agents';
// Quick check if memory files exist
if (await hasProjectMemory('/path/to/project', { providers: 'claude' })) {
console.log('Found project instructions!');
}
// Load memory directly
const memory = await loadProjectMemory('/path/to/project', {
providers: ['claude', 'gemini'],
});
console.log(`Loaded ${memory.files.length} files`);
console.log(`Estimated tokens: ${memory.estimatedTokens}`);
// Get patterns for a provider
const patterns = getProviderPatterns('cursor');
// Returns: [{ pattern: 'CURSOR.md', ... }, { pattern: '.cursorrules', ... }]
// List all supported providers
const providers = getSupportedProviders();
// Returns: ['claude', 'anthropic', 'gemini', 'openai', 'gpt', 'copilot', 'cursor', 'codeium']Rate Limiting
Protect against API rate limits with automatic retry and backoff:
import { Agent, ClaudeProvider, createRateLimitedProvider } from '@compilr-dev/agents';
// Wrap any provider with rate limiting
const provider = createRateLimitedProvider(
new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
{
maxRequestsPerMinute: 50,
maxTokensPerMinute: 100000,
maxRetries: 3,
initialBackoffMs: 1000,
maxBackoffMs: 60000,
backoffMultiplier: 2,
onRateLimited: (info) => {
console.log(`Rate limited, waiting ${info.waitMs}ms`);
},
}
);
const agent = new Agent({ provider });Standalone Rate Limiter
import { RateLimiter } from '@compilr-dev/agents';
const limiter = new RateLimiter({
maxRequestsPerMinute: 60,
maxTokensPerMinute: 100000,
});
// Check before making requests
await limiter.waitForCapacity();
// Record usage after requests
limiter.recordRequest(1500); // tokens used
// Check current state
console.log(`Requests remaining: ${limiter.getAvailableRequests()}`);
console.log(`Tokens remaining: ${limiter.getAvailableTokens()}`);Usage Tracking
Track token usage across agent sessions:
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
usage: {
enabled: true,
warnThresholds: {
inputTokens: 100000,
outputTokens: 50000,
},
onWarning: (warning) => {
console.log(`Usage warning: ${warning.metric} at ${warning.currentValue}`);
},
},
});
// After running the agent
const result = await agent.run('Hello!');
// Get usage stats
const usage = agent.getUsage();
console.log(`Input tokens: ${usage.inputTokens}`);
console.log(`Output tokens: ${usage.outputTokens}`);
console.log(`Total tokens: ${usage.totalTokens}`);
console.log(`Requests: ${usage.requests}`);Rehearsal System (Impact Analysis)
Rehearsal analyzes potentially destructive operations before execution, showing what files and git state would be affected:
import { Agent, ClaudeProvider } from '@compilr-dev/agents';
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
rehearsal: {
enabled: true,
gitAnalysis: true, // Analyze git impact
fileAnalysis: true, // Analyze file impact
autoConfirm: false, // Require user confirmation
workingDirectory: '/path/to/project',
onRehearsalComplete: async (result) => {
console.log('=== Rehearsal Report ===');
console.log(`Files affected: ${result.files?.filesAffected || 0}`);
console.log(`Git impact: ${result.git?.summary || 'None'}`);
// Return true to proceed, false to cancel
return await askUserConfirmation('Proceed with these changes?');
},
},
});
// Destructive operations trigger rehearsal automatically
await agent.run('Delete all .log files and reset the git branch');Rehearsal Report
The rehearsal system provides detailed impact analysis:
interface RehearsalResult {
files?: {
filesAffected: number;
deletions: string[];
modifications: string[];
creations: string[];
totalSizeImpact: number;
};
git?: {
hasUncommittedChanges: boolean;
uncommittedFiles: string[];
branchesAffected: string[];
commitsAffected: number;
summary: string;
};
riskLevel: 'low' | 'medium' | 'high' | 'critical';
warnings: string[];
}Task Tool (Sub-Agents)
Spawn specialized sub-agents for complex tasks:
import { Agent, ClaudeProvider, createTaskTool } from '@compilr-dev/agents';
const taskTool = createTaskTool({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
agentTypes: {
explore: {
systemPrompt: 'You are a code exploration expert.',
tools: ['read_file', 'glob', 'grep'],
contextMode: 'full', // Inherit full conversation context
},
'code-review': {
systemPrompt: 'You are a code reviewer.',
tools: ['read_file', 'grep'],
contextMode: 'summary', // Get summarized context
},
refactor: {
systemPrompt: 'You are a refactoring expert.',
tools: ['read_file', 'write_file', 'edit'],
contextMode: 'none', // No inherited context
},
},
defaultThoroughness: 'medium', // 'quick' | 'medium' | 'thorough'
maxConcurrentTasks: 3,
});
agent.registerTool(taskTool);
// The LLM can now spawn sub-agents:
// task({ agentType: 'explore', prompt: 'Find all React components' })Built-in Agent Types
| Type | Purpose | Default Tools |
|------|---------|---------------|
| explore | Codebase exploration | glob, grep, read_file |
| code-review | Code quality review | read_file, grep |
| refactor | Code refactoring | read_file, write_file, edit |
| test-runner | Running tests | bash, read_file |
| doc-lookup | Documentation search | read_file, grep, web_fetch |
| debug | Debugging assistance | read_file, grep, bash |
| security-audit | Security analysis | read_file, grep |
| plan | Task planning | read_file, glob |
| general | General purpose | All tools |
Hooks System
Extend agent behavior with lifecycle hooks:
import { Agent, ClaudeProvider, HooksManager } from '@compilr-dev/agents';
const hooks = new HooksManager();
// Before each LLM call
hooks.register('beforeChat', async (context) => {
console.log(`Sending ${context.messages.length} messages`);
// Modify messages if needed
return { messages: context.messages };
});
// After each LLM response
hooks.register('afterChat', async (context) => {
console.log(`Received: ${context.response.text?.slice(0, 50)}...`);
});
// Before tool execution
hooks.register('beforeToolCall', async (context) => {
console.log(`Calling ${context.toolName} with`, context.input);
// Return { skip: true } to skip execution
});
// After tool execution
hooks.register('afterToolCall', async (context) => {
console.log(`${context.toolName} returned:`, context.result);
// Modify result if needed
return { result: context.result };
});
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
hooks,
});Skills
Skills are reusable prompt templates that enhance agent capabilities:
import { Agent, ClaudeProvider, SkillsManager, BUILTIN_SKILLS } from '@compilr-dev/agents';
// Use built-in skills
const skills = new SkillsManager({
skills: BUILTIN_SKILLS, // code-review, debug, explain, refactor, planning, security-review
});
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
skills,
});
// Invoke a skill
const result = await agent.run('/skill code-review src/auth.ts');Custom Skills
const customSkills = new SkillsManager({
skills: [
{
name: 'optimize',
description: 'Optimize code for performance',
prompt: `Analyze the following code and suggest performance optimizations:
- Identify bottlenecks
- Suggest algorithmic improvements
- Recommend caching strategies
- Consider memory usage`,
tags: ['performance', 'optimization'],
},
],
});Built-in Skills
| Skill | Description |
|-------|-------------|
| code-review | Comprehensive code review with quality, security, and style feedback |
| debug | Step-by-step debugging assistance |
| explain | Clear explanations of code functionality |
| refactor | Refactoring suggestions with clean code principles |
| planning | Task breakdown and implementation planning |
| security-review | Security vulnerability analysis |
Tracing & Logging
Monitor agent execution with structured logging:
import { Agent, ClaudeProvider, TracingManager } from '@compilr-dev/agents';
const tracing = new TracingManager({
enabled: true,
logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
destination: 'console', // or 'file' or custom function
filePath: './agent-logs.jsonl',
includeTimestamps: true,
redactPatterns: [/api[_-]?key/i, /password/i], // Redact sensitive data
});
const agent = new Agent({
provider: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY }),
tracing,
});
// Logs include:
// - LLM requests/responses
// - Tool calls and results
// - Errors and warnings
// - Timing informationOpenTelemetry Integration
import { createOtelTracing } from '@compilr-dev/agents';
// Export traces to your observability platform
const tracing = createOtelTracing({
serviceName: 'my-agent',
endpoint: 'http://localhost:4318/v1/traces',
});
const agent = new Agent({ provider, tracing });Examples
See the examples/ directory for runnable demos:
# Basic agent usage
npx tsx examples/basic-agent.ts
# Agent with custom tools
npx tsx examples/agent-with-tools.ts
# Streaming events
npx tsx examples/streaming-events.tsTesting
# Run tests
npm test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watchDesign Philosophy
- Minimal dependencies: Only
@anthropic-ai/sdkas optional peer dep - Type safety: Full TypeScript support with strict mode
- Extensibility: Easy to add new providers and tools
- Testability: MockProvider for easy unit testing
- Real-time feedback: Event system for CLI applications
License
MIT
