@geekbeer/minion 2.5.1 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/api.js +26 -0
- package/config.js +27 -0
- package/execution-store.js +3 -2
- package/lib/llm-checker.js +114 -0
- package/minion-cli.sh +155 -5
- package/package.json +3 -1
- package/routes/directives.js +163 -0
- package/routes/files.js +23 -9
- package/routes/health.js +2 -0
- package/routes/index.js +15 -0
- package/routes/routines.js +260 -0
- package/routes/skills.js +125 -6
- package/routes/terminal.js +2 -2
- package/routes/workflows.js +16 -3
- package/routine-runner.js +404 -0
- package/routine-store.js +117 -0
- package/rules/minion.md +389 -53
- package/server.js +18 -5
- package/settings/permissions.json +2 -0
- package/workflow-runner.js +14 -11
- package/workflow-store.js +8 -3
package/rules/minion.md
CHANGED
|
@@ -3,6 +3,51 @@
|
|
|
3
3
|
You are running on a Minion VPS managed by the @geekbeer/minion package.
|
|
4
4
|
A local Agent API server runs at `http://localhost:3001` and a CLI tool `minion-cli` is available.
|
|
5
5
|
|
|
6
|
+
## Architecture: Project / Workflow / Routine
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Project (組織・課金単位)
|
|
10
|
+
├── Context (markdown, PMが更新)
|
|
11
|
+
├── Members (minion + role: pm | engineer)
|
|
12
|
+
└── Workflows (何をするか + オプションのcronスケジュール)
|
|
13
|
+
├── Versions (pipeline の不変スナップショット)
|
|
14
|
+
└── Executions (実行履歴, ステップごとの進捗)
|
|
15
|
+
|
|
16
|
+
Minion
|
|
17
|
+
└── Routines (いつ・何を, cron付き, ミニオンローカルのタスク)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Workflow** はプロジェクトスコープ。バージョン管理されたパイプライン(スキル列)を持つ。オプションで `cron_expression` によるスケジュール実行が可能。HQ UIからワンショット実行もできる。
|
|
21
|
+
- **Routine** はミニオンスコープ。ミニオンローカルの定期タスク。cron_expression を持ち自律トリガーする。
|
|
22
|
+
- ミニオンは複数プロジェクトに `pm` または `engineer` として参加できる。
|
|
23
|
+
- ワークフローの各ステップには `assigned_role`(pm/engineer)と `requires_review` フラグがある。
|
|
24
|
+
|
|
25
|
+
## Workflow Execution Model (DB ステートマシン)
|
|
26
|
+
|
|
27
|
+
ワークフロー実行はHQのDBがステートマシンとして管理する。ミニオン間通信は不要。
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
1. Execution 作成 (HQ UIのRunボタン or cronスケジュール)
|
|
31
|
+
→ workflow_execution (status='pending')
|
|
32
|
+
→ workflow_step_executions (全ステップ status='pending')
|
|
33
|
+
|
|
34
|
+
2. ミニオンエージェントが GET /api/minion/pending-steps をポーリング
|
|
35
|
+
→ 自分のロールに該当 & 前ステップ完了済みのステップを取得
|
|
36
|
+
|
|
37
|
+
3. ミニオンがステップを実行し完了を報告
|
|
38
|
+
→ POST /api/minion/execution で status/outcome を更新
|
|
39
|
+
|
|
40
|
+
4. 次ステップが eligible に → 別のミニオンが検知して実行
|
|
41
|
+
→ requires_review=true の場合はHQでレビュー承認後に次へ
|
|
42
|
+
|
|
43
|
+
5. 全ステップ完了 → execution 全体を completed に
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**ポイント:**
|
|
47
|
+
- HQの責務は「executionレコードを作る(指示書発行)」だけ
|
|
48
|
+
- 各ミニオンは自分の担当ステップだけをポーリングして実行
|
|
49
|
+
- ミニオンエージェント(Port 3001)が軽量HTTPポーリングを行い、pending検知時のみClaude Codeを起動(トークン節約)
|
|
50
|
+
|
|
6
51
|
## CLI Commands
|
|
7
52
|
|
|
8
53
|
```bash
|
|
@@ -35,7 +80,7 @@ Authentication: `Authorization: Bearer $API_TOKEN` header (except where noted).
|
|
|
35
80
|
| Method | Endpoint | Description |
|
|
36
81
|
|--------|----------|-------------|
|
|
37
82
|
| GET | `/api/list-skills` | List local deployed skills |
|
|
38
|
-
| DELETE | `/api/skills/:name` | Delete local skill |
|
|
83
|
+
| DELETE | `/api/skills/:name` | Delete a local skill |
|
|
39
84
|
| POST | `/api/skills/push/:name` | Push local skill to HQ |
|
|
40
85
|
| POST | `/api/skills/fetch/:name` | Fetch skill from HQ and deploy locally |
|
|
41
86
|
| GET | `/api/skills/remote` | List skills on HQ |
|
|
@@ -44,23 +89,22 @@ Authentication: `Authorization: Bearer $API_TOKEN` header (except where noted).
|
|
|
44
89
|
|
|
45
90
|
| Method | Endpoint | Description |
|
|
46
91
|
|--------|----------|-------------|
|
|
47
|
-
| GET | `/api/workflows` | List all workflows with next_run |
|
|
48
|
-
| POST | `/api/workflows` | Receive/upsert workflows from HQ |
|
|
92
|
+
| GET | `/api/workflows` | List all local workflows with next_run |
|
|
93
|
+
| POST | `/api/workflows` | Receive/upsert workflows (from HQ or local) |
|
|
49
94
|
| POST | `/api/workflows/push/:name` | Push local workflow to HQ |
|
|
50
95
|
| POST | `/api/workflows/fetch/:name` | Fetch workflow from HQ and deploy locally (+ pipeline skills) |
|
|
51
96
|
| GET | `/api/workflows/remote` | List workflows on HQ |
|
|
52
|
-
|
|
|
53
|
-
| DELETE | `/api/workflows/:id` | Remove workflow |
|
|
97
|
+
| DELETE | `/api/workflows/:id` | Remove a local workflow |
|
|
54
98
|
| POST | `/api/workflows/trigger` | Manual trigger. Body: `{workflow_id}` |
|
|
55
99
|
|
|
56
100
|
### Executions
|
|
57
101
|
|
|
58
102
|
| Method | Endpoint | Description |
|
|
59
103
|
|--------|----------|-------------|
|
|
60
|
-
| GET | `/api/executions` | List execution history (
|
|
104
|
+
| GET | `/api/executions` | List execution history (`?limit=50`, `?workflow_id=`) |
|
|
61
105
|
| GET | `/api/executions/:id` | Get single execution |
|
|
62
|
-
| GET | `/api/executions/:id/log` | Get execution log content |
|
|
63
|
-
| POST | `/api/executions/:id/outcome` | Report outcome (no auth). Body: `{outcome}` |
|
|
106
|
+
| GET | `/api/executions/:id/log` | Get execution log content (`?tail=N`) |
|
|
107
|
+
| POST | `/api/executions/:id/outcome` | Report outcome (no auth). Body: `{outcome, summary?, details?}` |
|
|
64
108
|
|
|
65
109
|
Outcome values: `success`, `failure`, `partial`
|
|
66
110
|
|
|
@@ -69,13 +113,25 @@ Outcome values: `success`, `failure`, `partial`
|
|
|
69
113
|
| Method | Endpoint | Description |
|
|
70
114
|
|--------|----------|-------------|
|
|
71
115
|
| GET | `/api/terminal/sessions` | List tmux sessions |
|
|
72
|
-
| POST | `/api/terminal/send` | Send keys. Body: `{session,
|
|
73
|
-
| POST | `/api/terminal/create` | Create session. Body: `{name
|
|
116
|
+
| POST | `/api/terminal/send` | Send keys. Body: `{session, input?, enter?, special?}` |
|
|
117
|
+
| POST | `/api/terminal/create` | Create session. Body: `{name?, command?}` |
|
|
74
118
|
| POST | `/api/terminal/kill` | Kill session. Body: `{session}` |
|
|
75
|
-
| GET | `/api/terminal/capture` | Capture pane content. Query: `?session
|
|
119
|
+
| GET | `/api/terminal/capture` | Capture pane content. Query: `?session=&lines=100` |
|
|
76
120
|
| GET | `/api/terminal/ttyd/status` | ttyd process status |
|
|
77
|
-
| POST | `/api/terminal/ttyd/start` | Start ttyd. Body: `{session}` |
|
|
78
|
-
| POST | `/api/terminal/ttyd/stop` | Stop ttyd. Body: `{session}` |
|
|
121
|
+
| POST | `/api/terminal/ttyd/start` | Start ttyd for session. Body: `{session}` |
|
|
122
|
+
| POST | `/api/terminal/ttyd/stop` | Stop ttyd for session. Body: `{session}` |
|
|
123
|
+
| POST | `/api/terminal/ttyd/stop-all` | Stop all ttyd processes |
|
|
124
|
+
|
|
125
|
+
### Files
|
|
126
|
+
|
|
127
|
+
Files are stored in `~/files/`. Max upload size: 50MB.
|
|
128
|
+
|
|
129
|
+
| Method | Endpoint | Description |
|
|
130
|
+
|--------|----------|-------------|
|
|
131
|
+
| GET | `/api/files` | List files in directory. Query: `?path=subdir` |
|
|
132
|
+
| GET | `/api/files/*` | Download a file |
|
|
133
|
+
| POST | `/api/files/*` | Upload a file (Content-Type: `application/octet-stream`) |
|
|
134
|
+
| DELETE | `/api/files/*` | Delete a file or directory |
|
|
79
135
|
|
|
80
136
|
### Commands
|
|
81
137
|
|
|
@@ -86,6 +142,229 @@ Outcome values: `success`, `failure`, `partial`
|
|
|
86
142
|
|
|
87
143
|
Available commands: `restart-agent`, `update-agent`, `restart-display`, `status-services`
|
|
88
144
|
|
|
145
|
+
## HQ API Endpoints (https://<HQ_URL>)
|
|
146
|
+
|
|
147
|
+
Authentication: `Authorization: Bearer $API_TOKEN` header.
|
|
148
|
+
Base path: All endpoints are prefixed with `/api/minion` by the agent's API client.
|
|
149
|
+
|
|
150
|
+
### Projects
|
|
151
|
+
|
|
152
|
+
| Method | Endpoint | Description |
|
|
153
|
+
|--------|----------|-------------|
|
|
154
|
+
| GET | `/api/minion/me/projects` | 参加プロジェクト一覧(role 含む) |
|
|
155
|
+
|
|
156
|
+
Response:
|
|
157
|
+
```json
|
|
158
|
+
[
|
|
159
|
+
{
|
|
160
|
+
"id": "uuid",
|
|
161
|
+
"name": "project-name",
|
|
162
|
+
"description": "...",
|
|
163
|
+
"github_owner": "org",
|
|
164
|
+
"github_repo": "repo",
|
|
165
|
+
"role": "pm",
|
|
166
|
+
"created_at": "...",
|
|
167
|
+
"updated_at": "..."
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`role` is either `"pm"` (project manager) or `"engineer"`.
|
|
173
|
+
|
|
174
|
+
### Project Context
|
|
175
|
+
|
|
176
|
+
| Method | Endpoint | Description |
|
|
177
|
+
|--------|----------|-------------|
|
|
178
|
+
| GET | `/api/minion/me/project/[id]/context` | プロジェクトコンテキスト取得 |
|
|
179
|
+
| PUT | `/api/minion/me/project/[id]/context` | コンテキスト更新(PM のみ) |
|
|
180
|
+
|
|
181
|
+
PUT body: `{ "content": "markdown string" }`
|
|
182
|
+
|
|
183
|
+
### Workflows (project-scoped, versioned)
|
|
184
|
+
|
|
185
|
+
| Method | Endpoint | Description |
|
|
186
|
+
|--------|----------|-------------|
|
|
187
|
+
| GET | `/api/minion/workflows` | 参加プロジェクトのアクティブなワークフロー一覧 |
|
|
188
|
+
| POST | `/api/minion/workflows` | ワークフローを push(新規作成 or 新バージョン) |
|
|
189
|
+
|
|
190
|
+
GET Response:
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"workflows": [
|
|
194
|
+
{
|
|
195
|
+
"name": "daily-check",
|
|
196
|
+
"pipeline_skill_names": ["skill-1", "skill-2"],
|
|
197
|
+
"pipeline": [
|
|
198
|
+
{
|
|
199
|
+
"skill_version_id": "uuid",
|
|
200
|
+
"skill_name": "skill-1",
|
|
201
|
+
"skill_display_name": "Skill One",
|
|
202
|
+
"skill_version": 3,
|
|
203
|
+
"assigned_role": "engineer",
|
|
204
|
+
"requires_review": false,
|
|
205
|
+
"is_my_step": true
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"content": "...",
|
|
209
|
+
"version": 3,
|
|
210
|
+
"cron_expression": "0 9 * * *",
|
|
211
|
+
"project_id": "uuid",
|
|
212
|
+
"created_at": "..."
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
各ステップの `is_my_step` はミニオン自身のプロジェクトロールと `assigned_role` の一致を示す。
|
|
219
|
+
|
|
220
|
+
POST body (push):
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"name": "my-workflow",
|
|
224
|
+
"pipeline_skill_names": ["skill-1", "skill-2"],
|
|
225
|
+
"content": "Workflow description",
|
|
226
|
+
"project_id": "uuid",
|
|
227
|
+
"change_summary": "Added skill-2 to pipeline"
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
push するとパイプライン内のスキル名が `skill_version_id` に解決され、新バージョンが自動作成される。
|
|
232
|
+
|
|
233
|
+
### Pending Steps (軽量ポーリング)
|
|
234
|
+
|
|
235
|
+
| Method | Endpoint | Description |
|
|
236
|
+
|--------|----------|-------------|
|
|
237
|
+
| GET | `/api/minion/pending-steps` | 自分が実行すべき pending ステップ一覧 |
|
|
238
|
+
|
|
239
|
+
**このエンドポイントはミニオンエージェント(非AI)による高頻度ポーリング用。**
|
|
240
|
+
Claude Code(AIスキル実行)はステップ検知時のみ起動する。
|
|
241
|
+
|
|
242
|
+
Response:
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"steps": [
|
|
246
|
+
{
|
|
247
|
+
"step_execution_id": "uuid",
|
|
248
|
+
"execution_id": "uuid",
|
|
249
|
+
"workflow_name": "daily-check",
|
|
250
|
+
"step_index": 0,
|
|
251
|
+
"skill_version_id": "uuid",
|
|
252
|
+
"assigned_role": "engineer"
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
返却条件:
|
|
259
|
+
- `assigned_role` がミニオンのプロジェクトロールと一致
|
|
260
|
+
- ステップの `status` が `pending`
|
|
261
|
+
- 前ステップが全て `completed`(`requires_review` の場合は `approved` も必要)
|
|
262
|
+
|
|
263
|
+
### Workflow Execution Recording
|
|
264
|
+
|
|
265
|
+
| Method | Endpoint | Description |
|
|
266
|
+
|--------|----------|-------------|
|
|
267
|
+
| POST | `/api/minion/workflow-execution` | 新規 execution 開始を記録 |
|
|
268
|
+
| POST | `/api/minion/execution` | ステップ実行状況を HQ に報告 |
|
|
269
|
+
|
|
270
|
+
POST `/api/minion/workflow-execution` body:
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"workflow_version_id": "uuid"
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Response:
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"execution_id": "uuid",
|
|
281
|
+
"status": "running",
|
|
282
|
+
"steps_count": 3
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
POST `/api/minion/execution` body:
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"workflow_execution_id": "uuid",
|
|
290
|
+
"steps": [
|
|
291
|
+
{
|
|
292
|
+
"step_index": 0,
|
|
293
|
+
"skill_version_id": "uuid",
|
|
294
|
+
"assigned_role": "engineer",
|
|
295
|
+
"status": "completed",
|
|
296
|
+
"outcome": "success",
|
|
297
|
+
"started_at": "...",
|
|
298
|
+
"completed_at": "..."
|
|
299
|
+
}
|
|
300
|
+
],
|
|
301
|
+
"status": "running",
|
|
302
|
+
"started_at": "..."
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Issue Reporting (GitHub Issue 起票)
|
|
307
|
+
|
|
308
|
+
| Method | Endpoint | Description |
|
|
309
|
+
|--------|----------|-------------|
|
|
310
|
+
| POST | `/api/minion/report` | バグや改善提案を GitHub Issue として起票 |
|
|
311
|
+
|
|
312
|
+
POST `/api/minion/report` body:
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"title": "HQ APIで502エラーが発生する",
|
|
316
|
+
"body": "## 状況\n...\n\n## 再現手順\n...\n\n## エラー情報\n...\n\n## 環境\n...",
|
|
317
|
+
"labels": ["bug", "critical"]
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Response:
|
|
322
|
+
```json
|
|
323
|
+
{
|
|
324
|
+
"success": true,
|
|
325
|
+
"issue_url": "https://github.com/owner/repo/issues/42",
|
|
326
|
+
"issue_number": 42
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
使用可能なラベル: `bug`, `enhancement`, `critical`, `minor`
|
|
331
|
+
|
|
332
|
+
**使い方:** サービスのバグや改善点を発見したら、このエンドポイントで GitHub Issue を起票する。報告者のミニオン名と ID が自動で本文に付記される。
|
|
333
|
+
|
|
334
|
+
### Routines (minion-scoped)
|
|
335
|
+
|
|
336
|
+
| Method | Endpoint | Description |
|
|
337
|
+
|--------|----------|-------------|
|
|
338
|
+
| GET | `/api/minion/routines` | 自分のルーティン一覧 |
|
|
339
|
+
|
|
340
|
+
Response:
|
|
341
|
+
```json
|
|
342
|
+
[
|
|
343
|
+
{
|
|
344
|
+
"id": "uuid",
|
|
345
|
+
"name": "routine-name",
|
|
346
|
+
"pipeline_skill_names": ["skill-1", "execution-report"],
|
|
347
|
+
"content": "...",
|
|
348
|
+
"is_active": true,
|
|
349
|
+
"cron_expression": "0 9 * * 1-5",
|
|
350
|
+
"last_run": "...",
|
|
351
|
+
"next_run": "...",
|
|
352
|
+
"created_at": "...",
|
|
353
|
+
"updated_at": "..."
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Skills
|
|
359
|
+
|
|
360
|
+
| Method | Endpoint | Description |
|
|
361
|
+
|--------|----------|-------------|
|
|
362
|
+
| GET | `/api/minion/skills` | HQ に登録されたスキル一覧 |
|
|
363
|
+
| GET | `/api/minion/skills/[name]` | スキル詳細取得(content, files 含む) |
|
|
364
|
+
| POST | `/api/minion/skills` | スキル登録/更新(新バージョン自動作成) |
|
|
365
|
+
|
|
366
|
+
スキルはバージョン管理される。push ごとに新バージョンが作成され、ファイルは Supabase Storage に保存される。
|
|
367
|
+
|
|
89
368
|
## Environment Variables
|
|
90
369
|
|
|
91
370
|
| Variable | Description |
|
|
@@ -125,58 +404,52 @@ When not configured, the agent runs in standalone mode and HQ-dependent features
|
|
|
125
404
|
|
|
126
405
|
## Workflow Structure
|
|
127
406
|
|
|
128
|
-
Workflows are
|
|
407
|
+
Workflows are project-scoped and versioned. Each version is an immutable snapshot of the pipeline.
|
|
408
|
+
Fetched from HQ via `GET /api/minion/workflows`. Also stored locally in `workflows.json`.
|
|
129
409
|
|
|
130
410
|
```json
|
|
131
411
|
{
|
|
132
|
-
"id": "uuid",
|
|
133
412
|
"name": "my-workflow",
|
|
134
413
|
"pipeline_skill_names": ["skill-1", "skill-2", "execution-report"],
|
|
414
|
+
"pipeline": [
|
|
415
|
+
{
|
|
416
|
+
"skill_version_id": "uuid",
|
|
417
|
+
"skill_name": "skill-1",
|
|
418
|
+
"skill_display_name": "Skill One",
|
|
419
|
+
"skill_version": 2,
|
|
420
|
+
"assigned_role": "engineer",
|
|
421
|
+
"requires_review": false,
|
|
422
|
+
"is_my_step": true
|
|
423
|
+
}
|
|
424
|
+
],
|
|
135
425
|
"content": "Markdown description of the workflow",
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
426
|
+
"version": 2,
|
|
427
|
+
"cron_expression": "0 9 * * *",
|
|
428
|
+
"project_id": "uuid"
|
|
139
429
|
}
|
|
140
430
|
```
|
|
141
431
|
|
|
142
432
|
| Field | Type | Description |
|
|
143
433
|
|-------|------|-------------|
|
|
144
|
-
| `id` | string | UUID (auto-generated) |
|
|
145
434
|
| `name` | string | Slug identifier (`/^[a-z0-9-]+$/`) |
|
|
146
|
-
| `pipeline_skill_names` | string[] | Ordered skill names
|
|
435
|
+
| `pipeline_skill_names` | string[] | Ordered skill names (for display/push) |
|
|
436
|
+
| `pipeline` | PipelineStep[] | Resolved pipeline with version IDs and roles |
|
|
147
437
|
| `content` | string | Markdown body describing the workflow |
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
|
|
152
|
-
### Creating a workflow locally
|
|
438
|
+
| `version` | number | Current version number (auto-incremented on push) |
|
|
439
|
+
| `cron_expression` | string\|null | Cron schedule (null = manual/one-shot only) |
|
|
440
|
+
| `project_id` | string | UUID of the parent project |
|
|
153
441
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
curl -X POST http://localhost:3001/api/workflows \
|
|
158
|
-
-H "Authorization: Bearer $API_TOKEN" \
|
|
159
|
-
-H "Content-Type: application/json" \
|
|
160
|
-
-d '{
|
|
161
|
-
"workflows": [{
|
|
162
|
-
"id": "'$(uuidgen)'",
|
|
163
|
-
"name": "my-workflow",
|
|
164
|
-
"pipeline_skill_names": ["my-skill", "execution-report"],
|
|
165
|
-
"content": "Description of what this workflow does",
|
|
166
|
-
"cron_expression": "",
|
|
167
|
-
"is_active": false
|
|
168
|
-
}]
|
|
169
|
-
}'
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Then activate with a schedule:
|
|
442
|
+
### Pipeline Step Fields
|
|
173
443
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
444
|
+
| Field | Type | Description |
|
|
445
|
+
|-------|------|-------------|
|
|
446
|
+
| `skill_version_id` | string | UUID of the specific skill version |
|
|
447
|
+
| `skill_name` | string | Skill slug name |
|
|
448
|
+
| `skill_display_name` | string | Human-readable skill name |
|
|
449
|
+
| `skill_version` | number | Skill version number |
|
|
450
|
+
| `assigned_role` | string | `"pm"` or `"engineer"` — who executes this step |
|
|
451
|
+
| `requires_review` | boolean | If true, human review required after completion |
|
|
452
|
+
| `is_my_step` | boolean | Whether this minion's role matches assigned_role |
|
|
180
453
|
|
|
181
454
|
### Syncing workflows with HQ
|
|
182
455
|
|
|
@@ -186,10 +459,73 @@ curl -X PUT http://localhost:3001/api/workflows/<id>/schedule \
|
|
|
186
459
|
|
|
187
460
|
Pipeline skills must be deployed locally (in `~/.claude/skills/`) before pushing a workflow.
|
|
188
461
|
|
|
189
|
-
##
|
|
462
|
+
## Routine Structure
|
|
463
|
+
|
|
464
|
+
Routines are minion-scoped and fetched from HQ via `GET /api/minion/routines`.
|
|
465
|
+
They are also stored locally in `routines.json`. Each routine object:
|
|
466
|
+
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"id": "uuid",
|
|
470
|
+
"name": "morning-work",
|
|
471
|
+
"pipeline_skill_names": ["project-workflow-check", "execution-report"],
|
|
472
|
+
"content": "Markdown description of the routine",
|
|
473
|
+
"cron_expression": "0 9 * * 1-5",
|
|
474
|
+
"is_active": true,
|
|
475
|
+
"last_run": "2026-02-10T09:00:00.000Z",
|
|
476
|
+
"next_run": "2026-02-11T09:00:00.000Z"
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
| Field | Type | Description |
|
|
481
|
+
|-------|------|-------------|
|
|
482
|
+
| `id` | string | UUID (auto-generated) |
|
|
483
|
+
| `name` | string | Slug identifier (`/^[a-z0-9-]+$/`) |
|
|
484
|
+
| `pipeline_skill_names` | string[] | Ordered skill names to execute |
|
|
485
|
+
| `content` | string | Markdown body describing the routine |
|
|
486
|
+
| `cron_expression` | string | Cron schedule (empty = manual only) |
|
|
487
|
+
| `is_active` | boolean | Whether cron scheduling is enabled |
|
|
488
|
+
| `last_run` | string\|null | ISO timestamp of last execution |
|
|
489
|
+
| `next_run` | string\|null | ISO timestamp of next scheduled run |
|
|
190
490
|
|
|
191
|
-
|
|
491
|
+
## Routine Execution
|
|
492
|
+
|
|
493
|
+
Routines run on cron schedules. Each execution:
|
|
192
494
|
1. Creates a tmux session
|
|
193
495
|
2. Runs skills in pipeline order via `claude --dangerously-skip-permissions`
|
|
194
496
|
3. Appends `execution-report` skill to report outcome
|
|
195
|
-
4. Environment variables `MINION_EXECUTION_ID`, `
|
|
497
|
+
4. Environment variables `MINION_EXECUTION_ID`, `MINION_ROUTINE_ID`, `MINION_ROUTINE_NAME` are available during execution
|
|
498
|
+
|
|
499
|
+
## Workflow Step Execution via Pending Steps
|
|
500
|
+
|
|
501
|
+
ミニオンエージェントは `GET /api/minion/pending-steps` を定期ポーリングし、
|
|
502
|
+
自分の担当ステップを検知したら Claude Code を起動してスキルを実行する。
|
|
503
|
+
|
|
504
|
+
```
|
|
505
|
+
ミニオンエージェント (Port 3001, 常駐, トークン不要)
|
|
506
|
+
│
|
|
507
|
+
├── 軽量ポーリング: N秒ごとに GET /api/minion/pending-steps
|
|
508
|
+
│ → HTTPのみ、AIなし、コストゼロ
|
|
509
|
+
│ → pendingステップがなければ何もしない
|
|
510
|
+
│
|
|
511
|
+
└── ステップ検知時のみ:
|
|
512
|
+
1. Claude Code 起動 → 該当スキルを実行
|
|
513
|
+
2. POST /api/minion/execution → ステップ完了を報告
|
|
514
|
+
3. Claude Code 終了 → トークン消費はここだけ
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Workflow Execution via Routine (従来方式)
|
|
518
|
+
|
|
519
|
+
ワークフローは `project-workflow-check` システムスキルを通じてルーティンからも実行可能:
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
ルーティン: "morning-work" (cron: 0 9 * * 1-5)
|
|
523
|
+
pipeline: [project-workflow-check, execution-report]
|
|
524
|
+
↓
|
|
525
|
+
project-workflow-check:
|
|
526
|
+
1. GET /api/minion/me/projects → 参加プロジェクト一覧
|
|
527
|
+
2. GET /api/minion/workflows → active なワークフロー一覧
|
|
528
|
+
3. 各ワークフローの pipeline を順に実行
|
|
529
|
+
↓
|
|
530
|
+
execution-report → HQ に結果報告
|
|
531
|
+
```
|
package/server.js
CHANGED
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs')
|
|
9
9
|
const path = require('path')
|
|
10
|
-
const os = require('os')
|
|
11
10
|
|
|
12
11
|
const fastify = require('fastify')({ logger: true })
|
|
13
12
|
const { config, validate, isHqConfigured } = require('./config')
|
|
14
13
|
const workflowRunner = require('./workflow-runner')
|
|
15
14
|
const workflowStore = require('./workflow-store')
|
|
15
|
+
const routineRunner = require('./routine-runner')
|
|
16
|
+
const routineStore = require('./routine-store')
|
|
16
17
|
|
|
17
18
|
const { registerRoutes, setOffline, getProcessManager, getAllowedCommands } = require('./routes')
|
|
18
19
|
const { cleanupTtyd, killStaleTtydProcesses } = require('./routes/terminal')
|
|
@@ -31,8 +32,9 @@ async function shutdown(signal) {
|
|
|
31
32
|
|
|
32
33
|
setOffline()
|
|
33
34
|
|
|
34
|
-
// Stop workflow
|
|
35
|
+
// Stop workflow and routine runners
|
|
35
36
|
workflowRunner.stopAll()
|
|
37
|
+
routineRunner.stopAll()
|
|
36
38
|
|
|
37
39
|
// Stop terminal proxy and all ttyd processes
|
|
38
40
|
stopTerminalProxy()
|
|
@@ -53,7 +55,7 @@ process.on('SIGINT', () => shutdown('SIGINT'))
|
|
|
53
55
|
*/
|
|
54
56
|
function syncPermissions() {
|
|
55
57
|
const bundledPath = path.join(__dirname, 'settings', 'permissions.json')
|
|
56
|
-
const settingsDir = path.join(
|
|
58
|
+
const settingsDir = path.join(config.HOME_DIR, '.claude')
|
|
57
59
|
const settingsPath = path.join(settingsDir, 'settings.json')
|
|
58
60
|
|
|
59
61
|
try {
|
|
@@ -87,7 +89,7 @@ function syncPermissions() {
|
|
|
87
89
|
*/
|
|
88
90
|
function syncTmuxConfig() {
|
|
89
91
|
const bundledPath = path.join(__dirname, 'settings', 'tmux.conf')
|
|
90
|
-
const destPath = path.join(
|
|
92
|
+
const destPath = path.join(config.HOME_DIR, '.tmux.conf')
|
|
91
93
|
|
|
92
94
|
try {
|
|
93
95
|
if (!fs.existsSync(bundledPath)) return
|
|
@@ -105,7 +107,7 @@ function syncTmuxConfig() {
|
|
|
105
107
|
*/
|
|
106
108
|
function syncBundledRules() {
|
|
107
109
|
const bundledRulesDir = path.join(__dirname, 'rules')
|
|
108
|
-
const targetRulesDir = path.join(
|
|
110
|
+
const targetRulesDir = path.join(config.HOME_DIR, '.claude', 'rules')
|
|
109
111
|
|
|
110
112
|
try {
|
|
111
113
|
if (!fs.existsSync(bundledRulesDir)) return
|
|
@@ -164,6 +166,17 @@ async function start() {
|
|
|
164
166
|
console.error('[Server] Failed to load cached workflows:', err.message)
|
|
165
167
|
}
|
|
166
168
|
|
|
169
|
+
// Load cached routines and start routine runner
|
|
170
|
+
try {
|
|
171
|
+
const cachedRoutines = await routineStore.load()
|
|
172
|
+
if (cachedRoutines.length > 0) {
|
|
173
|
+
console.log(`[Server] Loading ${cachedRoutines.length} cached routines`)
|
|
174
|
+
routineRunner.loadRoutines(cachedRoutines)
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error('[Server] Failed to load cached routines:', err.message)
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
if (isHqConfigured()) {
|
|
168
181
|
console.log(`[Server] HQ URL: ${config.HQ_URL}`)
|
|
169
182
|
} else {
|
package/workflow-runner.js
CHANGED
|
@@ -14,11 +14,11 @@ const { Cron } = require('croner')
|
|
|
14
14
|
const { exec } = require('child_process')
|
|
15
15
|
const { promisify } = require('util')
|
|
16
16
|
const crypto = require('crypto')
|
|
17
|
-
const os = require('os')
|
|
18
17
|
const path = require('path')
|
|
19
18
|
const fs = require('fs').promises
|
|
20
19
|
const execAsync = promisify(exec)
|
|
21
20
|
|
|
21
|
+
const { config } = require('./config')
|
|
22
22
|
const executionStore = require('./execution-store')
|
|
23
23
|
const workflowStore = require('./workflow-store')
|
|
24
24
|
const logManager = require('./lib/log-manager')
|
|
@@ -90,13 +90,17 @@ async function cleanupMarkerFile(sessionName) {
|
|
|
90
90
|
* @param {object} workflow - Workflow configuration
|
|
91
91
|
* @returns {Promise<{success: boolean, error?: string, sessionName?: string}>}
|
|
92
92
|
*/
|
|
93
|
-
async function executeWorkflowSession(workflow, executionId, skillNames) {
|
|
94
|
-
const homeDir =
|
|
93
|
+
async function executeWorkflowSession(workflow, executionId, skillNames, options = {}) {
|
|
94
|
+
const homeDir = config.HOME_DIR
|
|
95
95
|
const sessionName = generateSessionName(workflow.id, executionId)
|
|
96
96
|
|
|
97
|
-
// Build prompt: run each skill in sequence
|
|
97
|
+
// Build prompt: run each skill in sequence
|
|
98
|
+
// When skipExecutionReport is true (dispatched step), the minion server's
|
|
99
|
+
// post-execution hook handles completion reporting instead of /execution-report.
|
|
98
100
|
const skillCommands = skillNames.map(name => `/${name}`).join(', then ')
|
|
99
|
-
const prompt =
|
|
101
|
+
const prompt = options.skipExecutionReport
|
|
102
|
+
? `Run the following skills in order: ${skillCommands}.`
|
|
103
|
+
: `Run the following skills in order: ${skillCommands}. After completing all skills, run /execution-report to report the results.`
|
|
100
104
|
|
|
101
105
|
// Extend PATH to include common CLI installation locations
|
|
102
106
|
const additionalPaths = [
|
|
@@ -187,11 +191,10 @@ async function executeWorkflowSession(workflow, executionId, skillNames) {
|
|
|
187
191
|
|
|
188
192
|
while (Date.now() - startTime < timeout) {
|
|
189
193
|
try {
|
|
190
|
-
await
|
|
191
|
-
|
|
194
|
+
await fs.access(exitCodeFile)
|
|
195
|
+
break // Exit code file exists — claude -p has finished
|
|
192
196
|
} catch {
|
|
193
|
-
|
|
194
|
-
break
|
|
197
|
+
await sleep(pollInterval)
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
200
|
|
|
@@ -245,7 +248,7 @@ async function saveExecution(executionData) {
|
|
|
245
248
|
* @param {object} workflow - Workflow configuration
|
|
246
249
|
* @returns {Promise<{execution_id: string, session_name: string}>}
|
|
247
250
|
*/
|
|
248
|
-
async function runWorkflow(workflow) {
|
|
251
|
+
async function runWorkflow(workflow, options = {}) {
|
|
249
252
|
const pipelineSkillNames = workflow.pipeline_skill_names || []
|
|
250
253
|
|
|
251
254
|
if (pipelineSkillNames.length === 0) {
|
|
@@ -285,7 +288,7 @@ async function runWorkflow(workflow) {
|
|
|
285
288
|
})
|
|
286
289
|
|
|
287
290
|
// Execute all skills in one session
|
|
288
|
-
const result = await executeWorkflowSession(workflow, executionId, pipelineSkillNames)
|
|
291
|
+
const result = await executeWorkflowSession(workflow, executionId, pipelineSkillNames, options)
|
|
289
292
|
|
|
290
293
|
const completedAt = new Date().toISOString()
|
|
291
294
|
console.log(`[WorkflowRunner] executeWorkflowSession returned: success=${result.success}, error=${result.error || 'none'}`)
|