@laurentenhoor/devclaw 0.1.0
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/LICENSE +21 -0
- package/README.md +406 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +2 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +42 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/binding-manager.d.ts +35 -0
- package/dist/lib/binding-manager.d.ts.map +1 -0
- package/dist/lib/binding-manager.js +88 -0
- package/dist/lib/binding-manager.js.map +1 -0
- package/dist/lib/cli.d.ts +12 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +69 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/dispatch.d.ts +58 -0
- package/dist/lib/dispatch.d.ts.map +1 -0
- package/dist/lib/dispatch.js +163 -0
- package/dist/lib/dispatch.js.map +1 -0
- package/dist/lib/model-selector.d.ts +21 -0
- package/dist/lib/model-selector.d.ts.map +1 -0
- package/dist/lib/model-selector.js +74 -0
- package/dist/lib/model-selector.js.map +1 -0
- package/dist/lib/notify.d.ts +54 -0
- package/dist/lib/notify.d.ts.map +1 -0
- package/dist/lib/notify.js +143 -0
- package/dist/lib/notify.js.map +1 -0
- package/dist/lib/onboarding.d.ts +5 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +124 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/projects.d.ts +64 -0
- package/dist/lib/projects.d.ts.map +1 -0
- package/dist/lib/projects.js +127 -0
- package/dist/lib/projects.js.map +1 -0
- package/dist/lib/providers/github.d.ts +23 -0
- package/dist/lib/providers/github.d.ts.map +1 -0
- package/dist/lib/providers/github.js +130 -0
- package/dist/lib/providers/github.js.map +1 -0
- package/dist/lib/providers/gitlab.d.ts +23 -0
- package/dist/lib/providers/gitlab.d.ts.map +1 -0
- package/dist/lib/providers/gitlab.js +133 -0
- package/dist/lib/providers/gitlab.js.map +1 -0
- package/dist/lib/providers/index.d.ts +12 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +25 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/provider.d.ts +35 -0
- package/dist/lib/providers/provider.d.ts.map +1 -0
- package/dist/lib/providers/provider.js +13 -0
- package/dist/lib/providers/provider.js.map +1 -0
- package/dist/lib/services/health.d.ts +38 -0
- package/dist/lib/services/health.d.ts.map +1 -0
- package/dist/lib/services/health.js +100 -0
- package/dist/lib/services/health.js.map +1 -0
- package/dist/lib/services/heartbeat.d.ts +38 -0
- package/dist/lib/services/heartbeat.d.ts.map +1 -0
- package/dist/lib/services/heartbeat.js +199 -0
- package/dist/lib/services/heartbeat.js.map +1 -0
- package/dist/lib/services/pipeline.d.ts +36 -0
- package/dist/lib/services/pipeline.d.ts.map +1 -0
- package/dist/lib/services/pipeline.js +90 -0
- package/dist/lib/services/pipeline.js.map +1 -0
- package/dist/lib/services/queue.d.ts +14 -0
- package/dist/lib/services/queue.d.ts.map +1 -0
- package/dist/lib/services/queue.js +31 -0
- package/dist/lib/services/queue.js.map +1 -0
- package/dist/lib/services/tick.d.ts +62 -0
- package/dist/lib/services/tick.d.ts.map +1 -0
- package/dist/lib/services/tick.js +160 -0
- package/dist/lib/services/tick.js.map +1 -0
- package/dist/lib/setup/agent.d.ts +14 -0
- package/dist/lib/setup/agent.d.ts.map +1 -0
- package/dist/lib/setup/agent.js +72 -0
- package/dist/lib/setup/agent.js.map +1 -0
- package/dist/lib/setup/config.d.ts +22 -0
- package/dist/lib/setup/config.d.ts.map +1 -0
- package/dist/lib/setup/config.js +67 -0
- package/dist/lib/setup/config.js.map +1 -0
- package/dist/lib/setup/index.d.ts +53 -0
- package/dist/lib/setup/index.d.ts.map +1 -0
- package/dist/lib/setup/index.js +68 -0
- package/dist/lib/setup/index.js.map +1 -0
- package/dist/lib/setup/workspace.d.ts +6 -0
- package/dist/lib/setup/workspace.d.ts.map +1 -0
- package/dist/lib/setup/workspace.js +69 -0
- package/dist/lib/setup/workspace.js.map +1 -0
- package/dist/lib/templates.d.ts +9 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +163 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/tiers.d.ts +55 -0
- package/dist/lib/tiers.d.ts.map +1 -0
- package/dist/lib/tiers.js +74 -0
- package/dist/lib/tiers.js.map +1 -0
- package/dist/lib/tool-helpers.d.ts +44 -0
- package/dist/lib/tool-helpers.d.ts.map +1 -0
- package/dist/lib/tool-helpers.js +65 -0
- package/dist/lib/tool-helpers.js.map +1 -0
- package/dist/lib/tools/health.d.ts +28 -0
- package/dist/lib/tools/health.d.ts.map +1 -0
- package/dist/lib/tools/health.js +61 -0
- package/dist/lib/tools/health.js.map +1 -0
- package/dist/lib/tools/onboard.d.ts +24 -0
- package/dist/lib/tools/onboard.d.ts.map +1 -0
- package/dist/lib/tools/onboard.js +27 -0
- package/dist/lib/tools/onboard.js.map +1 -0
- package/dist/lib/tools/project-register.d.ts +51 -0
- package/dist/lib/tools/project-register.d.ts.map +1 -0
- package/dist/lib/tools/project-register.js +172 -0
- package/dist/lib/tools/project-register.js.map +1 -0
- package/dist/lib/tools/queue-status.test.d.ts +2 -0
- package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
- package/dist/lib/tools/queue-status.test.js +48 -0
- package/dist/lib/tools/queue-status.test.js.map +1 -0
- package/dist/lib/tools/setup.d.ts +76 -0
- package/dist/lib/tools/setup.d.ts.map +1 -0
- package/dist/lib/tools/setup.js +102 -0
- package/dist/lib/tools/setup.js.map +1 -0
- package/dist/lib/tools/status.d.ts +24 -0
- package/dist/lib/tools/status.d.ts.map +1 -0
- package/dist/lib/tools/status.js +53 -0
- package/dist/lib/tools/status.js.map +1 -0
- package/dist/lib/tools/task-comment.d.ts +40 -0
- package/dist/lib/tools/task-comment.d.ts.map +1 -0
- package/dist/lib/tools/task-comment.js +84 -0
- package/dist/lib/tools/task-comment.js.map +1 -0
- package/dist/lib/tools/task-create.d.ts +54 -0
- package/dist/lib/tools/task-create.d.ts.map +1 -0
- package/dist/lib/tools/task-create.js +77 -0
- package/dist/lib/tools/task-create.js.map +1 -0
- package/dist/lib/tools/task-update.d.ts +40 -0
- package/dist/lib/tools/task-update.d.ts.map +1 -0
- package/dist/lib/tools/task-update.js +79 -0
- package/dist/lib/tools/task-update.js.map +1 -0
- package/dist/lib/tools/task-update.test.d.ts +7 -0
- package/dist/lib/tools/task-update.test.d.ts.map +1 -0
- package/dist/lib/tools/task-update.test.js +55 -0
- package/dist/lib/tools/task-update.test.js.map +1 -0
- package/dist/lib/tools/work-finish.d.ts +43 -0
- package/dist/lib/tools/work-finish.d.ts.map +1 -0
- package/dist/lib/tools/work-finish.js +77 -0
- package/dist/lib/tools/work-finish.js.map +1 -0
- package/dist/lib/tools/work-start.d.ts +39 -0
- package/dist/lib/tools/work-start.d.ts.map +1 -0
- package/dist/lib/tools/work-start.js +129 -0
- package/dist/lib/tools/work-start.js.map +1 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +8 -0
- package/dist/lib/types.js.map +1 -0
- package/docs/ARCHITECTURE.md +662 -0
- package/docs/CONFIGURATION.md +336 -0
- package/docs/MANAGEMENT.md +120 -0
- package/docs/ONBOARDING.md +251 -0
- package/docs/QA_WORKFLOW.md +120 -0
- package/docs/ROADMAP.md +96 -0
- package/docs/TESTING.md +339 -0
- package/docs/TOOLS.md +361 -0
- package/package.json +55 -0
package/docs/TESTING.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# DevClaw Testing Guide
|
|
2
|
+
|
|
3
|
+
Comprehensive automated testing for DevClaw onboarding and setup.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Run all tests
|
|
12
|
+
npm test
|
|
13
|
+
|
|
14
|
+
# Run with coverage report
|
|
15
|
+
npm run test:coverage
|
|
16
|
+
|
|
17
|
+
# Run in watch mode (auto-rerun on changes)
|
|
18
|
+
npm run test:watch
|
|
19
|
+
|
|
20
|
+
# Run with UI (browser-based test explorer)
|
|
21
|
+
npm run test:ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Test Coverage
|
|
25
|
+
|
|
26
|
+
### Scenario 1: New User (No Prior DevClaw Setup)
|
|
27
|
+
**File:** `tests/setup/new-user.test.ts`
|
|
28
|
+
|
|
29
|
+
**What's tested:**
|
|
30
|
+
- First-time agent creation with default models
|
|
31
|
+
- Channel binding creation (telegram/whatsapp)
|
|
32
|
+
- Workspace file generation (AGENTS.md, HEARTBEAT.md, projects/, log/)
|
|
33
|
+
- Plugin configuration initialization
|
|
34
|
+
- Error handling: channel not configured
|
|
35
|
+
- Error handling: channel disabled
|
|
36
|
+
|
|
37
|
+
**Example:**
|
|
38
|
+
```typescript
|
|
39
|
+
// Before: openclaw.json has no DevClaw agents
|
|
40
|
+
{
|
|
41
|
+
"agents": { "list": [{ "id": "main", ... }] },
|
|
42
|
+
"bindings": [],
|
|
43
|
+
"plugins": { "entries": {} }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// After: New orchestrator created
|
|
47
|
+
{
|
|
48
|
+
"agents": {
|
|
49
|
+
"list": [
|
|
50
|
+
{ "id": "main", ... },
|
|
51
|
+
{ "id": "my-first-orchestrator", ... }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"bindings": [
|
|
55
|
+
{ "agentId": "my-first-orchestrator", "match": { "channel": "telegram" } }
|
|
56
|
+
],
|
|
57
|
+
"plugins": {
|
|
58
|
+
"entries": {
|
|
59
|
+
"devclaw": {
|
|
60
|
+
"config": {
|
|
61
|
+
"models": {
|
|
62
|
+
"dev": {
|
|
63
|
+
"junior": "anthropic/claude-haiku-4-5",
|
|
64
|
+
"medior": "anthropic/claude-sonnet-4-5",
|
|
65
|
+
"senior": "anthropic/claude-opus-4-5"
|
|
66
|
+
},
|
|
67
|
+
"qa": {
|
|
68
|
+
"reviewer": "anthropic/claude-sonnet-4-5",
|
|
69
|
+
"tester": "anthropic/claude-haiku-4-5"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Scenario 2: Existing User (Migration)
|
|
80
|
+
**File:** `tests/setup/existing-user.test.ts`
|
|
81
|
+
|
|
82
|
+
**What's tested:**
|
|
83
|
+
- Channel conflict detection (existing channel-wide binding)
|
|
84
|
+
- Binding migration from old agent to new agent
|
|
85
|
+
- Custom model preservation during migration
|
|
86
|
+
- Old agent preservation (not deleted)
|
|
87
|
+
- Error handling: migration source doesn't exist
|
|
88
|
+
- Error handling: migration source has no binding
|
|
89
|
+
|
|
90
|
+
**Example:**
|
|
91
|
+
```typescript
|
|
92
|
+
// Before: Old orchestrator has telegram binding
|
|
93
|
+
{
|
|
94
|
+
"agents": {
|
|
95
|
+
"list": [
|
|
96
|
+
{ "id": "main", ... },
|
|
97
|
+
{ "id": "old-orchestrator", ... }
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"bindings": [
|
|
101
|
+
{ "agentId": "old-orchestrator", "match": { "channel": "telegram" } }
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// After: Binding migrated to new orchestrator
|
|
106
|
+
{
|
|
107
|
+
"agents": {
|
|
108
|
+
"list": [
|
|
109
|
+
{ "id": "main", ... },
|
|
110
|
+
{ "id": "old-orchestrator", ... },
|
|
111
|
+
{ "id": "new-orchestrator", ... }
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"bindings": [
|
|
115
|
+
{ "agentId": "new-orchestrator", "match": { "channel": "telegram" } }
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Scenario 3: Power User (Multiple Agents)
|
|
121
|
+
**File:** `tests/setup/power-user.test.ts`
|
|
122
|
+
|
|
123
|
+
**What's tested:**
|
|
124
|
+
- No conflicts with group-specific bindings
|
|
125
|
+
- Channel-wide binding creation alongside group bindings
|
|
126
|
+
- Multiple orchestrators coexisting
|
|
127
|
+
- Routing logic (specific bindings win over channel-wide)
|
|
128
|
+
- WhatsApp support
|
|
129
|
+
- Scale testing (12+ orchestrators)
|
|
130
|
+
|
|
131
|
+
**Example:**
|
|
132
|
+
```typescript
|
|
133
|
+
// Before: Two project orchestrators with group-specific bindings
|
|
134
|
+
{
|
|
135
|
+
"agents": {
|
|
136
|
+
"list": [
|
|
137
|
+
{ "id": "project-a-orchestrator", ... },
|
|
138
|
+
{ "id": "project-b-orchestrator", ... }
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"bindings": [
|
|
142
|
+
{
|
|
143
|
+
"agentId": "project-a-orchestrator",
|
|
144
|
+
"match": { "channel": "telegram", "peer": { "kind": "group", "id": "-1001234567890" } }
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"agentId": "project-b-orchestrator",
|
|
148
|
+
"match": { "channel": "telegram", "peer": { "kind": "group", "id": "-1009876543210" } }
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// After: Channel-wide orchestrator added (no conflicts)
|
|
154
|
+
{
|
|
155
|
+
"agents": {
|
|
156
|
+
"list": [
|
|
157
|
+
{ "id": "project-a-orchestrator", ... },
|
|
158
|
+
{ "id": "project-b-orchestrator", ... },
|
|
159
|
+
{ "id": "global-orchestrator", ... }
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
"bindings": [
|
|
163
|
+
{
|
|
164
|
+
"agentId": "project-a-orchestrator",
|
|
165
|
+
"match": { "channel": "telegram", "peer": { "kind": "group", "id": "-1001234567890" } }
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"agentId": "project-b-orchestrator",
|
|
169
|
+
"match": { "channel": "telegram", "peer": { "kind": "group", "id": "-1009876543210" } }
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"agentId": "global-orchestrator",
|
|
173
|
+
"match": { "channel": "telegram" } // Channel-wide (no peer)
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Routing: Group messages go to specific agents, everything else goes to global
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Test Architecture
|
|
182
|
+
|
|
183
|
+
### Mock File System
|
|
184
|
+
The tests use an in-memory mock file system (`MockFileSystem`) that simulates:
|
|
185
|
+
- Reading/writing openclaw.json
|
|
186
|
+
- Creating/reading workspace files
|
|
187
|
+
- Tracking command executions (openclaw agents add)
|
|
188
|
+
|
|
189
|
+
**Why?** Tests run in isolation without touching the real file system, making them:
|
|
190
|
+
- Fast (no I/O)
|
|
191
|
+
- Reliable (no file conflicts)
|
|
192
|
+
- Repeatable (clean state every test)
|
|
193
|
+
|
|
194
|
+
### Fixtures
|
|
195
|
+
Pre-built configurations for different user types:
|
|
196
|
+
- `createNewUserConfig()` - Empty slate
|
|
197
|
+
- `createCommonUserConfig()` - One orchestrator with binding
|
|
198
|
+
- `createPowerUserConfig()` - Multiple orchestrators with group bindings
|
|
199
|
+
- `createNoChannelConfig()` - Channel not configured
|
|
200
|
+
- `createDisabledChannelConfig()` - Channel disabled
|
|
201
|
+
|
|
202
|
+
### Assertions
|
|
203
|
+
Reusable assertion helpers that make tests readable:
|
|
204
|
+
```typescript
|
|
205
|
+
assertAgentExists(mockFs, "my-agent", "My Agent");
|
|
206
|
+
assertChannelBinding(mockFs, "my-agent", "telegram");
|
|
207
|
+
assertWorkspaceFilesExist(mockFs, "my-agent");
|
|
208
|
+
assertDevClawConfig(mockFs, { junior: "anthropic/claude-haiku-4-5" });
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## CI/CD Integration
|
|
212
|
+
|
|
213
|
+
### GitHub Actions
|
|
214
|
+
```yaml
|
|
215
|
+
name: Test
|
|
216
|
+
on: [push, pull_request]
|
|
217
|
+
jobs:
|
|
218
|
+
test:
|
|
219
|
+
runs-on: ubuntu-latest
|
|
220
|
+
steps:
|
|
221
|
+
- uses: actions/checkout@v3
|
|
222
|
+
- uses: actions/setup-node@v3
|
|
223
|
+
with:
|
|
224
|
+
node-version: 20
|
|
225
|
+
- run: npm ci
|
|
226
|
+
- run: npm test
|
|
227
|
+
- run: npm run test:coverage
|
|
228
|
+
- uses: codecov/codecov-action@v3
|
|
229
|
+
with:
|
|
230
|
+
files: ./coverage/coverage-final.json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### GitLab CI
|
|
234
|
+
```yaml
|
|
235
|
+
test:
|
|
236
|
+
image: node:20
|
|
237
|
+
script:
|
|
238
|
+
- npm ci
|
|
239
|
+
- npm test
|
|
240
|
+
- npm run test:coverage
|
|
241
|
+
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
|
|
242
|
+
artifacts:
|
|
243
|
+
reports:
|
|
244
|
+
coverage_report:
|
|
245
|
+
coverage_format: cobertura
|
|
246
|
+
path: coverage/cobertura-coverage.xml
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Debugging Tests
|
|
250
|
+
|
|
251
|
+
### Run specific test
|
|
252
|
+
```bash
|
|
253
|
+
npm test -- new-user # Run all new-user tests
|
|
254
|
+
npm test -- "should create agent" # Run tests matching pattern
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Debug with Node inspector
|
|
258
|
+
```bash
|
|
259
|
+
node --inspect-brk node_modules/.bin/vitest run
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Then open Chrome DevTools at `chrome://inspect`
|
|
263
|
+
|
|
264
|
+
### View coverage report
|
|
265
|
+
```bash
|
|
266
|
+
npm run test:coverage
|
|
267
|
+
open coverage/index.html
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Adding Tests
|
|
271
|
+
|
|
272
|
+
### 1. Choose the right test file
|
|
273
|
+
- New feature → `tests/setup/new-user.test.ts`
|
|
274
|
+
- Migration feature → `tests/setup/existing-user.test.ts`
|
|
275
|
+
- Multi-agent feature → `tests/setup/power-user.test.ts`
|
|
276
|
+
|
|
277
|
+
### 2. Write the test
|
|
278
|
+
```typescript
|
|
279
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
280
|
+
import { MockFileSystem } from "../helpers/mock-fs.js";
|
|
281
|
+
import { createNewUserConfig } from "../helpers/fixtures.js";
|
|
282
|
+
import { assertAgentExists } from "../helpers/assertions.js";
|
|
283
|
+
|
|
284
|
+
describe("My new feature", () => {
|
|
285
|
+
let mockFs: MockFileSystem;
|
|
286
|
+
|
|
287
|
+
beforeEach(() => {
|
|
288
|
+
mockFs = new MockFileSystem(createNewUserConfig());
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("should do something useful", async () => {
|
|
292
|
+
// GIVEN: initial state (via fixture)
|
|
293
|
+
const beforeCount = countAgents(mockFs);
|
|
294
|
+
|
|
295
|
+
// WHEN: execute the operation
|
|
296
|
+
const config = mockFs.getConfig();
|
|
297
|
+
config.agents.list.push({
|
|
298
|
+
id: "test-agent",
|
|
299
|
+
name: "Test Agent",
|
|
300
|
+
workspace: "/home/test/.openclaw/workspace-test-agent",
|
|
301
|
+
agentDir: "/home/test/.openclaw/agents/test-agent/agent",
|
|
302
|
+
});
|
|
303
|
+
mockFs.setConfig(config);
|
|
304
|
+
|
|
305
|
+
// THEN: verify the outcome
|
|
306
|
+
assertAgentExists(mockFs, "test-agent", "Test Agent");
|
|
307
|
+
expect(countAgents(mockFs)).toBe(beforeCount + 1);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 3. Run your test
|
|
313
|
+
```bash
|
|
314
|
+
npm test -- "should do something useful"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Best Practices
|
|
318
|
+
|
|
319
|
+
### ✅ DO
|
|
320
|
+
- Test one thing per test
|
|
321
|
+
- Use descriptive test names ("should create agent with telegram binding")
|
|
322
|
+
- Use fixtures for initial state
|
|
323
|
+
- Use assertion helpers for readability
|
|
324
|
+
- Test error cases
|
|
325
|
+
|
|
326
|
+
### ❌ DON'T
|
|
327
|
+
- Test implementation details (test behavior, not internals)
|
|
328
|
+
- Share state between tests (use beforeEach)
|
|
329
|
+
- Mock everything (only mock file system and commands)
|
|
330
|
+
- Write brittle tests (avoid hard-coded UUIDs, timestamps)
|
|
331
|
+
|
|
332
|
+
## Test Metrics
|
|
333
|
+
|
|
334
|
+
Current coverage:
|
|
335
|
+
- **Lines:** Target 80%+
|
|
336
|
+
- **Functions:** Target 90%+
|
|
337
|
+
- **Branches:** Target 75%+
|
|
338
|
+
|
|
339
|
+
Run `npm run test:coverage` to see detailed metrics.
|
package/docs/TOOLS.md
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# DevClaw — Tools Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for all 11 tools registered by DevClaw. See [`index.ts`](../index.ts) for registration.
|
|
4
|
+
|
|
5
|
+
## Worker Lifecycle
|
|
6
|
+
|
|
7
|
+
### `work_start`
|
|
8
|
+
|
|
9
|
+
Pick up a task from the issue queue. Handles level assignment, label transition, session creation/reuse, task dispatch, and audit logging — all in one call.
|
|
10
|
+
|
|
11
|
+
**Source:** [`lib/tools/work-start.ts`](../lib/tools/work-start.ts)
|
|
12
|
+
|
|
13
|
+
**Context:** Only works in project group chats.
|
|
14
|
+
|
|
15
|
+
**Parameters:**
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Required | Description |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| `issueId` | number | No | Issue ID. If omitted, picks next by priority. |
|
|
20
|
+
| `role` | `"dev"` \| `"qa"` | No | Worker role. Auto-detected from issue label if omitted. |
|
|
21
|
+
| `projectGroupId` | string | No | Project group ID. Auto-detected from group context. |
|
|
22
|
+
| `level` | string | No | Developer level (`junior`, `medior`, `senior`, `reviewer`). Auto-detected if omitted. |
|
|
23
|
+
|
|
24
|
+
**What it does atomically:**
|
|
25
|
+
|
|
26
|
+
1. Resolves project from `projects.json`
|
|
27
|
+
2. Validates no active worker for this role
|
|
28
|
+
3. Fetches issue from tracker, verifies correct label state
|
|
29
|
+
4. Assigns level (LLM-chosen via `level` param → label detection → keyword heuristic fallback)
|
|
30
|
+
5. Resolves level to model ID via config or defaults
|
|
31
|
+
6. Loads prompt instructions from `projects/roles/<project>/<role>.md`
|
|
32
|
+
7. Looks up existing session for assigned level (session-per-level)
|
|
33
|
+
8. Transitions label (e.g. `To Do` → `Doing`)
|
|
34
|
+
9. Creates session via Gateway RPC if new (`sessions.patch`)
|
|
35
|
+
10. Dispatches task to worker session via CLI (`openclaw gateway call agent`)
|
|
36
|
+
11. Updates `projects.json` state (active, issueId, level, session key)
|
|
37
|
+
12. Writes audit log entries (work_start + model_selection)
|
|
38
|
+
13. Sends notification
|
|
39
|
+
14. Returns announcement text
|
|
40
|
+
|
|
41
|
+
**Level selection priority:**
|
|
42
|
+
|
|
43
|
+
1. `level` parameter (LLM-selected) — highest priority
|
|
44
|
+
2. Issue label (e.g. a label named "junior" or "senior")
|
|
45
|
+
3. Keyword heuristic from `model-selector.ts` — fallback
|
|
46
|
+
|
|
47
|
+
**Execution guards:**
|
|
48
|
+
|
|
49
|
+
- Rejects if role already has an active worker
|
|
50
|
+
- Respects `roleExecution` (sequential: rejects if other role is active)
|
|
51
|
+
|
|
52
|
+
**On failure:** Rolls back label transition. No orphaned state.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### `work_finish`
|
|
57
|
+
|
|
58
|
+
Complete a task with a result. Called by workers (DEV/QA sub-agent sessions) directly, or by the orchestrator.
|
|
59
|
+
|
|
60
|
+
**Source:** [`lib/tools/work-finish.ts`](../lib/tools/work-finish.ts)
|
|
61
|
+
|
|
62
|
+
**Parameters:**
|
|
63
|
+
|
|
64
|
+
| Parameter | Type | Required | Description |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `role` | `"dev"` \| `"qa"` | Yes | Worker role |
|
|
67
|
+
| `result` | string | Yes | Completion result (see table below) |
|
|
68
|
+
| `projectGroupId` | string | Yes | Project group ID |
|
|
69
|
+
| `summary` | string | No | Brief summary for the announcement |
|
|
70
|
+
| `prUrl` | string | No | PR/MR URL (auto-detected if omitted) |
|
|
71
|
+
|
|
72
|
+
**Valid results by role:**
|
|
73
|
+
|
|
74
|
+
| Role | Result | Label transition | Side effects |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| DEV | `"done"` | Doing → To Test | git pull, auto-detect PR URL |
|
|
77
|
+
| DEV | `"blocked"` | Doing → To Do | Task returns to queue |
|
|
78
|
+
| QA | `"pass"` | Testing → Done | Issue closed |
|
|
79
|
+
| QA | `"fail"` | Testing → To Improve | Issue reopened |
|
|
80
|
+
| QA | `"refine"` | Testing → Refining | Awaits human decision |
|
|
81
|
+
| QA | `"blocked"` | Testing → To Test | Task returns to QA queue |
|
|
82
|
+
|
|
83
|
+
**What it does atomically:**
|
|
84
|
+
|
|
85
|
+
1. Validates role:result combination
|
|
86
|
+
2. Resolves project and active worker
|
|
87
|
+
3. Executes completion via pipeline service (label transition + side effects)
|
|
88
|
+
4. Deactivates worker (sessions map preserved for reuse)
|
|
89
|
+
5. Sends notification
|
|
90
|
+
6. Ticks queue to fill free worker slots
|
|
91
|
+
7. Writes audit log
|
|
92
|
+
|
|
93
|
+
**Scheduling:** After completion, `work_finish` ticks the queue. The scheduler sees the new label (`To Test` or `To Improve`) and dispatches the next worker if a slot is free.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Task Management
|
|
98
|
+
|
|
99
|
+
### `task_create`
|
|
100
|
+
|
|
101
|
+
Create a new issue in the project's issue tracker.
|
|
102
|
+
|
|
103
|
+
**Source:** [`lib/tools/task-create.ts`](../lib/tools/task-create.ts)
|
|
104
|
+
|
|
105
|
+
**Parameters:**
|
|
106
|
+
|
|
107
|
+
| Parameter | Type | Required | Description |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `projectGroupId` | string | Yes | Project group ID |
|
|
110
|
+
| `title` | string | Yes | Issue title |
|
|
111
|
+
| `description` | string | No | Full issue body (markdown) |
|
|
112
|
+
| `label` | StateLabel | No | State label. Defaults to `"Planning"`. |
|
|
113
|
+
| `assignees` | string[] | No | GitHub/GitLab usernames to assign |
|
|
114
|
+
| `pickup` | boolean | No | If true, immediately pick up for DEV after creation |
|
|
115
|
+
|
|
116
|
+
**Use cases:**
|
|
117
|
+
|
|
118
|
+
- Orchestrator creates tasks from chat messages
|
|
119
|
+
- Workers file follow-up bugs discovered during development
|
|
120
|
+
- Breaking down epics into smaller tasks
|
|
121
|
+
|
|
122
|
+
**Default behavior:** Creates issues in `"Planning"` state. Only use `"To Do"` when the user explicitly requests immediate work.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### `task_update`
|
|
127
|
+
|
|
128
|
+
Change an issue's state label manually without going through the full pickup/complete flow.
|
|
129
|
+
|
|
130
|
+
**Source:** [`lib/tools/task-update.ts`](../lib/tools/task-update.ts)
|
|
131
|
+
|
|
132
|
+
**Parameters:**
|
|
133
|
+
|
|
134
|
+
| Parameter | Type | Required | Description |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| `projectGroupId` | string | Yes | Project group ID |
|
|
137
|
+
| `issueId` | number | Yes | Issue ID to update |
|
|
138
|
+
| `state` | StateLabel | Yes | New state label |
|
|
139
|
+
| `reason` | string | No | Audit log reason for the change |
|
|
140
|
+
|
|
141
|
+
**Valid states:** `Planning`, `To Do`, `Doing`, `To Test`, `Testing`, `Done`, `To Improve`, `Refining`
|
|
142
|
+
|
|
143
|
+
**Use cases:**
|
|
144
|
+
|
|
145
|
+
- Manual state adjustments (e.g. `Planning → To Do` after approval)
|
|
146
|
+
- Failed auto-transitions that need correction
|
|
147
|
+
- Bulk state changes by orchestrator
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `task_comment`
|
|
152
|
+
|
|
153
|
+
Add a comment to an issue for feedback, notes, or discussion.
|
|
154
|
+
|
|
155
|
+
**Source:** [`lib/tools/task-comment.ts`](../lib/tools/task-comment.ts)
|
|
156
|
+
|
|
157
|
+
**Parameters:**
|
|
158
|
+
|
|
159
|
+
| Parameter | Type | Required | Description |
|
|
160
|
+
|---|---|---|---|
|
|
161
|
+
| `projectGroupId` | string | Yes | Project group ID |
|
|
162
|
+
| `issueId` | number | Yes | Issue ID to comment on |
|
|
163
|
+
| `body` | string | Yes | Comment body (markdown) |
|
|
164
|
+
| `authorRole` | `"dev"` \| `"qa"` \| `"orchestrator"` | No | Attribution role prefix |
|
|
165
|
+
|
|
166
|
+
**Use cases:**
|
|
167
|
+
|
|
168
|
+
- QA adds review feedback before pass/fail decision
|
|
169
|
+
- DEV posts implementation notes or progress updates
|
|
170
|
+
- Orchestrator adds summary comments
|
|
171
|
+
|
|
172
|
+
When `authorRole` is provided, the comment is prefixed with a role emoji and attribution label.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Operations
|
|
177
|
+
|
|
178
|
+
### `status`
|
|
179
|
+
|
|
180
|
+
Lightweight queue + worker state dashboard.
|
|
181
|
+
|
|
182
|
+
**Source:** [`lib/tools/status.ts`](../lib/tools/status.ts)
|
|
183
|
+
|
|
184
|
+
**Context:** Auto-filters to project in group chats. Shows all projects in DMs.
|
|
185
|
+
|
|
186
|
+
**Parameters:**
|
|
187
|
+
|
|
188
|
+
| Parameter | Type | Required | Description |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| `projectGroupId` | string | No | Filter to specific project. Omit for all. |
|
|
191
|
+
|
|
192
|
+
**Returns per project:**
|
|
193
|
+
|
|
194
|
+
- Worker state: active/idle, current issue, level, start time
|
|
195
|
+
- Queue counts: To Do, To Test, To Improve
|
|
196
|
+
- Role execution mode
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### `health`
|
|
201
|
+
|
|
202
|
+
Worker health scan with optional auto-fix.
|
|
203
|
+
|
|
204
|
+
**Source:** [`lib/tools/health.ts`](../lib/tools/health.ts)
|
|
205
|
+
|
|
206
|
+
**Context:** Auto-filters to project in group chats.
|
|
207
|
+
|
|
208
|
+
**Parameters:**
|
|
209
|
+
|
|
210
|
+
| Parameter | Type | Required | Description |
|
|
211
|
+
|---|---|---|---|
|
|
212
|
+
| `projectGroupId` | string | No | Filter to specific project. Omit for all. |
|
|
213
|
+
| `fix` | boolean | No | Apply fixes for detected issues. Default: `false` (read-only). |
|
|
214
|
+
| `activeSessions` | string[] | No | Active session IDs for zombie detection. |
|
|
215
|
+
|
|
216
|
+
**Health checks:**
|
|
217
|
+
|
|
218
|
+
| Issue | Severity | Detection | Auto-fix |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| Active worker with no session key | Critical | `active=true` but no session in map | Deactivate worker |
|
|
221
|
+
| Active worker whose session is dead | Critical | Session key not in active sessions list | Deactivate worker, revert label |
|
|
222
|
+
| Worker active >2 hours | Warning | `startTime` older than 2h | Deactivate worker, revert label to queue |
|
|
223
|
+
| Inactive worker with lingering issue ID | Warning | `active=false` but `issueId` still set | Clear issueId |
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
### `work_heartbeat`
|
|
228
|
+
|
|
229
|
+
Manual trigger for heartbeat: health fix + queue dispatch. Same logic as the background heartbeat service, but invoked on demand.
|
|
230
|
+
|
|
231
|
+
**Source:** [`lib/tools/work-heartbeat.ts`](../lib/tools/work-heartbeat.ts)
|
|
232
|
+
|
|
233
|
+
**Parameters:**
|
|
234
|
+
|
|
235
|
+
| Parameter | Type | Required | Description |
|
|
236
|
+
|---|---|---|---|
|
|
237
|
+
| `projectGroupId` | string | No | Target single project. Omit for all. |
|
|
238
|
+
| `dryRun` | boolean | No | Report only, don't dispatch. Default: `false`. |
|
|
239
|
+
| `maxPickups` | number | No | Max worker dispatches per tick. |
|
|
240
|
+
| `activeSessions` | string[] | No | Active session IDs for zombie detection. |
|
|
241
|
+
|
|
242
|
+
**Two-pass sweep:**
|
|
243
|
+
|
|
244
|
+
1. **Health pass** — Runs `checkWorkerHealth` per project per role. Auto-fixes zombies, stale workers, orphaned state.
|
|
245
|
+
2. **Tick pass** — Calls `projectTick` per project. Fills free worker slots by priority (To Improve > To Test > To Do).
|
|
246
|
+
|
|
247
|
+
**Execution guards:**
|
|
248
|
+
|
|
249
|
+
- `projectExecution: "sequential"` — only one project active at a time
|
|
250
|
+
- `roleExecution: "sequential"` — only one role (DEV or QA) active at a time per project (enforced in `projectTick`)
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Setup
|
|
255
|
+
|
|
256
|
+
### `project_register`
|
|
257
|
+
|
|
258
|
+
One-time project setup. Creates state labels, scaffolds prompt files, adds project to state.
|
|
259
|
+
|
|
260
|
+
**Source:** [`lib/tools/project-register.ts`](../lib/tools/project-register.ts)
|
|
261
|
+
|
|
262
|
+
**Context:** Only works in the Telegram/WhatsApp group being registered.
|
|
263
|
+
|
|
264
|
+
**Parameters:**
|
|
265
|
+
|
|
266
|
+
| Parameter | Type | Required | Description |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| `projectGroupId` | string | No | Auto-detected from current group if omitted |
|
|
269
|
+
| `name` | string | Yes | Short project name (e.g. `my-webapp`) |
|
|
270
|
+
| `repo` | string | Yes | Path to git repo (e.g. `~/git/my-project`) |
|
|
271
|
+
| `groupName` | string | No | Display name. Defaults to `Project: {name}`. |
|
|
272
|
+
| `baseBranch` | string | Yes | Base branch for development |
|
|
273
|
+
| `deployBranch` | string | No | Deploy branch. Defaults to baseBranch. |
|
|
274
|
+
| `deployUrl` | string | No | Deployment URL |
|
|
275
|
+
| `roleExecution` | `"parallel"` \| `"sequential"` | No | DEV/QA parallelism. Default: `"parallel"`. |
|
|
276
|
+
|
|
277
|
+
**What it does atomically:**
|
|
278
|
+
|
|
279
|
+
1. Validates project not already registered
|
|
280
|
+
2. Resolves repo path, auto-detects GitHub/GitLab from git remote
|
|
281
|
+
3. Verifies provider health (CLI installed and authenticated)
|
|
282
|
+
4. Creates all 8 state labels (idempotent — safe to run again)
|
|
283
|
+
5. Adds project entry to `projects.json` with empty worker state
|
|
284
|
+
- DEV sessions: `{ junior: null, medior: null, senior: null }`
|
|
285
|
+
- QA sessions: `{ reviewer: null, tester: null }`
|
|
286
|
+
6. Scaffolds prompt files: `projects/roles/<project>/dev.md` and `qa.md`
|
|
287
|
+
7. Writes audit log
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### `setup`
|
|
292
|
+
|
|
293
|
+
Agent + workspace initialization.
|
|
294
|
+
|
|
295
|
+
**Source:** [`lib/tools/setup.ts`](../lib/tools/setup.ts)
|
|
296
|
+
|
|
297
|
+
**Parameters:**
|
|
298
|
+
|
|
299
|
+
| Parameter | Type | Required | Description |
|
|
300
|
+
|---|---|---|---|
|
|
301
|
+
| `newAgentName` | string | No | Create a new agent. Omit to configure current workspace. |
|
|
302
|
+
| `channelBinding` | `"telegram"` \| `"whatsapp"` | No | Channel to bind (with `newAgentName` only) |
|
|
303
|
+
| `migrateFrom` | string | No | Agent ID to migrate channel binding from |
|
|
304
|
+
| `models` | object | No | Model overrides per role and level (see [Configuration](CONFIGURATION.md#model-tiers)) |
|
|
305
|
+
| `projectExecution` | `"parallel"` \| `"sequential"` | No | Project execution mode |
|
|
306
|
+
|
|
307
|
+
**What it does:**
|
|
308
|
+
|
|
309
|
+
1. Creates a new agent or configures existing workspace
|
|
310
|
+
2. Optionally binds messaging channel (Telegram/WhatsApp)
|
|
311
|
+
3. Optionally migrates channel binding from another agent
|
|
312
|
+
4. Writes workspace files: AGENTS.md, HEARTBEAT.md, `projects/projects.json`
|
|
313
|
+
5. Configures model tiers in `openclaw.json`
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
### `onboard`
|
|
318
|
+
|
|
319
|
+
Conversational onboarding guide. Returns step-by-step instructions for the agent to walk the user through setup.
|
|
320
|
+
|
|
321
|
+
**Source:** [`lib/tools/onboard.ts`](../lib/tools/onboard.ts)
|
|
322
|
+
|
|
323
|
+
**Note:** Call this before `setup` to get step-by-step guidance.
|
|
324
|
+
|
|
325
|
+
**Parameters:**
|
|
326
|
+
|
|
327
|
+
| Parameter | Type | Required | Description |
|
|
328
|
+
|---|---|---|---|
|
|
329
|
+
| `mode` | `"first-run"` \| `"reconfigure"` | No | Auto-detected from current state |
|
|
330
|
+
|
|
331
|
+
**Flow:**
|
|
332
|
+
|
|
333
|
+
1. Call `onboard` — returns QA-style step-by-step instructions
|
|
334
|
+
2. Agent walks user through: agent selection, channel binding, model tiers
|
|
335
|
+
3. Agent calls `setup` with collected answers
|
|
336
|
+
4. User registers projects via `project_register` in group chats
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Completion Rules Reference
|
|
341
|
+
|
|
342
|
+
The pipeline service (`lib/services/pipeline.ts`) defines declarative completion rules:
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
dev:done → Doing → To Test (git pull, detect PR)
|
|
346
|
+
dev:blocked → Doing → To Do (return to queue)
|
|
347
|
+
qa:pass → Testing → Done (close issue)
|
|
348
|
+
qa:fail → Testing → To Improve (reopen issue)
|
|
349
|
+
qa:refine → Testing → Refining (await human decision)
|
|
350
|
+
qa:blocked → Testing → To Test (return to QA queue)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Issue Priority Order
|
|
354
|
+
|
|
355
|
+
When the heartbeat or `work_heartbeat` fills free worker slots, issues are prioritized:
|
|
356
|
+
|
|
357
|
+
1. **To Improve** — QA failures get fixed first (highest priority)
|
|
358
|
+
2. **To Test** — Completed DEV work gets reviewed next
|
|
359
|
+
3. **To Do** — Fresh tasks are picked up last
|
|
360
|
+
|
|
361
|
+
This ensures the pipeline clears its backlog before starting new work.
|