@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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/index.d.ts +88 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +107 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/audit.d.ts +2 -0
  8. package/dist/lib/audit.d.ts.map +1 -0
  9. package/dist/lib/audit.js +42 -0
  10. package/dist/lib/audit.js.map +1 -0
  11. package/dist/lib/binding-manager.d.ts +35 -0
  12. package/dist/lib/binding-manager.d.ts.map +1 -0
  13. package/dist/lib/binding-manager.js +88 -0
  14. package/dist/lib/binding-manager.js.map +1 -0
  15. package/dist/lib/cli.d.ts +12 -0
  16. package/dist/lib/cli.d.ts.map +1 -0
  17. package/dist/lib/cli.js +69 -0
  18. package/dist/lib/cli.js.map +1 -0
  19. package/dist/lib/dispatch.d.ts +58 -0
  20. package/dist/lib/dispatch.d.ts.map +1 -0
  21. package/dist/lib/dispatch.js +163 -0
  22. package/dist/lib/dispatch.js.map +1 -0
  23. package/dist/lib/model-selector.d.ts +21 -0
  24. package/dist/lib/model-selector.d.ts.map +1 -0
  25. package/dist/lib/model-selector.js +74 -0
  26. package/dist/lib/model-selector.js.map +1 -0
  27. package/dist/lib/notify.d.ts +54 -0
  28. package/dist/lib/notify.d.ts.map +1 -0
  29. package/dist/lib/notify.js +143 -0
  30. package/dist/lib/notify.js.map +1 -0
  31. package/dist/lib/onboarding.d.ts +5 -0
  32. package/dist/lib/onboarding.d.ts.map +1 -0
  33. package/dist/lib/onboarding.js +124 -0
  34. package/dist/lib/onboarding.js.map +1 -0
  35. package/dist/lib/projects.d.ts +64 -0
  36. package/dist/lib/projects.d.ts.map +1 -0
  37. package/dist/lib/projects.js +127 -0
  38. package/dist/lib/projects.js.map +1 -0
  39. package/dist/lib/providers/github.d.ts +23 -0
  40. package/dist/lib/providers/github.d.ts.map +1 -0
  41. package/dist/lib/providers/github.js +130 -0
  42. package/dist/lib/providers/github.js.map +1 -0
  43. package/dist/lib/providers/gitlab.d.ts +23 -0
  44. package/dist/lib/providers/gitlab.d.ts.map +1 -0
  45. package/dist/lib/providers/gitlab.js +133 -0
  46. package/dist/lib/providers/gitlab.js.map +1 -0
  47. package/dist/lib/providers/index.d.ts +12 -0
  48. package/dist/lib/providers/index.d.ts.map +1 -0
  49. package/dist/lib/providers/index.js +25 -0
  50. package/dist/lib/providers/index.js.map +1 -0
  51. package/dist/lib/providers/provider.d.ts +35 -0
  52. package/dist/lib/providers/provider.d.ts.map +1 -0
  53. package/dist/lib/providers/provider.js +13 -0
  54. package/dist/lib/providers/provider.js.map +1 -0
  55. package/dist/lib/services/health.d.ts +38 -0
  56. package/dist/lib/services/health.d.ts.map +1 -0
  57. package/dist/lib/services/health.js +100 -0
  58. package/dist/lib/services/health.js.map +1 -0
  59. package/dist/lib/services/heartbeat.d.ts +38 -0
  60. package/dist/lib/services/heartbeat.d.ts.map +1 -0
  61. package/dist/lib/services/heartbeat.js +199 -0
  62. package/dist/lib/services/heartbeat.js.map +1 -0
  63. package/dist/lib/services/pipeline.d.ts +36 -0
  64. package/dist/lib/services/pipeline.d.ts.map +1 -0
  65. package/dist/lib/services/pipeline.js +90 -0
  66. package/dist/lib/services/pipeline.js.map +1 -0
  67. package/dist/lib/services/queue.d.ts +14 -0
  68. package/dist/lib/services/queue.d.ts.map +1 -0
  69. package/dist/lib/services/queue.js +31 -0
  70. package/dist/lib/services/queue.js.map +1 -0
  71. package/dist/lib/services/tick.d.ts +62 -0
  72. package/dist/lib/services/tick.d.ts.map +1 -0
  73. package/dist/lib/services/tick.js +160 -0
  74. package/dist/lib/services/tick.js.map +1 -0
  75. package/dist/lib/setup/agent.d.ts +14 -0
  76. package/dist/lib/setup/agent.d.ts.map +1 -0
  77. package/dist/lib/setup/agent.js +72 -0
  78. package/dist/lib/setup/agent.js.map +1 -0
  79. package/dist/lib/setup/config.d.ts +22 -0
  80. package/dist/lib/setup/config.d.ts.map +1 -0
  81. package/dist/lib/setup/config.js +67 -0
  82. package/dist/lib/setup/config.js.map +1 -0
  83. package/dist/lib/setup/index.d.ts +53 -0
  84. package/dist/lib/setup/index.d.ts.map +1 -0
  85. package/dist/lib/setup/index.js +68 -0
  86. package/dist/lib/setup/index.js.map +1 -0
  87. package/dist/lib/setup/workspace.d.ts +6 -0
  88. package/dist/lib/setup/workspace.d.ts.map +1 -0
  89. package/dist/lib/setup/workspace.js +69 -0
  90. package/dist/lib/setup/workspace.js.map +1 -0
  91. package/dist/lib/templates.d.ts +9 -0
  92. package/dist/lib/templates.d.ts.map +1 -0
  93. package/dist/lib/templates.js +163 -0
  94. package/dist/lib/templates.js.map +1 -0
  95. package/dist/lib/tiers.d.ts +55 -0
  96. package/dist/lib/tiers.d.ts.map +1 -0
  97. package/dist/lib/tiers.js +74 -0
  98. package/dist/lib/tiers.js.map +1 -0
  99. package/dist/lib/tool-helpers.d.ts +44 -0
  100. package/dist/lib/tool-helpers.d.ts.map +1 -0
  101. package/dist/lib/tool-helpers.js +65 -0
  102. package/dist/lib/tool-helpers.js.map +1 -0
  103. package/dist/lib/tools/health.d.ts +28 -0
  104. package/dist/lib/tools/health.d.ts.map +1 -0
  105. package/dist/lib/tools/health.js +61 -0
  106. package/dist/lib/tools/health.js.map +1 -0
  107. package/dist/lib/tools/onboard.d.ts +24 -0
  108. package/dist/lib/tools/onboard.d.ts.map +1 -0
  109. package/dist/lib/tools/onboard.js +27 -0
  110. package/dist/lib/tools/onboard.js.map +1 -0
  111. package/dist/lib/tools/project-register.d.ts +51 -0
  112. package/dist/lib/tools/project-register.d.ts.map +1 -0
  113. package/dist/lib/tools/project-register.js +172 -0
  114. package/dist/lib/tools/project-register.js.map +1 -0
  115. package/dist/lib/tools/queue-status.test.d.ts +2 -0
  116. package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
  117. package/dist/lib/tools/queue-status.test.js +48 -0
  118. package/dist/lib/tools/queue-status.test.js.map +1 -0
  119. package/dist/lib/tools/setup.d.ts +76 -0
  120. package/dist/lib/tools/setup.d.ts.map +1 -0
  121. package/dist/lib/tools/setup.js +102 -0
  122. package/dist/lib/tools/setup.js.map +1 -0
  123. package/dist/lib/tools/status.d.ts +24 -0
  124. package/dist/lib/tools/status.d.ts.map +1 -0
  125. package/dist/lib/tools/status.js +53 -0
  126. package/dist/lib/tools/status.js.map +1 -0
  127. package/dist/lib/tools/task-comment.d.ts +40 -0
  128. package/dist/lib/tools/task-comment.d.ts.map +1 -0
  129. package/dist/lib/tools/task-comment.js +84 -0
  130. package/dist/lib/tools/task-comment.js.map +1 -0
  131. package/dist/lib/tools/task-create.d.ts +54 -0
  132. package/dist/lib/tools/task-create.d.ts.map +1 -0
  133. package/dist/lib/tools/task-create.js +77 -0
  134. package/dist/lib/tools/task-create.js.map +1 -0
  135. package/dist/lib/tools/task-update.d.ts +40 -0
  136. package/dist/lib/tools/task-update.d.ts.map +1 -0
  137. package/dist/lib/tools/task-update.js +79 -0
  138. package/dist/lib/tools/task-update.js.map +1 -0
  139. package/dist/lib/tools/task-update.test.d.ts +7 -0
  140. package/dist/lib/tools/task-update.test.d.ts.map +1 -0
  141. package/dist/lib/tools/task-update.test.js +55 -0
  142. package/dist/lib/tools/task-update.test.js.map +1 -0
  143. package/dist/lib/tools/work-finish.d.ts +43 -0
  144. package/dist/lib/tools/work-finish.d.ts.map +1 -0
  145. package/dist/lib/tools/work-finish.js +77 -0
  146. package/dist/lib/tools/work-finish.js.map +1 -0
  147. package/dist/lib/tools/work-start.d.ts +39 -0
  148. package/dist/lib/tools/work-start.d.ts.map +1 -0
  149. package/dist/lib/tools/work-start.js +129 -0
  150. package/dist/lib/tools/work-start.js.map +1 -0
  151. package/dist/lib/types.d.ts +17 -0
  152. package/dist/lib/types.d.ts.map +1 -0
  153. package/dist/lib/types.js +8 -0
  154. package/dist/lib/types.js.map +1 -0
  155. package/docs/ARCHITECTURE.md +662 -0
  156. package/docs/CONFIGURATION.md +336 -0
  157. package/docs/MANAGEMENT.md +120 -0
  158. package/docs/ONBOARDING.md +251 -0
  159. package/docs/QA_WORKFLOW.md +120 -0
  160. package/docs/ROADMAP.md +96 -0
  161. package/docs/TESTING.md +339 -0
  162. package/docs/TOOLS.md +361 -0
  163. package/package.json +55 -0
@@ -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.