oh-my-claude-sisyphus 3.5.7 → 3.6.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/agents/executor-high.md +2 -0
- package/agents/executor-low.md +2 -0
- package/agents/executor.md +2 -0
- package/agents/templates/base-agent.md +9 -0
- package/commands/cancel.md +8 -8
- package/commands/swarm.md +350 -148
- package/dist/__tests__/hooks/auto-slash-command/executor.test.d.ts +7 -0
- package/dist/__tests__/hooks/auto-slash-command/executor.test.d.ts.map +1 -0
- package/dist/__tests__/hooks/auto-slash-command/executor.test.js +374 -0
- package/dist/__tests__/hooks/auto-slash-command/executor.test.js.map +1 -0
- package/dist/__tests__/hooks/learner/bridge.test.d.ts +11 -0
- package/dist/__tests__/hooks/learner/bridge.test.d.ts.map +1 -0
- package/dist/__tests__/hooks/learner/bridge.test.js +199 -0
- package/dist/__tests__/hooks/learner/bridge.test.js.map +1 -0
- package/dist/__tests__/hooks.test.js +10 -9
- package/dist/__tests__/hooks.test.js.map +1 -1
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/agents/codex-agents.d.ts +20 -0
- package/dist/agents/codex-agents.d.ts.map +1 -0
- package/dist/agents/codex-agents.js +36 -0
- package/dist/agents/codex-agents.js.map +1 -0
- package/dist/agents/preamble.d.ts +14 -0
- package/dist/agents/preamble.d.ts.map +1 -0
- package/dist/agents/preamble.js +26 -0
- package/dist/agents/preamble.js.map +1 -0
- package/dist/hooks/autopilot/__tests__/cancel.test.js +14 -4
- package/dist/hooks/autopilot/__tests__/cancel.test.js.map +1 -1
- package/dist/hooks/autopilot/__tests__/state.test.js +1 -0
- package/dist/hooks/autopilot/__tests__/state.test.js.map +1 -1
- package/dist/hooks/autopilot/__tests__/summary.test.js +38 -3
- package/dist/hooks/autopilot/__tests__/summary.test.js.map +1 -1
- package/dist/hooks/autopilot/state.d.ts +1 -1
- package/dist/hooks/autopilot/state.d.ts.map +1 -1
- package/dist/hooks/autopilot/state.js +15 -8
- package/dist/hooks/autopilot/state.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/learner/bridge.d.ts +71 -0
- package/dist/hooks/learner/bridge.d.ts.map +1 -0
- package/dist/hooks/learner/bridge.js +426 -0
- package/dist/hooks/learner/bridge.js.map +1 -0
- package/dist/hooks/mode-registry/index.d.ts +135 -0
- package/dist/hooks/mode-registry/index.d.ts.map +1 -0
- package/dist/hooks/mode-registry/index.js +445 -0
- package/dist/hooks/mode-registry/index.js.map +1 -0
- package/dist/hooks/mode-registry/types.d.ts +31 -0
- package/dist/hooks/mode-registry/types.d.ts.map +1 -0
- package/dist/hooks/mode-registry/types.js +7 -0
- package/dist/hooks/mode-registry/types.js.map +1 -0
- package/dist/hooks/ralph/loop.js +6 -6
- package/dist/hooks/ralph/loop.js.map +1 -1
- package/dist/hooks/skill-bridge.cjs +349 -0
- package/dist/hooks/swarm/__tests__/claiming.test.d.ts +2 -0
- package/dist/hooks/swarm/__tests__/claiming.test.d.ts.map +1 -0
- package/dist/hooks/swarm/__tests__/claiming.test.js +170 -0
- package/dist/hooks/swarm/__tests__/claiming.test.js.map +1 -0
- package/dist/hooks/swarm/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/swarm/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/swarm/__tests__/index.test.js +157 -0
- package/dist/hooks/swarm/__tests__/index.test.js.map +1 -0
- package/dist/hooks/swarm/__tests__/mode-registry.test.d.ts +2 -0
- package/dist/hooks/swarm/__tests__/mode-registry.test.d.ts.map +1 -0
- package/dist/hooks/swarm/__tests__/mode-registry.test.js +177 -0
- package/dist/hooks/swarm/__tests__/mode-registry.test.js.map +1 -0
- package/dist/hooks/swarm/claiming.d.ts +101 -0
- package/dist/hooks/swarm/claiming.d.ts.map +1 -0
- package/dist/hooks/swarm/claiming.js +460 -0
- package/dist/hooks/swarm/claiming.js.map +1 -0
- package/dist/hooks/swarm/index.d.ts +221 -0
- package/dist/hooks/swarm/index.d.ts.map +1 -0
- package/dist/hooks/swarm/index.js +413 -0
- package/dist/hooks/swarm/index.js.map +1 -0
- package/dist/hooks/swarm/state.d.ts +94 -0
- package/dist/hooks/swarm/state.d.ts.map +1 -0
- package/dist/hooks/swarm/state.js +530 -0
- package/dist/hooks/swarm/state.js.map +1 -0
- package/dist/hooks/swarm/types.d.ts +116 -0
- package/dist/hooks/swarm/types.d.ts.map +1 -0
- package/dist/hooks/swarm/types.js +22 -0
- package/dist/hooks/swarm/types.js.map +1 -0
- package/dist/hooks/ultrapilot/decomposer.d.ts +141 -0
- package/dist/hooks/ultrapilot/decomposer.d.ts.map +1 -0
- package/dist/hooks/ultrapilot/decomposer.js +377 -0
- package/dist/hooks/ultrapilot/decomposer.js.map +1 -0
- package/dist/hooks/ultrapilot/index.d.ts +31 -0
- package/dist/hooks/ultrapilot/index.d.ts.map +1 -1
- package/dist/hooks/ultrapilot/index.js +43 -2
- package/dist/hooks/ultrapilot/index.js.map +1 -1
- package/dist/hooks/ultrapilot/state.d.ts +1 -1
- package/dist/hooks/ultrapilot/state.d.ts.map +1 -1
- package/dist/hooks/ultrapilot/state.js +7 -0
- package/dist/hooks/ultrapilot/state.js.map +1 -1
- package/dist/hooks/ultraqa/index.js +5 -5
- package/dist/hooks/ultraqa/index.js.map +1 -1
- package/dist/hooks/ultrawork/index.js +3 -3
- package/dist/hooks/ultrawork/index.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.js +1 -1
- package/package.json +6 -2
- package/scripts/build-skill-bridge.mjs +32 -0
- package/scripts/skill-injector.mjs +77 -26
- package/skills/autopilot/SKILL.md +18 -0
- package/skills/cancel/SKILL.md +166 -141
- package/skills/ecomode/SKILL.md +14 -0
- package/skills/pipeline/SKILL.md +13 -0
- package/skills/ralph/SKILL.md +22 -1
- package/skills/swarm/SKILL.md +521 -197
- package/skills/ultrapilot/SKILL.md +82 -13
- package/skills/ultraqa/SKILL.md +13 -0
- package/skills/ultrawork/SKILL.md +14 -0
package/commands/swarm.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: N coordinated agents
|
|
2
|
+
description: N coordinated agents on shared task list with SQLite-based atomic claiming
|
|
3
3
|
aliases: [swarm-agents]
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ aliases: [swarm-agents]
|
|
|
7
7
|
|
|
8
8
|
[SWARM MODE ACTIVATED]
|
|
9
9
|
|
|
10
|
-
Spawn N coordinated agents working on a shared task list with atomic claiming. Like a dev team tackling multiple files in parallel.
|
|
10
|
+
Spawn N coordinated agents working on a shared task list with SQLite-based atomic claiming. Like a dev team tackling multiple files in parallel—fast, reliable, and with full fault tolerance.
|
|
11
11
|
|
|
12
12
|
## User's Request
|
|
13
13
|
|
|
@@ -22,22 +22,22 @@ Spawn N coordinated agents working on a shared task list with atomic claiming. L
|
|
|
22
22
|
### Parameters
|
|
23
23
|
|
|
24
24
|
- **N** - Number of agents (1-5, enforced by Claude Code limit)
|
|
25
|
-
- **agent-type** - Agent to spawn (executor, build-fixer,
|
|
25
|
+
- **agent-type** - Agent to spawn (e.g., executor, build-fixer, architect)
|
|
26
26
|
- **task** - High-level task to decompose and distribute
|
|
27
27
|
|
|
28
28
|
### Examples
|
|
29
29
|
|
|
30
|
-
```
|
|
30
|
+
```bash
|
|
31
31
|
/oh-my-claudecode:swarm 5:executor "fix all TypeScript errors"
|
|
32
32
|
/oh-my-claudecode:swarm 3:build-fixer "fix build errors in src/"
|
|
33
33
|
/oh-my-claudecode:swarm 4:designer "implement responsive layouts for all components"
|
|
34
34
|
/oh-my-claudecode:swarm 2:architect "analyze and document all API endpoints"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Architecture
|
|
38
38
|
|
|
39
39
|
```
|
|
40
|
-
User: /swarm 5:executor
|
|
40
|
+
User: "/swarm 5:executor fix all TypeScript errors"
|
|
41
41
|
|
|
|
42
42
|
v
|
|
43
43
|
[SWARM ORCHESTRATOR]
|
|
@@ -50,14 +50,29 @@ User: /swarm 5:executor "fix all TypeScript errors"
|
|
|
50
50
|
+--+--+--+--+
|
|
51
51
|
|
|
|
52
52
|
v
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
[SQLITE DATABASE]
|
|
54
|
+
┌─────────────────────┐
|
|
55
|
+
│ tasks table │
|
|
56
|
+
├─────────────────────┤
|
|
57
|
+
│ id, description │
|
|
58
|
+
│ status (pending, │
|
|
59
|
+
│ claimed, done, │
|
|
60
|
+
│ failed) │
|
|
61
|
+
│ claimed_by, claimed_at
|
|
62
|
+
│ completed_at, result│
|
|
63
|
+
│ error │
|
|
64
|
+
├─────────────────────┤
|
|
65
|
+
│ heartbeats table │
|
|
66
|
+
│ (agent monitoring) │
|
|
67
|
+
└─────────────────────┘
|
|
59
68
|
```
|
|
60
69
|
|
|
70
|
+
**Key Features:**
|
|
71
|
+
- SQLite transactions ensure only one agent can claim a task
|
|
72
|
+
- Lease-based ownership with automatic timeout and recovery
|
|
73
|
+
- Heartbeat monitoring for detecting dead agents
|
|
74
|
+
- Full ACID compliance for task state
|
|
75
|
+
|
|
61
76
|
## Workflow
|
|
62
77
|
|
|
63
78
|
### 1. Parse Input
|
|
@@ -67,161 +82,329 @@ From `{{ARGUMENTS}}`, extract:
|
|
|
67
82
|
- agent-type (executor, build-fixer, etc.)
|
|
68
83
|
- task description
|
|
69
84
|
|
|
70
|
-
### 2. Create Task
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
4. Each task gets: id, file, description, status, owner, timestamp
|
|
85
|
+
### 2. Create Task Pool
|
|
86
|
+
- Analyze codebase based on task
|
|
87
|
+
- Break into file-specific subtasks
|
|
88
|
+
- Initialize SQLite database with task pool
|
|
89
|
+
- Each task gets: id, description, status (pending), and metadata columns
|
|
76
90
|
|
|
77
91
|
### 3. Spawn Agents
|
|
78
|
-
|
|
79
|
-
Launch N agents via Task tool:
|
|
92
|
+
- Launch N agents via Task tool
|
|
80
93
|
- Set `run_in_background: true` for all
|
|
81
|
-
- Each agent
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
- Each agent connects to the SQLite database
|
|
95
|
+
- Agents enter claiming loop automatically
|
|
96
|
+
|
|
97
|
+
**Important:** Use worker preamble when spawning agents to prevent sub-agent recursion:
|
|
85
98
|
|
|
86
|
-
|
|
99
|
+
```typescript
|
|
100
|
+
import { wrapWithPreamble } from '../agents/preamble.js';
|
|
87
101
|
|
|
102
|
+
const prompt = wrapWithPreamble(`Your task: ${taskDescription}`);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Task Claiming Protocol (SQLite Transactional)
|
|
88
106
|
Each agent follows this loop:
|
|
89
107
|
|
|
90
108
|
```
|
|
91
109
|
LOOP:
|
|
92
|
-
1.
|
|
93
|
-
2.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
110
|
+
1. Call claimTask(agentId)
|
|
111
|
+
2. SQLite transaction:
|
|
112
|
+
- Find first pending task
|
|
113
|
+
- UPDATE status='claimed', claimed_by=agentId, claimed_at=now
|
|
114
|
+
- INSERT/UPDATE heartbeat record
|
|
115
|
+
- Atomically commit (only one agent succeeds)
|
|
116
|
+
3. Execute task
|
|
117
|
+
4. Call completeTask(agentId, taskId, result) or failTask()
|
|
118
|
+
5. GOTO LOOP (until hasPendingWork() returns false)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Atomic Claiming Details:**
|
|
122
|
+
- SQLite `IMMEDIATE` transaction prevents race conditions
|
|
123
|
+
- Only agent updating the row successfully gets the task
|
|
124
|
+
- Heartbeat automatically updated on claim
|
|
125
|
+
- If claim fails (already claimed), agent retries with next task
|
|
126
|
+
- Lease Timeout: 5 minutes per task
|
|
127
|
+
- If timeout exceeded + no heartbeat, cleanupStaleClaims releases task back to pending
|
|
128
|
+
|
|
129
|
+
### 5. Heartbeat Protocol
|
|
130
|
+
- Agents call `heartbeat(agentId)` every 60 seconds (or custom interval)
|
|
131
|
+
- Heartbeat records: agent_id, last_heartbeat timestamp, current_task_id
|
|
132
|
+
- Orchestrator runs cleanupStaleClaims every 60 seconds
|
|
133
|
+
- If heartbeat is stale (>5 minutes old) and task claimed, task auto-releases
|
|
134
|
+
|
|
135
|
+
### 6. Progress Tracking
|
|
136
|
+
- Orchestrator monitors via TaskOutput
|
|
137
|
+
- Shows live progress: pending/claimed/done/failed counts
|
|
138
|
+
- Active agent count via getActiveAgents()
|
|
139
|
+
- Reports which agent is working on which task via getAgentTasks()
|
|
140
|
+
- Detects idle agents (all tasks claimed by others)
|
|
141
|
+
|
|
142
|
+
### 7. Completion
|
|
143
|
+
Exit when ANY of:
|
|
144
|
+
- isSwarmComplete() returns true (all tasks done or failed)
|
|
145
|
+
- All agents idle (no pending tasks, no claimed tasks)
|
|
146
|
+
- User cancels via `/oh-my-claudecode:cancel`
|
|
147
|
+
|
|
148
|
+
## Storage
|
|
149
|
+
|
|
150
|
+
### SQLite Database (`.omc/state/swarm.db`)
|
|
151
|
+
|
|
152
|
+
The swarm uses a single SQLite database stored at `.omc/state/swarm.db`. This provides:
|
|
153
|
+
- **ACID compliance** - All task state transitions are atomic
|
|
154
|
+
- **Concurrent access** - Multiple agents query/update safely
|
|
155
|
+
- **Persistence** - State survives agent crashes
|
|
156
|
+
- **Query efficiency** - Fast status lookups and filtering
|
|
157
|
+
|
|
158
|
+
#### `tasks` Table Schema
|
|
159
|
+
```sql
|
|
160
|
+
CREATE TABLE tasks (
|
|
161
|
+
id TEXT PRIMARY KEY,
|
|
162
|
+
description TEXT NOT NULL,
|
|
163
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
164
|
+
-- pending: waiting to be claimed
|
|
165
|
+
-- claimed: claimed by an agent, in progress
|
|
166
|
+
-- done: completed successfully
|
|
167
|
+
-- failed: completed with error
|
|
168
|
+
claimed_by TEXT, -- agent ID that claimed this task
|
|
169
|
+
claimed_at INTEGER, -- Unix timestamp when claimed
|
|
170
|
+
completed_at INTEGER, -- Unix timestamp when completed
|
|
171
|
+
result TEXT, -- Optional result/output from task
|
|
172
|
+
error TEXT -- Error message if task failed
|
|
173
|
+
);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `heartbeats` Table Schema
|
|
177
|
+
```sql
|
|
178
|
+
CREATE TABLE heartbeats (
|
|
179
|
+
agent_id TEXT PRIMARY KEY,
|
|
180
|
+
last_heartbeat INTEGER NOT NULL, -- Unix timestamp of last heartbeat
|
|
181
|
+
current_task_id TEXT -- Task agent is currently working on
|
|
182
|
+
);
|
|
98
183
|
```
|
|
99
184
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
###
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
185
|
+
#### `swarm_session` Table Schema
|
|
186
|
+
```sql
|
|
187
|
+
CREATE TABLE swarm_session (
|
|
188
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
189
|
+
session_id TEXT NOT NULL,
|
|
190
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
191
|
+
agent_count INTEGER NOT NULL,
|
|
192
|
+
started_at INTEGER NOT NULL,
|
|
193
|
+
completed_at INTEGER
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Task Claiming Protocol (Detailed)
|
|
198
|
+
|
|
199
|
+
### Atomic Claim Operation with SQLite
|
|
200
|
+
|
|
201
|
+
The core strength of the implementation is transactional atomicity:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
function claimTask(agentId: string): ClaimResult {
|
|
205
|
+
// Transaction ensures only ONE agent succeeds
|
|
206
|
+
const claimTransaction = db.transaction(() => {
|
|
207
|
+
// Step 1: Find first pending task
|
|
208
|
+
const task = db.prepare(
|
|
209
|
+
'SELECT id, description FROM tasks WHERE status = "pending" ORDER BY id LIMIT 1'
|
|
210
|
+
).get();
|
|
211
|
+
|
|
212
|
+
if (!task) {
|
|
213
|
+
return { success: false, reason: 'No pending tasks' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Step 2: Attempt claim (will only succeed if status is still 'pending')
|
|
217
|
+
const result = db.prepare(
|
|
218
|
+
'UPDATE tasks SET status = "claimed", claimed_by = ?, claimed_at = ? WHERE id = ? AND status = "pending"'
|
|
219
|
+
).run(agentId, Date.now(), task.id);
|
|
220
|
+
|
|
221
|
+
if (result.changes === 0) {
|
|
222
|
+
// Another agent claimed it between SELECT and UPDATE - try next
|
|
223
|
+
return { success: false, reason: 'Task was claimed by another agent' };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Step 3: Update heartbeat to show we're alive and working
|
|
227
|
+
db.prepare(
|
|
228
|
+
'INSERT OR REPLACE INTO heartbeats (agent_id, last_heartbeat, current_task_id) VALUES (?, ?, ?)'
|
|
229
|
+
).run(agentId, Date.now(), task.id);
|
|
230
|
+
|
|
231
|
+
return { success: true, taskId: task.id, description: task.description };
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return claimTransaction(); // Atomic execution
|
|
138
235
|
}
|
|
139
236
|
```
|
|
140
237
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
238
|
+
**Why SQLite Transactions Work:**
|
|
239
|
+
- `db.transaction()` uses `IMMEDIATE` locking
|
|
240
|
+
- Prevents other agents from modifying rows between SELECT and UPDATE
|
|
241
|
+
- All-or-nothing atomicity: claim succeeds completely or fails completely
|
|
242
|
+
- No race conditions, no lost updates
|
|
243
|
+
|
|
244
|
+
### Lease Timeout & Auto-Release
|
|
245
|
+
|
|
246
|
+
Tasks are automatically released if claimed too long without heartbeat:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
function cleanupStaleClaims(leaseTimeout: number = 5 * 60 * 1000) {
|
|
250
|
+
// Default 5-minute timeout
|
|
251
|
+
const cutoffTime = Date.now() - leaseTimeout;
|
|
252
|
+
|
|
253
|
+
const cleanupTransaction = db.transaction(() => {
|
|
254
|
+
// Find claimed tasks where:
|
|
255
|
+
// 1. Claimed longer than timeout, OR
|
|
256
|
+
// 2. Agent hasn't sent heartbeat in that time
|
|
257
|
+
const staleTasks = db.prepare(`
|
|
258
|
+
SELECT t.id
|
|
259
|
+
FROM tasks t
|
|
260
|
+
LEFT JOIN heartbeats h ON t.claimed_by = h.agent_id
|
|
261
|
+
WHERE t.status = 'claimed'
|
|
262
|
+
AND t.claimed_at < ?
|
|
263
|
+
AND (h.last_heartbeat IS NULL OR h.last_heartbeat < ?)
|
|
264
|
+
`).all(cutoffTime, cutoffTime);
|
|
265
|
+
|
|
266
|
+
// Release each stale task back to pending
|
|
267
|
+
for (const staleTask of staleTasks) {
|
|
268
|
+
db.prepare('UPDATE tasks SET status = "pending", claimed_by = NULL, claimed_at = NULL WHERE id = ?')
|
|
269
|
+
.run(staleTask.id);
|
|
171
270
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
"done": 2
|
|
178
|
-
}
|
|
271
|
+
|
|
272
|
+
return staleTasks.length;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return cleanupTransaction();
|
|
179
276
|
}
|
|
180
277
|
```
|
|
181
278
|
|
|
182
|
-
|
|
279
|
+
**How Recovery Works:**
|
|
280
|
+
1. Orchestrator calls cleanupStaleClaims() every 60 seconds
|
|
281
|
+
2. If agent hasn't sent heartbeat in 5 minutes, task is auto-released
|
|
282
|
+
3. Another agent picks up the orphaned task
|
|
283
|
+
4. Original agent can continue working (it doesn't know it was released)
|
|
284
|
+
5. When original agent tries to mark task as done, verification fails safely
|
|
285
|
+
|
|
286
|
+
## API Reference
|
|
287
|
+
|
|
288
|
+
### Core API Functions
|
|
289
|
+
|
|
290
|
+
#### `startSwarm(config: SwarmConfig): Promise<boolean>`
|
|
291
|
+
Initialize the swarm with task pool and start cleanup timer.
|
|
292
|
+
|
|
293
|
+
#### `stopSwarm(deleteDatabase?: boolean): boolean`
|
|
294
|
+
Stop the swarm and optionally delete the database.
|
|
295
|
+
|
|
296
|
+
#### `claimTask(agentId: string): ClaimResult`
|
|
297
|
+
Claim the next pending task. Returns `{ success, taskId, description, reason }`.
|
|
298
|
+
|
|
299
|
+
#### `completeTask(agentId: string, taskId: string, result?: string): boolean`
|
|
300
|
+
Mark a task as done. Only succeeds if agent still owns the task.
|
|
183
301
|
|
|
184
|
-
|
|
302
|
+
#### `failTask(agentId: string, taskId: string, error: string): boolean`
|
|
303
|
+
Mark a task as failed with error details.
|
|
185
304
|
|
|
186
|
-
|
|
187
|
-
|
|
305
|
+
#### `heartbeat(agentId: string): boolean`
|
|
306
|
+
Send a heartbeat to indicate agent is alive. Call every 60 seconds during long-running tasks.
|
|
188
307
|
|
|
189
|
-
|
|
308
|
+
#### `cleanupStaleClaims(leaseTimeout?: number): number`
|
|
309
|
+
Manually trigger cleanup of expired claims. Called automatically every 60 seconds.
|
|
190
310
|
|
|
191
|
-
|
|
311
|
+
#### `hasPendingWork(): boolean`
|
|
312
|
+
Check if there are unclaimed tasks available.
|
|
192
313
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
2. Find first task with status="pending"
|
|
196
|
-
3. Claim it atomically (set status="claimed", owner="{id}", timeout)
|
|
197
|
-
4. Execute the task
|
|
198
|
-
5. Mark status="done", set completed_at
|
|
199
|
-
6. Repeat until no pending tasks
|
|
314
|
+
#### `isSwarmComplete(): boolean`
|
|
315
|
+
Check if all tasks are done or failed.
|
|
200
316
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
- Update status="claimed", add your ID
|
|
204
|
-
- Set timeout_at = now + 5 minutes
|
|
205
|
-
- Write file back
|
|
206
|
-
- If file changed between read/write, retry
|
|
317
|
+
#### `getSwarmStats(): SwarmStats | null`
|
|
318
|
+
Get task counts and timing info.
|
|
207
319
|
|
|
208
|
-
|
|
209
|
-
|
|
320
|
+
### Configuration (SwarmConfig)
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
interface SwarmConfig {
|
|
324
|
+
agentCount: number; // Number of agents (1-5)
|
|
325
|
+
tasks: string[]; // Task descriptions
|
|
326
|
+
agentType?: string; // Agent type (default: 'executor')
|
|
327
|
+
leaseTimeout?: number; // Milliseconds (default: 5 min)
|
|
328
|
+
heartbeatInterval?: number; // Milliseconds (default: 60 sec)
|
|
329
|
+
cwd?: string; // Working directory
|
|
330
|
+
}
|
|
210
331
|
```
|
|
211
332
|
|
|
212
|
-
|
|
333
|
+
### Types
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
interface SwarmTask {
|
|
337
|
+
id: string;
|
|
338
|
+
description: string;
|
|
339
|
+
status: 'pending' | 'claimed' | 'done' | 'failed';
|
|
340
|
+
claimedBy: string | null;
|
|
341
|
+
claimedAt: number | null;
|
|
342
|
+
completedAt: number | null;
|
|
343
|
+
error?: string;
|
|
344
|
+
result?: string;
|
|
345
|
+
}
|
|
213
346
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
347
|
+
interface ClaimResult {
|
|
348
|
+
success: boolean;
|
|
349
|
+
taskId: string | null;
|
|
350
|
+
description?: string;
|
|
351
|
+
reason?: string;
|
|
352
|
+
}
|
|
218
353
|
|
|
219
|
-
|
|
354
|
+
interface SwarmStats {
|
|
355
|
+
totalTasks: number;
|
|
356
|
+
pendingTasks: number;
|
|
357
|
+
claimedTasks: number;
|
|
358
|
+
doneTasks: number;
|
|
359
|
+
failedTasks: number;
|
|
360
|
+
activeAgents: number;
|
|
361
|
+
elapsedTime: number;
|
|
362
|
+
}
|
|
363
|
+
```
|
|
220
364
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
- **
|
|
224
|
-
- **
|
|
365
|
+
## Key Parameters
|
|
366
|
+
|
|
367
|
+
- **Max Agents:** 5 (enforced by Claude Code background task limit)
|
|
368
|
+
- **Lease Timeout:** 5 minutes (default, configurable)
|
|
369
|
+
- Tasks claimed longer than this without heartbeat are auto-released
|
|
370
|
+
- **Heartbeat Interval:** 60 seconds (recommended)
|
|
371
|
+
- Agents should call `heartbeat()` at least this often
|
|
372
|
+
- Prevents false timeout while working on long tasks
|
|
373
|
+
- **Cleanup Interval:** 60 seconds
|
|
374
|
+
- Orchestrator automatically runs `cleanupStaleClaims()` to release orphaned tasks
|
|
375
|
+
- **Database:** SQLite (stored at `.omc/state/swarm.db`)
|
|
376
|
+
- One database per swarm session
|
|
377
|
+
- Survives agent crashes
|
|
378
|
+
- Provides ACID guarantees
|
|
379
|
+
|
|
380
|
+
## Error Handling & Recovery
|
|
381
|
+
|
|
382
|
+
### Agent Crash
|
|
383
|
+
- Task is claimed but agent stops sending heartbeats
|
|
384
|
+
- After 5 minutes of no heartbeat, cleanupStaleClaims() releases the task
|
|
385
|
+
- Task returns to 'pending' status for another agent to claim
|
|
386
|
+
- Original agent's incomplete work is safely abandoned
|
|
387
|
+
|
|
388
|
+
### Task Completion Failure
|
|
389
|
+
- Agent calls `completeTask()` but is no longer the owner (was released)
|
|
390
|
+
- The update silently fails (no agent matches in WHERE clause)
|
|
391
|
+
- Agent can detect this by checking return value
|
|
392
|
+
- Agent should log error and continue to next task
|
|
393
|
+
|
|
394
|
+
### Database Unavailable
|
|
395
|
+
- `startSwarm()` returns false if database initialization fails
|
|
396
|
+
- `claimTask()` returns `{ success: false, reason: 'Database not initialized' }`
|
|
397
|
+
- Check `isSwarmReady()` before proceeding
|
|
398
|
+
|
|
399
|
+
### All Agents Idle
|
|
400
|
+
- Orchestrator detects via `getActiveAgents() === 0` or `hasPendingWork() === false`
|
|
401
|
+
- Triggers final cleanup and marks swarm as complete
|
|
402
|
+
- Remaining failed tasks are preserved in database
|
|
403
|
+
|
|
404
|
+
### No Tasks Available
|
|
405
|
+
- `claimTask()` returns success=false with reason 'No pending tasks available'
|
|
406
|
+
- Agent should check `hasPendingWork()` before looping
|
|
407
|
+
- Safe for agent to exit cleanly when no work remains
|
|
225
408
|
|
|
226
409
|
## Cancellation
|
|
227
410
|
|
|
@@ -233,42 +416,61 @@ Use unified cancel command:
|
|
|
233
416
|
This:
|
|
234
417
|
- Stops orchestrator monitoring
|
|
235
418
|
- Signals all background agents to exit
|
|
236
|
-
- Preserves partial progress in
|
|
419
|
+
- Preserves partial progress in database
|
|
237
420
|
- Marks session as "cancelled"
|
|
238
421
|
|
|
239
422
|
## Use Cases
|
|
240
423
|
|
|
241
424
|
### Fix All Type Errors
|
|
242
425
|
```
|
|
243
|
-
/swarm 5:executor "fix all TypeScript type errors"
|
|
426
|
+
/oh-my-claudecode:swarm 5:executor "fix all TypeScript type errors"
|
|
244
427
|
```
|
|
245
428
|
Spawns 5 executors, each claiming and fixing individual files.
|
|
246
429
|
|
|
247
430
|
### Implement UI Components
|
|
248
431
|
```
|
|
249
|
-
/swarm 3:designer "implement Material-UI styling for all components"
|
|
432
|
+
/oh-my-claudecode:swarm 3:designer "implement Material-UI styling for all components"
|
|
250
433
|
```
|
|
251
434
|
Spawns 3 designers, each styling different component files.
|
|
252
435
|
|
|
253
436
|
### Security Audit
|
|
254
437
|
```
|
|
255
|
-
/swarm 4:security-reviewer "review all API endpoints for vulnerabilities"
|
|
438
|
+
/oh-my-claudecode:swarm 4:security-reviewer "review all API endpoints for vulnerabilities"
|
|
256
439
|
```
|
|
257
440
|
Spawns 4 security reviewers, each auditing different endpoints.
|
|
258
441
|
|
|
259
442
|
### Documentation Sprint
|
|
260
443
|
```
|
|
261
|
-
/swarm 2:writer "add JSDoc comments to all exported functions"
|
|
444
|
+
/oh-my-claudecode:swarm 2:writer "add JSDoc comments to all exported functions"
|
|
262
445
|
```
|
|
263
446
|
Spawns 2 writers, each documenting different modules.
|
|
264
447
|
|
|
265
|
-
## Benefits
|
|
448
|
+
## Benefits of SQLite-Based Implementation
|
|
449
|
+
|
|
450
|
+
### Atomicity & Safety
|
|
451
|
+
- **Race-Condition Free:** SQLite transactions guarantee only one agent claims each task
|
|
452
|
+
- **No Lost Updates:** ACID compliance means state changes are durable
|
|
453
|
+
- **Orphan Prevention:** Expired claims are automatically released without manual intervention
|
|
454
|
+
|
|
455
|
+
### Performance
|
|
456
|
+
- **Fast Queries:** Indexed lookups on task status and agent ID
|
|
457
|
+
- **Concurrent Access:** Multiple agents read/write without blocking
|
|
458
|
+
- **Minimal Lock Time:** Transactions are microseconds, not seconds
|
|
459
|
+
|
|
460
|
+
### Reliability
|
|
461
|
+
- **Crash Recovery:** Database survives agent failures
|
|
462
|
+
- **Automatic Cleanup:** Stale claims don't block progress
|
|
463
|
+
- **Lease-Based:** Time-based expiration prevents indefinite hangs
|
|
464
|
+
|
|
465
|
+
### Developer Experience
|
|
466
|
+
- **Simple API:** Just `claimTask()`, `completeTask()`, `heartbeat()`
|
|
467
|
+
- **Full Visibility:** Query any task or agent status at any time
|
|
468
|
+
- **Easy Debugging:** SQL queries show exact state without decoding JSON
|
|
266
469
|
|
|
267
|
-
|
|
268
|
-
- **
|
|
269
|
-
- **
|
|
270
|
-
- **
|
|
271
|
-
- **Scalable:** Works for 10s to 100s of subtasks
|
|
470
|
+
### Scalability
|
|
471
|
+
- **10s to 1000s of Tasks:** SQLite handles easily
|
|
472
|
+
- **Full Task Retention:** Complete history in database for analysis
|
|
473
|
+
- **Extensible Schema:** Add custom columns for task metadata
|
|
272
474
|
|
|
273
475
|
## Output
|
|
274
476
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/hooks/auto-slash-command/executor.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|