# @crewx/sdk

> CrewX SDK — facade for loading crewx.yaml and querying agents

Latest version **0.8.9** (published 2026-06-28) · UNLICENSED license · 0 weekly downloads

## Install

```sh
npm install @crewx/sdk
pnpm add @crewx/sdk
yarn add @crewx/sdk
bun add @crewx/sdk
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.8.9 |
| Published | 2026-06-28 |
| First published | 2026-03-30 |
| Weekly downloads | 0 |
| License | UNLICENSED |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=20.19.0 |
| Dependencies | 7 |
| Unpacked size | 1 MB |
| Known vulnerabilities | 0 (+6 in 2 direct dependencies) |
| Install scripts | no |
| Maintainers | dohapark81 |

## Links

- npm: https://www.npmjs.com/package/@crewx/sdk
- npm.io page: https://npm.io/package/@crewx/sdk

## Dependencies (7)

- [ai](https://npm.io/package/ai.md) 4.0.0
- [zod](https://npm.io/package/zod.md) 3.25.76
- [js-yaml](https://npm.io/package/js-yaml.md) 4.1.0
- [handlebars](https://npm.io/package/handlebars.md) 4.7.9
- [drizzle-orm](https://npm.io/package/drizzle-orm.md) 0.45.2
- [@agentclientprotocol/sdk](https://npm.io/package/@agentclientprotocol/sdk.md) 0.21.0
- [@modelcontextprotocol/sdk](https://npm.io/package/@modelcontextprotocol/sdk.md) 1.27.0

## Recent versions

- 0.8.9 (latest) — 2026-06-28
- 0.9.0-rc.112 (next) — 2026-09-21
- 0.9.0-rc.111 — 2026-09-20
- 0.9.0-rc.110 — 2026-09-19
- 0.9.0-rc.109 — 2026-09-18
- 0.9.0-rc.108 — 2026-09-17
- 0.9.0-rc.107 — 2026-09-17
- 0.9.0-rc.106 — 2026-09-16
- 0.9.0-rc.105 — 2026-09-16
- 0.9.0-rc.104 — 2026-09-16
- 0.9.0-rc.103 — 2026-09-15
- 0.9.0-rc.102 — 2026-09-14
- 0.9.0-rc.101 — 2026-09-14
- 0.9.0-rc.100 — 2026-09-14
- 0.9.0-rc.99 — 2026-09-14
- … 293 more at https://npm.io/package/@crewx/sdk/versions

## README

# @crewx/sdk

CrewX SDK — load `crewx.yaml` and run AI agents from TypeScript/JavaScript.

> **Status**: `0.9.0-alpha` — Core API, Plugin system, and Event system are stable. Tool system coming soon.

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Core API](#2-core-api)
3. [crewx.yaml Structure](#3-crewxyaml-structure)
4. [Layout System](#4-layout-system)
5. [Template Helpers](#5-template-helpers)
6. [Provider Bridge](#6-provider-bridge)
7. [Plugin System](#7-plugin-system)
8. [Event System](#8-event-system)
9. [Type Reference](#9-type-reference)

---

## 1. Quick Start

### Install

```bash
npm install @crewx/sdk
```

### Hello World

```typescript
import { Crewx } from '@crewx/sdk';

const crewx = await Crewx.loadYaml('./crewx.yaml');

const result = await crewx.query('assistant', 'Hello! What can you do?');

if (result.ok) {
  console.log(result.data);          // agent's response text
} else {
  console.error(result.error?.message);
}
```

### Minimal `crewx.yaml`

```yaml
agents:
  assistant:
    name: Assistant
    provider: cli/claude
    inline:
      model: claude-sonnet-4-6
      prompt: |
        You are a helpful assistant.
        Answer concisely.
```

### Task Execution (with side effects)

```typescript
const crewx = await Crewx.loadYaml('./crewx.yaml');

// execute() allows the agent to write files, run commands, etc.
const result = await crewx.execute('assistant', 'Refactor src/utils.ts');

console.log(`Done in ${result.meta.durationMs}ms`);
console.log(result.data);
```

### Plugin Setup (optional)

```typescript
import { FileLoggerPlugin } from '@crewx/cli/plugins/file-logger';
import { SqliteTracingPlugin } from '@crewx/cli/plugins/sqlite-tracing';

await crewx.use(new FileLoggerPlugin());    // log files → .crewx/logs/
await crewx.use(new SqliteTracingPlugin()); // task records → ~/.crewx/crewx.db
// ... run tasks ...
await crewx.close(); // flush all plugins on exit
```

---

## 2. Core API

### `Crewx.loadYaml(path, options?)`

Load from a `crewx.yaml` file. Documents referenced in the config are loaded automatically.

```typescript
const crewx = await Crewx.loadYaml('./crewx.yaml');

// With options
const crewx = await Crewx.loadYaml('./crewx.yaml', {
  execPolicy: { allow: ['git *', 'npm run *'], deny: ['rm *'] },
});
```

### `Crewx.fromConfig(config, options?, projectRoot?)`

Create from an already-parsed config object. Useful when you load YAML yourself or build config programmatically.

```typescript
import { Crewx, CrewxProjectConfig } from '@crewx/sdk';

const config: CrewxProjectConfig = {
  agents: [
    { id: 'bot', provider: 'cli/claude', inline: { prompt: 'You are a bot.' } },
  ],
};

const crewx = await Crewx.fromConfig(config, {}, process.cwd());
```

### `crewx.query(agentRef, message, options?)`

Ask an agent a question. Read-only — the agent is expected to respond with text only.

```typescript
const result = await crewx.query('assistant', 'Summarize this PR');

// With options
const result = await crewx.query('assistant', 'Translate to Korean', {
  model: 'claude-opus-4-6',      // override model
  provider: 'cli/claude',        // override provider
  context: 'Additional context', // prepended to the message
});
```

> **Note**: `'@assistant'` 형태도 동작합니다 (CLI 호환). SDK에서는 bare id가 권장됩니다.

**Returns**: [`QueryResult`](#queryresult)

### `crewx.execute(agentRef, message, options?)`

Run a task with an agent. The agent may write files, run shell commands, etc. Uses `--dangerously-skip-permissions` for `cli/claude`.

```typescript
const result = await crewx.execute('coder', 'Fix the failing tests in src/');

if (!result.ok) {
  console.error('Failed:', result.error?.code, result.error?.message);
}
```

**Returns**: [`ExecuteResult`](#executeresult)

### `crewx.renderAgentPromptFull(agentId, options?)`

Render the complete system prompt for an agent — layout + template expansion. Used to inspect what the agent actually receives, or to pass the prompt to another system.

```typescript
const prompt = await crewx.renderAgentPromptFull('assistant');
console.log(prompt);

// With layout override
const prompt = await crewx.renderAgentPromptFull('assistant', {
  layout: 'crewx/minimal',
  session: { mode: 'execute', platform: 'api' },
});
```

### `crewx.registerLayout(name, template)`

Register a custom layout at runtime. See [Layout System](#4-layout-system).

```typescript
crewx.registerLayout('my-layout', `
# {{agent.name}}
{{agent.inline.prompt}}
---
Session: {{session.mode}}
`);

const prompt = await crewx.renderAgentPromptFull('assistant', {
  layout: 'my-layout',
});
```

### `crewx.agents`

`ReadonlyMap<string, AgentConfig>` — all agents loaded from `crewx.yaml`.

```typescript
// List all agents
for (const [id, agent] of crewx.agents) {
  console.log(id, agent.provider);
}

// Check if an agent exists
if (crewx.agents.has('coder')) {
  // ...
}
```

### `crewx.filterAgents(filters)`

Filter agents by role, team, or provider. Supports glob patterns.

```typescript
// All agents on the 'backend' team
const agents = crewx.filterAgents({ team: 'backend' });

// All Claude-based agents
const agents = crewx.filterAgents({ provider: 'cli/claude' });

// Wildcard: all cli/* providers
const agents = crewx.filterAgents({ provider: 'cli/*' });
```

---

## 3. `crewx.yaml` Structure

```yaml
# ─── Agents ──────────────────────────────────────────────────
agents:
  my_agent:
    name: My Agent              # Display name (optional)
    role: Backend Developer     # Role label (optional)
    team: core                  # Team label (optional)
    provider: cli/claude        # Provider (required)
    working_directory: .        # Working dir for the agent

    inline:                     # Inline agent definition
      model: claude-sonnet-4-6  # Model override (optional)
      prompt: |                 # System prompt (Handlebars template)
        You are an expert {{agent.role}}.
        Today's context: {{{documents.guidelines.content}}}
      layout: crewx/minimal     # Layout override for this agent (optional)

# ─── Layouts ─────────────────────────────────────────────────
layouts:
  default: crewx/minimal        # Project-level default layout

# ─── Documents ───────────────────────────────────────────────
documents:
  guidelines:
    path: ./docs/guidelines.md  # Loaded at startup, available as documents.guidelines
  api_spec:
    path: ./docs/api.md
```

### Multiple providers

```yaml
agents:
  polyglot:
    provider:                   # Array = first is primary, rest are fallbacks (future)
      - cli/claude
      - cli/gemini
```

---

## 4. Layout System

Layouts wrap the agent's raw prompt with structure — identity blocks, session info, available tools, etc.

### Built-in Layouts

| ID | Description |
|----|-------------|
| `crewx/default` | Full structured layout (identity + session + prompt) |
| `crewx/minimal` | Minimal wrapper — just the agent prompt |

### Resolution Priority

When calling `renderAgentPromptFull()`, the layout is resolved in this order (first match wins):

1. `options.layout` — call-site override
2. `agent.inline.layout` — per-agent definition in `crewx.yaml`
3. `config.layouts.default` — project-level default
4. `crewx/default` — SDK built-in fallback

### Custom Layout via `registerLayout()`

```typescript
crewx.registerLayout('compact', `
## {{agent.name}} ({{agent.role}})
{{agent.inline.prompt}}
`);

const prompt = await crewx.renderAgentPromptFull('assistant', {
  layout: 'compact',
});
```

### Inline Layout in `crewx.yaml`

```yaml
agents:
  assistant:
    provider: cli/claude
    inline:
      prompt: You are helpful.
      layout:
        id: crewx/default
        props:
          show_skills: false     # Pass props to the layout template
```

### Inline Template String

```typescript
const prompt = await crewx.renderAgentPromptFull('assistant', {
  layout: { template: 'SYSTEM: {{agent.inline.prompt}}' },
});
```

---

## 5. Template Helpers

The `inline.prompt` field in `crewx.yaml` is a **Handlebars template**. These helpers are available:

### P0 Helpers (Core)

| Helper | Usage | Description |
|--------|-------|-------------|
| `exec` | `{{exec "git log --oneline -5"}}` | Run shell command and inline output |
| `include` | `{{include someVar}}` | Include a string variable as-is (no escaping) |
| `fenced_code` | `{{fenced_code content lang="ts"}}` | Wrap content in Markdown code block |

### Condition Helpers

```handlebars
{{#if (eq agent.team "backend")}}Backend mode{{/if}}
{{#if (and featureA featureB)}}Both enabled{{/if}}
```

Available: `eq`, `ne`, `and`, `or`, `not`, `contains`

### Utility Helpers

| Helper | Description |
|--------|-------------|
| `truncate text len` | Truncate string to N chars |
| `length array` | Array/string length |
| `escapeHandlebars text` | Escape `{{` in content |
| `formatFileSize bytes` | `1048576` → `1 MB` |
| `formatTimestamp ms` | Unix ms → readable date |

### Document Access in Templates

Documents defined in `crewx.yaml` are automatically available:

```handlebars
# Inline the full document
{{{documents.guidelines.content}}}

# Access metadata
Path: {{documents.guidelines.path}}
```

### `exec` Security Policy

Control which shell commands are allowed in templates:

```typescript
const crewx = await Crewx.loadYaml('./crewx.yaml', {
  execPolicy: {
    allow: ['git *', 'npm run *', 'cat *'],
    deny:  ['rm *', 'curl *'],
  },
});
```

Or set it in `crewx.yaml`:

```yaml
settings:
  template:
    exec:
      allow:
        - "git *"
        - "npm run *"
      deny:
        - "rm *"
```

Glob syntax: `*` matches within a segment, `**` matches across path segments.

---

## 6. Provider Bridge

Each agent has a `provider` field that tells the SDK which AI backend to use.

### Supported Providers

| Provider | CLI Command | Notes |
|----------|-------------|-------|
| `cli/claude` | `claude` | Claude Code CLI |
| `cli/gemini` | `gemini` | Gemini CLI |
| `cli/copilot` | `gh copilot suggest` | GitHub Copilot |
| `cli/codex` | `codex` | OpenAI Codex CLI |

> **Coming Soon**: `api/claude`, `api/openai` — direct API providers without CLI dependency.

### Provider Override at Call Site

```typescript
// Use a different provider for one call
const result = await crewx.query('assistant', 'Hello', {
  provider: 'cli/gemini',
  model: 'gemini-2.0-flash',
});
```

### `query` vs `execute` Mode

| | `query()` | `execute()` |
|--|-----------|-------------|
| Intent | Read-only Q&A | Task with side effects |
| cli/claude flags | `-p --output-format stream-json --verbose` | + `--dangerously-skip-permissions` |
| Use when | Asking questions, generating text | Writing files, running commands |

---

## 7. Plugin System

Plugins extend `CrewxPlugin` from `@crewx/sdk` and attach event listeners in `attach()`. Register with `crewx.use(plugin)` and release resources by calling `crewx.close()`.

### Lifecycle

```typescript
await crewx.use(plugin);   // calls plugin.attach(crewx) — subscribe events, open DB, etc.
await crewx.close();       // calls plugin.detach(crewx) on all plugins in LIFO order
```

Same plugin instance registered twice is silently ignored.

### Built-in Plugins (`@crewx/cli`)

| Plugin | Import path | Storage |
|--------|-------------|---------|
| `FileLoggerPlugin` | `@crewx/cli/plugins/file-logger` | `.crewx/logs/{ts}_{traceId}.log` (one file per task) |
| `SqliteTracingPlugin` | `@crewx/cli/plugins/sqlite-tracing` | `~/.crewx/crewx.db` — `tasks` table |

> The `crewx` CLI auto-attaches both plugins for every run. No setup needed when using the CLI directly.

### Custom Plugin

```typescript
import { CrewxPlugin } from '@crewx/sdk';
import type { Crewx } from '@crewx/sdk';

class CostTrackerPlugin extends CrewxPlugin {
  readonly name = 'cost-tracker';

  attach(crewx: Crewx) {
    crewx.on('task:end', (e) => {
      if (e.costUsd) console.log(`[cost] ${e.agentRef}: $${e.costUsd.toFixed(4)}`);
    });
  }
  // detach() is optional — base class no-op is sufficient if no cleanup needed
}

await crewx.use(new CostTrackerPlugin());
```

---

## 8. Event System

The `Crewx` class extends `TypedEventEmitter`. Events are emitted automatically during `query()` and `execute()`.

### Event Catalog

| Event | When | Key Payload Fields |
|-------|------|--------------------|
| `task:start` | `query()`/`execute()` begins | `traceId`, `agentRef`, `mode`, `pid`, `model`, `provider`, `message`, `timestamp` |
| `task:end` | Call completes (success or failure) | `traceId`, `agentRef`, `durationMs`, `result`, `error`, `inputTokens`, `outputTokens`, `costUsd`, `model` |
| `task:output` | Each line of provider output | `traceId`, `agentRef`, `output`, `level` (`stdout`\|`stderr`\|`info`) |

All events share `traceId` (format: `tsk_XXXXXXXX`) and `timestamp` inherited from `BaseEvent`.

### `crewx.on(event, listener)` → `UnsubscribeFn`

```typescript
import type { TaskEndEvent } from '@crewx/sdk';

const unsub = crewx.on('task:end', (e: TaskEndEvent) => {
  console.log(`${e.agentRef} finished in ${e.durationMs}ms | tokens: ${e.inputTokens}+${e.outputTokens}`);
});

// Remove listener when no longer needed
unsub();
```

`crewx.once(event, listener)` fires exactly once and auto-unsubscribes.

> **Best practice**: Subscribe inside `Plugin.attach()` so listeners are automatically removed by `crewx.close()`. Direct `crewx.on()` calls are fine for one-off use but require manual `unsub()` calls.

---

## 9. Type Reference

### `CrewxOptions`

```typescript
interface CrewxOptions {
  workspaceRoot?: string;
  platform?: 'cli' | 'slack' | 'api';
  execPolicy?: ExecPolicy;        // Allowed/denied shell commands in {{exec}}
}
```

### `QueryOptions` / `ExecuteOptions`

```typescript
interface QueryOptions {
  model?: string;                 // Override model (e.g. 'claude-opus-4-6')
  provider?: string;              // Override provider (e.g. 'cli/gemini')
  context?: string;               // Extra context prepended to message
  metadata?: Record<string, unknown>;
}
// ExecuteOptions has the same shape
```

### `QueryResult` / `ExecuteResult`

```typescript
interface QueryResult {
  ok: boolean;
  data: string;                   // Agent's response text
  error?: {
    code: string;                 // 'AGENT_NOT_FOUND' | 'PROVIDER_ERROR' | 'QUERY_FAILED'
    message: string;
  };
  meta: {
    agentId: string;
    provider: string;
    model?: string;
    durationMs: number;
  };
}
// ExecuteResult has the same shape
```

### `AgentConfig`

```typescript
interface AgentConfig {
  id: string;
  name?: string;
  role?: string;
  team?: string;
  provider: string | string[];
  working_directory?: string;
  description?: string;
  inline?: {
    model?: string;
    system_prompt?: string;
    prompt?: string;
    layout?: string | { id: string; props?: Record<string, unknown> };
  };
}
```

### `ExecPolicy`

```typescript
interface ExecPolicy {
  allow: string[];   // Glob patterns for allowed commands
  deny: string[];    // Glob patterns for denied commands (takes precedence)
}
```

---

## Coming Soon

- **Tool System** — attach custom tools/functions to agents (§3 of design spec)
- **`api/*` Providers** — direct API calls without CLI dependency

---

## Requirements

- Node.js >= 20.19.0
- At least one CLI provider installed (e.g. `npm i -g @anthropic-ai/claude-code`)

---
_Source: https://npm.io/package/@crewx/sdk · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
