@crewx/sdk 0.8.1 → 0.8.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +584 -584
- package/dist/esm/plugins/index.js +11 -11
- package/dist/plugins/index.js +11 -11
- package/package.json +1 -1
- package/src/schemas/hooks.schema.json +59 -59
- package/templates/agents/default.yaml +500 -500
- package/templates/agents/minimal.yaml +16 -16
- package/templates/documents/crewx-manual.md +2278 -2278
- package/templates/documents/crewx-quick-guide.md +147 -147
package/README.md
CHANGED
|
@@ -1,584 +1,584 @@
|
|
|
1
|
-
# @crewx/sdk
|
|
2
|
-
|
|
3
|
-
CrewX SDK — load `crewx.yaml` and run AI agents from TypeScript/JavaScript.
|
|
4
|
-
|
|
5
|
-
> **Status**: `0.9.0-alpha` — Core API, Plugin system, and Event system are stable. Tool system coming soon.
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
|
|
9
|
-
1. [Quick Start](#1-quick-start)
|
|
10
|
-
2. [Core API](#2-core-api)
|
|
11
|
-
3. [crewx.yaml Structure](#3-crewxyaml-structure)
|
|
12
|
-
4. [Layout System](#4-layout-system)
|
|
13
|
-
5. [Template Helpers](#5-template-helpers)
|
|
14
|
-
6. [Provider Bridge](#6-provider-bridge)
|
|
15
|
-
7. [Plugin System](#7-plugin-system)
|
|
16
|
-
8. [Event System](#8-event-system)
|
|
17
|
-
9. [Type Reference](#9-type-reference)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 1. Quick Start
|
|
22
|
-
|
|
23
|
-
### Install
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install @crewx/sdk
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Hello World
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { Crewx } from '@crewx/sdk';
|
|
33
|
-
|
|
34
|
-
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
35
|
-
|
|
36
|
-
const result = await crewx.query('assistant', 'Hello! What can you do?');
|
|
37
|
-
|
|
38
|
-
if (result.ok) {
|
|
39
|
-
console.log(result.data); // agent's response text
|
|
40
|
-
} else {
|
|
41
|
-
console.error(result.error?.message);
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Minimal `crewx.yaml`
|
|
46
|
-
|
|
47
|
-
```yaml
|
|
48
|
-
agents:
|
|
49
|
-
assistant:
|
|
50
|
-
name: Assistant
|
|
51
|
-
provider: cli/claude
|
|
52
|
-
inline:
|
|
53
|
-
model: claude-sonnet-4-6
|
|
54
|
-
prompt: |
|
|
55
|
-
You are a helpful assistant.
|
|
56
|
-
Answer concisely.
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Task Execution (with side effects)
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
63
|
-
|
|
64
|
-
// execute() allows the agent to write files, run commands, etc.
|
|
65
|
-
const result = await crewx.execute('assistant', 'Refactor src/utils.ts');
|
|
66
|
-
|
|
67
|
-
console.log(`Done in ${result.meta.durationMs}ms`);
|
|
68
|
-
console.log(result.data);
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Plugin Setup (optional)
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { FileLoggerPlugin } from '@crewx/cli/plugins/file-logger';
|
|
75
|
-
import { SqliteTracingPlugin } from '@crewx/cli/plugins/sqlite-tracing';
|
|
76
|
-
|
|
77
|
-
await crewx.use(new FileLoggerPlugin()); // log files → .crewx/logs/
|
|
78
|
-
await crewx.use(new SqliteTracingPlugin()); // task records → ~/.crewx/crewx.db
|
|
79
|
-
// ... run tasks ...
|
|
80
|
-
await crewx.close(); // flush all plugins on exit
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 2. Core API
|
|
86
|
-
|
|
87
|
-
### `Crewx.loadYaml(path, options?)`
|
|
88
|
-
|
|
89
|
-
Load from a `crewx.yaml` file. Documents referenced in the config are loaded automatically.
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
93
|
-
|
|
94
|
-
// With options
|
|
95
|
-
const crewx = await Crewx.loadYaml('./crewx.yaml', {
|
|
96
|
-
execPolicy: { allow: ['git *', 'npm run *'], deny: ['rm *'] },
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### `Crewx.fromConfig(config, options?, projectRoot?)`
|
|
101
|
-
|
|
102
|
-
Create from an already-parsed config object. Useful when you load YAML yourself or build config programmatically.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
import { Crewx, CrewxProjectConfig } from '@crewx/sdk';
|
|
106
|
-
|
|
107
|
-
const config: CrewxProjectConfig = {
|
|
108
|
-
agents: [
|
|
109
|
-
{ id: 'bot', provider: 'cli/claude', inline: { prompt: 'You are a bot.' } },
|
|
110
|
-
],
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const crewx = await Crewx.fromConfig(config, {}, process.cwd());
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### `crewx.query(agentRef, message, options?)`
|
|
117
|
-
|
|
118
|
-
Ask an agent a question. Read-only — the agent is expected to respond with text only.
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
const result = await crewx.query('assistant', 'Summarize this PR');
|
|
122
|
-
|
|
123
|
-
// With options
|
|
124
|
-
const result = await crewx.query('assistant', 'Translate to Korean', {
|
|
125
|
-
model: 'claude-opus-4-6', // override model
|
|
126
|
-
provider: 'cli/claude', // override provider
|
|
127
|
-
context: 'Additional context', // prepended to the message
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
> **Note**: `'@assistant'` 형태도 동작합니다 (CLI 호환). SDK에서는 bare id가 권장됩니다.
|
|
132
|
-
|
|
133
|
-
**Returns**: [`QueryResult`](#queryresult)
|
|
134
|
-
|
|
135
|
-
### `crewx.execute(agentRef, message, options?)`
|
|
136
|
-
|
|
137
|
-
Run a task with an agent. The agent may write files, run shell commands, etc. Uses `--dangerously-skip-permissions` for `cli/claude`.
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
const result = await crewx.execute('coder', 'Fix the failing tests in src/');
|
|
141
|
-
|
|
142
|
-
if (!result.ok) {
|
|
143
|
-
console.error('Failed:', result.error?.code, result.error?.message);
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**Returns**: [`ExecuteResult`](#executeresult)
|
|
148
|
-
|
|
149
|
-
### `crewx.renderAgentPromptFull(agentId, options?)`
|
|
150
|
-
|
|
151
|
-
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.
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
const prompt = await crewx.renderAgentPromptFull('assistant');
|
|
155
|
-
console.log(prompt);
|
|
156
|
-
|
|
157
|
-
// With layout override
|
|
158
|
-
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
159
|
-
layout: 'crewx/minimal',
|
|
160
|
-
session: { mode: 'execute', platform: 'api' },
|
|
161
|
-
});
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### `crewx.registerLayout(name, template)`
|
|
165
|
-
|
|
166
|
-
Register a custom layout at runtime. See [Layout System](#4-layout-system).
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
crewx.registerLayout('my-layout', `
|
|
170
|
-
# {{agent.name}}
|
|
171
|
-
{{agent.inline.prompt}}
|
|
172
|
-
---
|
|
173
|
-
Session: {{session.mode}}
|
|
174
|
-
`);
|
|
175
|
-
|
|
176
|
-
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
177
|
-
layout: 'my-layout',
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### `crewx.agents`
|
|
182
|
-
|
|
183
|
-
`ReadonlyMap<string, AgentConfig>` — all agents loaded from `crewx.yaml`.
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
// List all agents
|
|
187
|
-
for (const [id, agent] of crewx.agents) {
|
|
188
|
-
console.log(id, agent.provider);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Check if an agent exists
|
|
192
|
-
if (crewx.agents.has('coder')) {
|
|
193
|
-
// ...
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### `crewx.filterAgents(filters)`
|
|
198
|
-
|
|
199
|
-
Filter agents by role, team, or provider. Supports glob patterns.
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// All agents on the 'backend' team
|
|
203
|
-
const agents = crewx.filterAgents({ team: 'backend' });
|
|
204
|
-
|
|
205
|
-
// All Claude-based agents
|
|
206
|
-
const agents = crewx.filterAgents({ provider: 'cli/claude' });
|
|
207
|
-
|
|
208
|
-
// Wildcard: all cli/* providers
|
|
209
|
-
const agents = crewx.filterAgents({ provider: 'cli/*' });
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## 3. `crewx.yaml` Structure
|
|
215
|
-
|
|
216
|
-
```yaml
|
|
217
|
-
# ─── Agents ──────────────────────────────────────────────────
|
|
218
|
-
agents:
|
|
219
|
-
my_agent:
|
|
220
|
-
name: My Agent # Display name (optional)
|
|
221
|
-
role: Backend Developer # Role label (optional)
|
|
222
|
-
team: core # Team label (optional)
|
|
223
|
-
provider: cli/claude # Provider (required)
|
|
224
|
-
working_directory: . # Working dir for the agent
|
|
225
|
-
|
|
226
|
-
inline: # Inline agent definition
|
|
227
|
-
model: claude-sonnet-4-6 # Model override (optional)
|
|
228
|
-
prompt: | # System prompt (Handlebars template)
|
|
229
|
-
You are an expert {{agent.role}}.
|
|
230
|
-
Today's context: {{{documents.guidelines.content}}}
|
|
231
|
-
layout: crewx/minimal # Layout override for this agent (optional)
|
|
232
|
-
|
|
233
|
-
# ─── Layouts ─────────────────────────────────────────────────
|
|
234
|
-
layouts:
|
|
235
|
-
default: crewx/minimal # Project-level default layout
|
|
236
|
-
|
|
237
|
-
# ─── Documents ───────────────────────────────────────────────
|
|
238
|
-
documents:
|
|
239
|
-
guidelines:
|
|
240
|
-
path: ./docs/guidelines.md # Loaded at startup, available as documents.guidelines
|
|
241
|
-
api_spec:
|
|
242
|
-
path: ./docs/api.md
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Multiple providers
|
|
246
|
-
|
|
247
|
-
```yaml
|
|
248
|
-
agents:
|
|
249
|
-
polyglot:
|
|
250
|
-
provider: # Array = first is primary, rest are fallbacks (future)
|
|
251
|
-
- cli/claude
|
|
252
|
-
- cli/gemini
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## 4. Layout System
|
|
258
|
-
|
|
259
|
-
Layouts wrap the agent's raw prompt with structure — identity blocks, session info, available tools, etc.
|
|
260
|
-
|
|
261
|
-
### Built-in Layouts
|
|
262
|
-
|
|
263
|
-
| ID | Description |
|
|
264
|
-
|----|-------------|
|
|
265
|
-
| `crewx/default` | Full structured layout (identity + session + prompt) |
|
|
266
|
-
| `crewx/minimal` | Minimal wrapper — just the agent prompt |
|
|
267
|
-
|
|
268
|
-
### Resolution Priority
|
|
269
|
-
|
|
270
|
-
When calling `renderAgentPromptFull()`, the layout is resolved in this order (first match wins):
|
|
271
|
-
|
|
272
|
-
1. `options.layout` — call-site override
|
|
273
|
-
2. `agent.inline.layout` — per-agent definition in `crewx.yaml`
|
|
274
|
-
3. `config.layouts.default` — project-level default
|
|
275
|
-
4. `crewx/default` — SDK built-in fallback
|
|
276
|
-
|
|
277
|
-
### Custom Layout via `registerLayout()`
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
crewx.registerLayout('compact', `
|
|
281
|
-
## {{agent.name}} ({{agent.role}})
|
|
282
|
-
{{agent.inline.prompt}}
|
|
283
|
-
`);
|
|
284
|
-
|
|
285
|
-
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
286
|
-
layout: 'compact',
|
|
287
|
-
});
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Inline Layout in `crewx.yaml`
|
|
291
|
-
|
|
292
|
-
```yaml
|
|
293
|
-
agents:
|
|
294
|
-
assistant:
|
|
295
|
-
provider: cli/claude
|
|
296
|
-
inline:
|
|
297
|
-
prompt: You are helpful.
|
|
298
|
-
layout:
|
|
299
|
-
id: crewx/default
|
|
300
|
-
props:
|
|
301
|
-
show_skills: false # Pass props to the layout template
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Inline Template String
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
308
|
-
layout: { template: 'SYSTEM: {{agent.inline.prompt}}' },
|
|
309
|
-
});
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## 5. Template Helpers
|
|
315
|
-
|
|
316
|
-
The `inline.prompt` field in `crewx.yaml` is a **Handlebars template**. These helpers are available:
|
|
317
|
-
|
|
318
|
-
### P0 Helpers (Core)
|
|
319
|
-
|
|
320
|
-
| Helper | Usage | Description |
|
|
321
|
-
|--------|-------|-------------|
|
|
322
|
-
| `exec` | `{{exec "git log --oneline -5"}}` | Run shell command and inline output |
|
|
323
|
-
| `include` | `{{include someVar}}` | Include a string variable as-is (no escaping) |
|
|
324
|
-
| `fenced_code` | `{{fenced_code content lang="ts"}}` | Wrap content in Markdown code block |
|
|
325
|
-
|
|
326
|
-
### Condition Helpers
|
|
327
|
-
|
|
328
|
-
```handlebars
|
|
329
|
-
{{#if (eq agent.team "backend")}}Backend mode{{/if}}
|
|
330
|
-
{{#if (and featureA featureB)}}Both enabled{{/if}}
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
Available: `eq`, `ne`, `and`, `or`, `not`, `contains`
|
|
334
|
-
|
|
335
|
-
### Utility Helpers
|
|
336
|
-
|
|
337
|
-
| Helper | Description |
|
|
338
|
-
|--------|-------------|
|
|
339
|
-
| `truncate text len` | Truncate string to N chars |
|
|
340
|
-
| `length array` | Array/string length |
|
|
341
|
-
| `escapeHandlebars text` | Escape `{{` in content |
|
|
342
|
-
| `formatFileSize bytes` | `1048576` → `1 MB` |
|
|
343
|
-
| `formatTimestamp ms` | Unix ms → readable date |
|
|
344
|
-
|
|
345
|
-
### Document Access in Templates
|
|
346
|
-
|
|
347
|
-
Documents defined in `crewx.yaml` are automatically available:
|
|
348
|
-
|
|
349
|
-
```handlebars
|
|
350
|
-
# Inline the full document
|
|
351
|
-
{{{documents.guidelines.content}}}
|
|
352
|
-
|
|
353
|
-
# Access metadata
|
|
354
|
-
Path: {{documents.guidelines.path}}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### `exec` Security Policy
|
|
358
|
-
|
|
359
|
-
Control which shell commands are allowed in templates:
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
const crewx = await Crewx.loadYaml('./crewx.yaml', {
|
|
363
|
-
execPolicy: {
|
|
364
|
-
allow: ['git *', 'npm run *', 'cat *'],
|
|
365
|
-
deny: ['rm *', 'curl *'],
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
Or set it in `crewx.yaml`:
|
|
371
|
-
|
|
372
|
-
```yaml
|
|
373
|
-
settings:
|
|
374
|
-
template:
|
|
375
|
-
exec:
|
|
376
|
-
allow:
|
|
377
|
-
- "git *"
|
|
378
|
-
- "npm run *"
|
|
379
|
-
deny:
|
|
380
|
-
- "rm *"
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
Glob syntax: `*` matches within a segment, `**` matches across path segments.
|
|
384
|
-
|
|
385
|
-
---
|
|
386
|
-
|
|
387
|
-
## 6. Provider Bridge
|
|
388
|
-
|
|
389
|
-
Each agent has a `provider` field that tells the SDK which AI backend to use.
|
|
390
|
-
|
|
391
|
-
### Supported Providers
|
|
392
|
-
|
|
393
|
-
| Provider | CLI Command | Notes |
|
|
394
|
-
|----------|-------------|-------|
|
|
395
|
-
| `cli/claude` | `claude` | Claude Code CLI |
|
|
396
|
-
| `cli/gemini` | `gemini` | Gemini CLI |
|
|
397
|
-
| `cli/copilot` | `gh copilot suggest` | GitHub Copilot |
|
|
398
|
-
| `cli/codex` | `codex` | OpenAI Codex CLI |
|
|
399
|
-
|
|
400
|
-
> **Coming Soon**: `api/claude`, `api/openai` — direct API providers without CLI dependency.
|
|
401
|
-
|
|
402
|
-
### Provider Override at Call Site
|
|
403
|
-
|
|
404
|
-
```typescript
|
|
405
|
-
// Use a different provider for one call
|
|
406
|
-
const result = await crewx.query('assistant', 'Hello', {
|
|
407
|
-
provider: 'cli/gemini',
|
|
408
|
-
model: 'gemini-2.0-flash',
|
|
409
|
-
});
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
### `query` vs `execute` Mode
|
|
413
|
-
|
|
414
|
-
| | `query()` | `execute()` |
|
|
415
|
-
|--|-----------|-------------|
|
|
416
|
-
| Intent | Read-only Q&A | Task with side effects |
|
|
417
|
-
| cli/claude flags | `-p --output-format stream-json --verbose` | + `--dangerously-skip-permissions` |
|
|
418
|
-
| Use when | Asking questions, generating text | Writing files, running commands |
|
|
419
|
-
|
|
420
|
-
---
|
|
421
|
-
|
|
422
|
-
## 7. Plugin System
|
|
423
|
-
|
|
424
|
-
Plugins extend `CrewxPlugin` from `@crewx/sdk` and attach event listeners in `attach()`. Register with `crewx.use(plugin)` and release resources by calling `crewx.close()`.
|
|
425
|
-
|
|
426
|
-
### Lifecycle
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
await crewx.use(plugin); // calls plugin.attach(crewx) — subscribe events, open DB, etc.
|
|
430
|
-
await crewx.close(); // calls plugin.detach(crewx) on all plugins in LIFO order
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
Same plugin instance registered twice is silently ignored.
|
|
434
|
-
|
|
435
|
-
### Built-in Plugins (`@crewx/cli`)
|
|
436
|
-
|
|
437
|
-
| Plugin | Import path | Storage |
|
|
438
|
-
|--------|-------------|---------|
|
|
439
|
-
| `FileLoggerPlugin` | `@crewx/cli/plugins/file-logger` | `.crewx/logs/{ts}_{traceId}.log` (one file per task) |
|
|
440
|
-
| `SqliteTracingPlugin` | `@crewx/cli/plugins/sqlite-tracing` | `~/.crewx/crewx.db` — `tasks` table |
|
|
441
|
-
|
|
442
|
-
> The `crewx` CLI auto-attaches both plugins for every run. No setup needed when using the CLI directly.
|
|
443
|
-
|
|
444
|
-
### Custom Plugin
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
import { CrewxPlugin } from '@crewx/sdk';
|
|
448
|
-
import type { Crewx } from '@crewx/sdk';
|
|
449
|
-
|
|
450
|
-
class CostTrackerPlugin extends CrewxPlugin {
|
|
451
|
-
readonly name = 'cost-tracker';
|
|
452
|
-
|
|
453
|
-
attach(crewx: Crewx) {
|
|
454
|
-
crewx.on('task:end', (e) => {
|
|
455
|
-
if (e.costUsd) console.log(`[cost] ${e.agentRef}: $${e.costUsd.toFixed(4)}`);
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
// detach() is optional — base class no-op is sufficient if no cleanup needed
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
await crewx.use(new CostTrackerPlugin());
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
---
|
|
465
|
-
|
|
466
|
-
## 8. Event System
|
|
467
|
-
|
|
468
|
-
The `Crewx` class extends `TypedEventEmitter`. Events are emitted automatically during `query()` and `execute()`.
|
|
469
|
-
|
|
470
|
-
### Event Catalog
|
|
471
|
-
|
|
472
|
-
| Event | When | Key Payload Fields |
|
|
473
|
-
|-------|------|--------------------|
|
|
474
|
-
| `task:start` | `query()`/`execute()` begins | `traceId`, `agentRef`, `mode`, `pid`, `model`, `provider`, `message`, `timestamp` |
|
|
475
|
-
| `task:end` | Call completes (success or failure) | `traceId`, `agentRef`, `durationMs`, `result`, `error`, `inputTokens`, `outputTokens`, `costUsd`, `model` |
|
|
476
|
-
| `task:output` | Each line of provider output | `traceId`, `agentRef`, `output`, `level` (`stdout`\|`stderr`\|`info`) |
|
|
477
|
-
|
|
478
|
-
All events share `traceId` (format: `tsk_XXXXXXXX`) and `timestamp` inherited from `BaseEvent`.
|
|
479
|
-
|
|
480
|
-
### `crewx.on(event, listener)` → `UnsubscribeFn`
|
|
481
|
-
|
|
482
|
-
```typescript
|
|
483
|
-
import type { TaskEndEvent } from '@crewx/sdk';
|
|
484
|
-
|
|
485
|
-
const unsub = crewx.on('task:end', (e: TaskEndEvent) => {
|
|
486
|
-
console.log(`${e.agentRef} finished in ${e.durationMs}ms | tokens: ${e.inputTokens}+${e.outputTokens}`);
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
// Remove listener when no longer needed
|
|
490
|
-
unsub();
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
`crewx.once(event, listener)` fires exactly once and auto-unsubscribes.
|
|
494
|
-
|
|
495
|
-
> **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.
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
## 9. Type Reference
|
|
500
|
-
|
|
501
|
-
### `CrewxOptions`
|
|
502
|
-
|
|
503
|
-
```typescript
|
|
504
|
-
interface CrewxOptions {
|
|
505
|
-
workspaceRoot?: string;
|
|
506
|
-
platform?: 'cli' | 'slack' | 'api';
|
|
507
|
-
execPolicy?: ExecPolicy; // Allowed/denied shell commands in {{exec}}
|
|
508
|
-
}
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### `QueryOptions` / `ExecuteOptions`
|
|
512
|
-
|
|
513
|
-
```typescript
|
|
514
|
-
interface QueryOptions {
|
|
515
|
-
model?: string; // Override model (e.g. 'claude-opus-4-6')
|
|
516
|
-
provider?: string; // Override provider (e.g. 'cli/gemini')
|
|
517
|
-
context?: string; // Extra context prepended to message
|
|
518
|
-
metadata?: Record<string, unknown>;
|
|
519
|
-
}
|
|
520
|
-
// ExecuteOptions has the same shape
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### `QueryResult` / `ExecuteResult`
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
interface QueryResult {
|
|
527
|
-
ok: boolean;
|
|
528
|
-
data: string; // Agent's response text
|
|
529
|
-
error?: {
|
|
530
|
-
code: string; // 'AGENT_NOT_FOUND' | 'PROVIDER_ERROR' | 'QUERY_FAILED'
|
|
531
|
-
message: string;
|
|
532
|
-
};
|
|
533
|
-
meta: {
|
|
534
|
-
agentId: string;
|
|
535
|
-
provider: string;
|
|
536
|
-
model?: string;
|
|
537
|
-
durationMs: number;
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
// ExecuteResult has the same shape
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
### `AgentConfig`
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
interface AgentConfig {
|
|
547
|
-
id: string;
|
|
548
|
-
name?: string;
|
|
549
|
-
role?: string;
|
|
550
|
-
team?: string;
|
|
551
|
-
provider: string | string[];
|
|
552
|
-
working_directory?: string;
|
|
553
|
-
description?: string;
|
|
554
|
-
inline?: {
|
|
555
|
-
model?: string;
|
|
556
|
-
system_prompt?: string;
|
|
557
|
-
prompt?: string;
|
|
558
|
-
layout?: string | { id: string; props?: Record<string, unknown> };
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### `ExecPolicy`
|
|
564
|
-
|
|
565
|
-
```typescript
|
|
566
|
-
interface ExecPolicy {
|
|
567
|
-
allow: string[]; // Glob patterns for allowed commands
|
|
568
|
-
deny: string[]; // Glob patterns for denied commands (takes precedence)
|
|
569
|
-
}
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
---
|
|
573
|
-
|
|
574
|
-
## Coming Soon
|
|
575
|
-
|
|
576
|
-
- **Tool System** — attach custom tools/functions to agents (§3 of design spec)
|
|
577
|
-
- **`api/*` Providers** — direct API calls without CLI dependency
|
|
578
|
-
|
|
579
|
-
---
|
|
580
|
-
|
|
581
|
-
## Requirements
|
|
582
|
-
|
|
583
|
-
- Node.js >= 20.19.0
|
|
584
|
-
- At least one CLI provider installed (e.g. `npm i -g @anthropic-ai/claude-code`)
|
|
1
|
+
# @crewx/sdk
|
|
2
|
+
|
|
3
|
+
CrewX SDK — load `crewx.yaml` and run AI agents from TypeScript/JavaScript.
|
|
4
|
+
|
|
5
|
+
> **Status**: `0.9.0-alpha` — Core API, Plugin system, and Event system are stable. Tool system coming soon.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Quick Start](#1-quick-start)
|
|
10
|
+
2. [Core API](#2-core-api)
|
|
11
|
+
3. [crewx.yaml Structure](#3-crewxyaml-structure)
|
|
12
|
+
4. [Layout System](#4-layout-system)
|
|
13
|
+
5. [Template Helpers](#5-template-helpers)
|
|
14
|
+
6. [Provider Bridge](#6-provider-bridge)
|
|
15
|
+
7. [Plugin System](#7-plugin-system)
|
|
16
|
+
8. [Event System](#8-event-system)
|
|
17
|
+
9. [Type Reference](#9-type-reference)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Quick Start
|
|
22
|
+
|
|
23
|
+
### Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @crewx/sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Hello World
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Crewx } from '@crewx/sdk';
|
|
33
|
+
|
|
34
|
+
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
35
|
+
|
|
36
|
+
const result = await crewx.query('assistant', 'Hello! What can you do?');
|
|
37
|
+
|
|
38
|
+
if (result.ok) {
|
|
39
|
+
console.log(result.data); // agent's response text
|
|
40
|
+
} else {
|
|
41
|
+
console.error(result.error?.message);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Minimal `crewx.yaml`
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
agents:
|
|
49
|
+
assistant:
|
|
50
|
+
name: Assistant
|
|
51
|
+
provider: cli/claude
|
|
52
|
+
inline:
|
|
53
|
+
model: claude-sonnet-4-6
|
|
54
|
+
prompt: |
|
|
55
|
+
You are a helpful assistant.
|
|
56
|
+
Answer concisely.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Task Execution (with side effects)
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
63
|
+
|
|
64
|
+
// execute() allows the agent to write files, run commands, etc.
|
|
65
|
+
const result = await crewx.execute('assistant', 'Refactor src/utils.ts');
|
|
66
|
+
|
|
67
|
+
console.log(`Done in ${result.meta.durationMs}ms`);
|
|
68
|
+
console.log(result.data);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Plugin Setup (optional)
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { FileLoggerPlugin } from '@crewx/cli/plugins/file-logger';
|
|
75
|
+
import { SqliteTracingPlugin } from '@crewx/cli/plugins/sqlite-tracing';
|
|
76
|
+
|
|
77
|
+
await crewx.use(new FileLoggerPlugin()); // log files → .crewx/logs/
|
|
78
|
+
await crewx.use(new SqliteTracingPlugin()); // task records → ~/.crewx/crewx.db
|
|
79
|
+
// ... run tasks ...
|
|
80
|
+
await crewx.close(); // flush all plugins on exit
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 2. Core API
|
|
86
|
+
|
|
87
|
+
### `Crewx.loadYaml(path, options?)`
|
|
88
|
+
|
|
89
|
+
Load from a `crewx.yaml` file. Documents referenced in the config are loaded automatically.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const crewx = await Crewx.loadYaml('./crewx.yaml');
|
|
93
|
+
|
|
94
|
+
// With options
|
|
95
|
+
const crewx = await Crewx.loadYaml('./crewx.yaml', {
|
|
96
|
+
execPolicy: { allow: ['git *', 'npm run *'], deny: ['rm *'] },
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `Crewx.fromConfig(config, options?, projectRoot?)`
|
|
101
|
+
|
|
102
|
+
Create from an already-parsed config object. Useful when you load YAML yourself or build config programmatically.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { Crewx, CrewxProjectConfig } from '@crewx/sdk';
|
|
106
|
+
|
|
107
|
+
const config: CrewxProjectConfig = {
|
|
108
|
+
agents: [
|
|
109
|
+
{ id: 'bot', provider: 'cli/claude', inline: { prompt: 'You are a bot.' } },
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const crewx = await Crewx.fromConfig(config, {}, process.cwd());
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `crewx.query(agentRef, message, options?)`
|
|
117
|
+
|
|
118
|
+
Ask an agent a question. Read-only — the agent is expected to respond with text only.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const result = await crewx.query('assistant', 'Summarize this PR');
|
|
122
|
+
|
|
123
|
+
// With options
|
|
124
|
+
const result = await crewx.query('assistant', 'Translate to Korean', {
|
|
125
|
+
model: 'claude-opus-4-6', // override model
|
|
126
|
+
provider: 'cli/claude', // override provider
|
|
127
|
+
context: 'Additional context', // prepended to the message
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> **Note**: `'@assistant'` 형태도 동작합니다 (CLI 호환). SDK에서는 bare id가 권장됩니다.
|
|
132
|
+
|
|
133
|
+
**Returns**: [`QueryResult`](#queryresult)
|
|
134
|
+
|
|
135
|
+
### `crewx.execute(agentRef, message, options?)`
|
|
136
|
+
|
|
137
|
+
Run a task with an agent. The agent may write files, run shell commands, etc. Uses `--dangerously-skip-permissions` for `cli/claude`.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const result = await crewx.execute('coder', 'Fix the failing tests in src/');
|
|
141
|
+
|
|
142
|
+
if (!result.ok) {
|
|
143
|
+
console.error('Failed:', result.error?.code, result.error?.message);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Returns**: [`ExecuteResult`](#executeresult)
|
|
148
|
+
|
|
149
|
+
### `crewx.renderAgentPromptFull(agentId, options?)`
|
|
150
|
+
|
|
151
|
+
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.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const prompt = await crewx.renderAgentPromptFull('assistant');
|
|
155
|
+
console.log(prompt);
|
|
156
|
+
|
|
157
|
+
// With layout override
|
|
158
|
+
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
159
|
+
layout: 'crewx/minimal',
|
|
160
|
+
session: { mode: 'execute', platform: 'api' },
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `crewx.registerLayout(name, template)`
|
|
165
|
+
|
|
166
|
+
Register a custom layout at runtime. See [Layout System](#4-layout-system).
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
crewx.registerLayout('my-layout', `
|
|
170
|
+
# {{agent.name}}
|
|
171
|
+
{{agent.inline.prompt}}
|
|
172
|
+
---
|
|
173
|
+
Session: {{session.mode}}
|
|
174
|
+
`);
|
|
175
|
+
|
|
176
|
+
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
177
|
+
layout: 'my-layout',
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `crewx.agents`
|
|
182
|
+
|
|
183
|
+
`ReadonlyMap<string, AgentConfig>` — all agents loaded from `crewx.yaml`.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// List all agents
|
|
187
|
+
for (const [id, agent] of crewx.agents) {
|
|
188
|
+
console.log(id, agent.provider);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check if an agent exists
|
|
192
|
+
if (crewx.agents.has('coder')) {
|
|
193
|
+
// ...
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### `crewx.filterAgents(filters)`
|
|
198
|
+
|
|
199
|
+
Filter agents by role, team, or provider. Supports glob patterns.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// All agents on the 'backend' team
|
|
203
|
+
const agents = crewx.filterAgents({ team: 'backend' });
|
|
204
|
+
|
|
205
|
+
// All Claude-based agents
|
|
206
|
+
const agents = crewx.filterAgents({ provider: 'cli/claude' });
|
|
207
|
+
|
|
208
|
+
// Wildcard: all cli/* providers
|
|
209
|
+
const agents = crewx.filterAgents({ provider: 'cli/*' });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 3. `crewx.yaml` Structure
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
# ─── Agents ──────────────────────────────────────────────────
|
|
218
|
+
agents:
|
|
219
|
+
my_agent:
|
|
220
|
+
name: My Agent # Display name (optional)
|
|
221
|
+
role: Backend Developer # Role label (optional)
|
|
222
|
+
team: core # Team label (optional)
|
|
223
|
+
provider: cli/claude # Provider (required)
|
|
224
|
+
working_directory: . # Working dir for the agent
|
|
225
|
+
|
|
226
|
+
inline: # Inline agent definition
|
|
227
|
+
model: claude-sonnet-4-6 # Model override (optional)
|
|
228
|
+
prompt: | # System prompt (Handlebars template)
|
|
229
|
+
You are an expert {{agent.role}}.
|
|
230
|
+
Today's context: {{{documents.guidelines.content}}}
|
|
231
|
+
layout: crewx/minimal # Layout override for this agent (optional)
|
|
232
|
+
|
|
233
|
+
# ─── Layouts ─────────────────────────────────────────────────
|
|
234
|
+
layouts:
|
|
235
|
+
default: crewx/minimal # Project-level default layout
|
|
236
|
+
|
|
237
|
+
# ─── Documents ───────────────────────────────────────────────
|
|
238
|
+
documents:
|
|
239
|
+
guidelines:
|
|
240
|
+
path: ./docs/guidelines.md # Loaded at startup, available as documents.guidelines
|
|
241
|
+
api_spec:
|
|
242
|
+
path: ./docs/api.md
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Multiple providers
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
agents:
|
|
249
|
+
polyglot:
|
|
250
|
+
provider: # Array = first is primary, rest are fallbacks (future)
|
|
251
|
+
- cli/claude
|
|
252
|
+
- cli/gemini
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 4. Layout System
|
|
258
|
+
|
|
259
|
+
Layouts wrap the agent's raw prompt with structure — identity blocks, session info, available tools, etc.
|
|
260
|
+
|
|
261
|
+
### Built-in Layouts
|
|
262
|
+
|
|
263
|
+
| ID | Description |
|
|
264
|
+
|----|-------------|
|
|
265
|
+
| `crewx/default` | Full structured layout (identity + session + prompt) |
|
|
266
|
+
| `crewx/minimal` | Minimal wrapper — just the agent prompt |
|
|
267
|
+
|
|
268
|
+
### Resolution Priority
|
|
269
|
+
|
|
270
|
+
When calling `renderAgentPromptFull()`, the layout is resolved in this order (first match wins):
|
|
271
|
+
|
|
272
|
+
1. `options.layout` — call-site override
|
|
273
|
+
2. `agent.inline.layout` — per-agent definition in `crewx.yaml`
|
|
274
|
+
3. `config.layouts.default` — project-level default
|
|
275
|
+
4. `crewx/default` — SDK built-in fallback
|
|
276
|
+
|
|
277
|
+
### Custom Layout via `registerLayout()`
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
crewx.registerLayout('compact', `
|
|
281
|
+
## {{agent.name}} ({{agent.role}})
|
|
282
|
+
{{agent.inline.prompt}}
|
|
283
|
+
`);
|
|
284
|
+
|
|
285
|
+
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
286
|
+
layout: 'compact',
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Inline Layout in `crewx.yaml`
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
agents:
|
|
294
|
+
assistant:
|
|
295
|
+
provider: cli/claude
|
|
296
|
+
inline:
|
|
297
|
+
prompt: You are helpful.
|
|
298
|
+
layout:
|
|
299
|
+
id: crewx/default
|
|
300
|
+
props:
|
|
301
|
+
show_skills: false # Pass props to the layout template
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Inline Template String
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const prompt = await crewx.renderAgentPromptFull('assistant', {
|
|
308
|
+
layout: { template: 'SYSTEM: {{agent.inline.prompt}}' },
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 5. Template Helpers
|
|
315
|
+
|
|
316
|
+
The `inline.prompt` field in `crewx.yaml` is a **Handlebars template**. These helpers are available:
|
|
317
|
+
|
|
318
|
+
### P0 Helpers (Core)
|
|
319
|
+
|
|
320
|
+
| Helper | Usage | Description |
|
|
321
|
+
|--------|-------|-------------|
|
|
322
|
+
| `exec` | `{{exec "git log --oneline -5"}}` | Run shell command and inline output |
|
|
323
|
+
| `include` | `{{include someVar}}` | Include a string variable as-is (no escaping) |
|
|
324
|
+
| `fenced_code` | `{{fenced_code content lang="ts"}}` | Wrap content in Markdown code block |
|
|
325
|
+
|
|
326
|
+
### Condition Helpers
|
|
327
|
+
|
|
328
|
+
```handlebars
|
|
329
|
+
{{#if (eq agent.team "backend")}}Backend mode{{/if}}
|
|
330
|
+
{{#if (and featureA featureB)}}Both enabled{{/if}}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Available: `eq`, `ne`, `and`, `or`, `not`, `contains`
|
|
334
|
+
|
|
335
|
+
### Utility Helpers
|
|
336
|
+
|
|
337
|
+
| Helper | Description |
|
|
338
|
+
|--------|-------------|
|
|
339
|
+
| `truncate text len` | Truncate string to N chars |
|
|
340
|
+
| `length array` | Array/string length |
|
|
341
|
+
| `escapeHandlebars text` | Escape `{{` in content |
|
|
342
|
+
| `formatFileSize bytes` | `1048576` → `1 MB` |
|
|
343
|
+
| `formatTimestamp ms` | Unix ms → readable date |
|
|
344
|
+
|
|
345
|
+
### Document Access in Templates
|
|
346
|
+
|
|
347
|
+
Documents defined in `crewx.yaml` are automatically available:
|
|
348
|
+
|
|
349
|
+
```handlebars
|
|
350
|
+
# Inline the full document
|
|
351
|
+
{{{documents.guidelines.content}}}
|
|
352
|
+
|
|
353
|
+
# Access metadata
|
|
354
|
+
Path: {{documents.guidelines.path}}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### `exec` Security Policy
|
|
358
|
+
|
|
359
|
+
Control which shell commands are allowed in templates:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
const crewx = await Crewx.loadYaml('./crewx.yaml', {
|
|
363
|
+
execPolicy: {
|
|
364
|
+
allow: ['git *', 'npm run *', 'cat *'],
|
|
365
|
+
deny: ['rm *', 'curl *'],
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Or set it in `crewx.yaml`:
|
|
371
|
+
|
|
372
|
+
```yaml
|
|
373
|
+
settings:
|
|
374
|
+
template:
|
|
375
|
+
exec:
|
|
376
|
+
allow:
|
|
377
|
+
- "git *"
|
|
378
|
+
- "npm run *"
|
|
379
|
+
deny:
|
|
380
|
+
- "rm *"
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Glob syntax: `*` matches within a segment, `**` matches across path segments.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 6. Provider Bridge
|
|
388
|
+
|
|
389
|
+
Each agent has a `provider` field that tells the SDK which AI backend to use.
|
|
390
|
+
|
|
391
|
+
### Supported Providers
|
|
392
|
+
|
|
393
|
+
| Provider | CLI Command | Notes |
|
|
394
|
+
|----------|-------------|-------|
|
|
395
|
+
| `cli/claude` | `claude` | Claude Code CLI |
|
|
396
|
+
| `cli/gemini` | `gemini` | Gemini CLI |
|
|
397
|
+
| `cli/copilot` | `gh copilot suggest` | GitHub Copilot |
|
|
398
|
+
| `cli/codex` | `codex` | OpenAI Codex CLI |
|
|
399
|
+
|
|
400
|
+
> **Coming Soon**: `api/claude`, `api/openai` — direct API providers without CLI dependency.
|
|
401
|
+
|
|
402
|
+
### Provider Override at Call Site
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Use a different provider for one call
|
|
406
|
+
const result = await crewx.query('assistant', 'Hello', {
|
|
407
|
+
provider: 'cli/gemini',
|
|
408
|
+
model: 'gemini-2.0-flash',
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### `query` vs `execute` Mode
|
|
413
|
+
|
|
414
|
+
| | `query()` | `execute()` |
|
|
415
|
+
|--|-----------|-------------|
|
|
416
|
+
| Intent | Read-only Q&A | Task with side effects |
|
|
417
|
+
| cli/claude flags | `-p --output-format stream-json --verbose` | + `--dangerously-skip-permissions` |
|
|
418
|
+
| Use when | Asking questions, generating text | Writing files, running commands |
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## 7. Plugin System
|
|
423
|
+
|
|
424
|
+
Plugins extend `CrewxPlugin` from `@crewx/sdk` and attach event listeners in `attach()`. Register with `crewx.use(plugin)` and release resources by calling `crewx.close()`.
|
|
425
|
+
|
|
426
|
+
### Lifecycle
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
await crewx.use(plugin); // calls plugin.attach(crewx) — subscribe events, open DB, etc.
|
|
430
|
+
await crewx.close(); // calls plugin.detach(crewx) on all plugins in LIFO order
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Same plugin instance registered twice is silently ignored.
|
|
434
|
+
|
|
435
|
+
### Built-in Plugins (`@crewx/cli`)
|
|
436
|
+
|
|
437
|
+
| Plugin | Import path | Storage |
|
|
438
|
+
|--------|-------------|---------|
|
|
439
|
+
| `FileLoggerPlugin` | `@crewx/cli/plugins/file-logger` | `.crewx/logs/{ts}_{traceId}.log` (one file per task) |
|
|
440
|
+
| `SqliteTracingPlugin` | `@crewx/cli/plugins/sqlite-tracing` | `~/.crewx/crewx.db` — `tasks` table |
|
|
441
|
+
|
|
442
|
+
> The `crewx` CLI auto-attaches both plugins for every run. No setup needed when using the CLI directly.
|
|
443
|
+
|
|
444
|
+
### Custom Plugin
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
import { CrewxPlugin } from '@crewx/sdk';
|
|
448
|
+
import type { Crewx } from '@crewx/sdk';
|
|
449
|
+
|
|
450
|
+
class CostTrackerPlugin extends CrewxPlugin {
|
|
451
|
+
readonly name = 'cost-tracker';
|
|
452
|
+
|
|
453
|
+
attach(crewx: Crewx) {
|
|
454
|
+
crewx.on('task:end', (e) => {
|
|
455
|
+
if (e.costUsd) console.log(`[cost] ${e.agentRef}: $${e.costUsd.toFixed(4)}`);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// detach() is optional — base class no-op is sufficient if no cleanup needed
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
await crewx.use(new CostTrackerPlugin());
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## 8. Event System
|
|
467
|
+
|
|
468
|
+
The `Crewx` class extends `TypedEventEmitter`. Events are emitted automatically during `query()` and `execute()`.
|
|
469
|
+
|
|
470
|
+
### Event Catalog
|
|
471
|
+
|
|
472
|
+
| Event | When | Key Payload Fields |
|
|
473
|
+
|-------|------|--------------------|
|
|
474
|
+
| `task:start` | `query()`/`execute()` begins | `traceId`, `agentRef`, `mode`, `pid`, `model`, `provider`, `message`, `timestamp` |
|
|
475
|
+
| `task:end` | Call completes (success or failure) | `traceId`, `agentRef`, `durationMs`, `result`, `error`, `inputTokens`, `outputTokens`, `costUsd`, `model` |
|
|
476
|
+
| `task:output` | Each line of provider output | `traceId`, `agentRef`, `output`, `level` (`stdout`\|`stderr`\|`info`) |
|
|
477
|
+
|
|
478
|
+
All events share `traceId` (format: `tsk_XXXXXXXX`) and `timestamp` inherited from `BaseEvent`.
|
|
479
|
+
|
|
480
|
+
### `crewx.on(event, listener)` → `UnsubscribeFn`
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import type { TaskEndEvent } from '@crewx/sdk';
|
|
484
|
+
|
|
485
|
+
const unsub = crewx.on('task:end', (e: TaskEndEvent) => {
|
|
486
|
+
console.log(`${e.agentRef} finished in ${e.durationMs}ms | tokens: ${e.inputTokens}+${e.outputTokens}`);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// Remove listener when no longer needed
|
|
490
|
+
unsub();
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
`crewx.once(event, listener)` fires exactly once and auto-unsubscribes.
|
|
494
|
+
|
|
495
|
+
> **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.
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## 9. Type Reference
|
|
500
|
+
|
|
501
|
+
### `CrewxOptions`
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
interface CrewxOptions {
|
|
505
|
+
workspaceRoot?: string;
|
|
506
|
+
platform?: 'cli' | 'slack' | 'api';
|
|
507
|
+
execPolicy?: ExecPolicy; // Allowed/denied shell commands in {{exec}}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### `QueryOptions` / `ExecuteOptions`
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
interface QueryOptions {
|
|
515
|
+
model?: string; // Override model (e.g. 'claude-opus-4-6')
|
|
516
|
+
provider?: string; // Override provider (e.g. 'cli/gemini')
|
|
517
|
+
context?: string; // Extra context prepended to message
|
|
518
|
+
metadata?: Record<string, unknown>;
|
|
519
|
+
}
|
|
520
|
+
// ExecuteOptions has the same shape
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### `QueryResult` / `ExecuteResult`
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
interface QueryResult {
|
|
527
|
+
ok: boolean;
|
|
528
|
+
data: string; // Agent's response text
|
|
529
|
+
error?: {
|
|
530
|
+
code: string; // 'AGENT_NOT_FOUND' | 'PROVIDER_ERROR' | 'QUERY_FAILED'
|
|
531
|
+
message: string;
|
|
532
|
+
};
|
|
533
|
+
meta: {
|
|
534
|
+
agentId: string;
|
|
535
|
+
provider: string;
|
|
536
|
+
model?: string;
|
|
537
|
+
durationMs: number;
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
// ExecuteResult has the same shape
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### `AgentConfig`
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
interface AgentConfig {
|
|
547
|
+
id: string;
|
|
548
|
+
name?: string;
|
|
549
|
+
role?: string;
|
|
550
|
+
team?: string;
|
|
551
|
+
provider: string | string[];
|
|
552
|
+
working_directory?: string;
|
|
553
|
+
description?: string;
|
|
554
|
+
inline?: {
|
|
555
|
+
model?: string;
|
|
556
|
+
system_prompt?: string;
|
|
557
|
+
prompt?: string;
|
|
558
|
+
layout?: string | { id: string; props?: Record<string, unknown> };
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### `ExecPolicy`
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
interface ExecPolicy {
|
|
567
|
+
allow: string[]; // Glob patterns for allowed commands
|
|
568
|
+
deny: string[]; // Glob patterns for denied commands (takes precedence)
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Coming Soon
|
|
575
|
+
|
|
576
|
+
- **Tool System** — attach custom tools/functions to agents (§3 of design spec)
|
|
577
|
+
- **`api/*` Providers** — direct API calls without CLI dependency
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Requirements
|
|
582
|
+
|
|
583
|
+
- Node.js >= 20.19.0
|
|
584
|
+
- At least one CLI provider installed (e.g. `npm i -g @anthropic-ai/claude-code`)
|