@geekbeer/minion 2.60.0 → 2.67.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.
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Project thread routes (local API on minion)
3
+ *
4
+ * Endpoints:
5
+ * - GET /api/help-threads - List open threads in minion's projects
6
+ * - POST /api/help-threads - Create a new thread (proxied to HQ)
7
+ * - GET /api/help-threads/:id - Get thread detail with messages
8
+ * - POST /api/help-threads/:id/messages - Post a message to a thread
9
+ * - POST /api/help-threads/:id/resolve - Resolve a thread
10
+ * - POST /api/help-threads/:id/cancel - Cancel a thread
11
+ * - DELETE /api/help-threads/:id - Permanently delete a thread (PM only)
12
+ * - GET /api/project-memories - Search project memories
13
+ * - POST /api/project-memories - Create a project memory
14
+ */
15
+
16
+ const { verifyToken } = require('../lib/auth')
17
+ const api = require('../api')
18
+
19
+ /**
20
+ * @param {import('fastify').FastifyInstance} fastify
21
+ */
22
+ async function helpThreadRoutes(fastify) {
23
+ // List open threads
24
+ fastify.get('/api/help-threads', async (request, reply) => {
25
+ if (!verifyToken(request)) {
26
+ reply.code(401)
27
+ return { success: false, error: 'Unauthorized' }
28
+ }
29
+
30
+ try {
31
+ const data = await api.getOpenHelpThreads()
32
+ return { success: true, threads: data.threads || [] }
33
+ } catch (error) {
34
+ console.error(`[HelpThreads] List error: ${error.message}`)
35
+ reply.code(500)
36
+ return { success: false, error: error.message }
37
+ }
38
+ })
39
+
40
+ // Create thread
41
+ fastify.post('/api/help-threads', async (request, reply) => {
42
+ if (!verifyToken(request)) {
43
+ reply.code(401)
44
+ return { success: false, error: 'Unauthorized' }
45
+ }
46
+
47
+ try {
48
+ const data = await api.createHelpThread(request.body)
49
+ return { success: true, thread: data.thread }
50
+ } catch (error) {
51
+ console.error(`[HelpThreads] Create error: ${error.message}`)
52
+ reply.code(error.statusCode || 500)
53
+ return { success: false, error: error.message }
54
+ }
55
+ })
56
+
57
+ // Get thread detail
58
+ fastify.get('/api/help-threads/:id', async (request, reply) => {
59
+ if (!verifyToken(request)) {
60
+ reply.code(401)
61
+ return { success: false, error: 'Unauthorized' }
62
+ }
63
+
64
+ try {
65
+ const data = await api.getHelpThread(request.params.id)
66
+ return { success: true, thread: data.thread, messages: data.messages }
67
+ } catch (error) {
68
+ console.error(`[HelpThreads] Get error: ${error.message}`)
69
+ reply.code(error.statusCode || 500)
70
+ return { success: false, error: error.message }
71
+ }
72
+ })
73
+
74
+ // Post message
75
+ fastify.post('/api/help-threads/:id/messages', async (request, reply) => {
76
+ if (!verifyToken(request)) {
77
+ reply.code(401)
78
+ return { success: false, error: 'Unauthorized' }
79
+ }
80
+
81
+ try {
82
+ const data = await api.postHelpMessage(request.params.id, request.body)
83
+ return { success: true, message: data.message }
84
+ } catch (error) {
85
+ console.error(`[HelpThreads] Message error: ${error.message}`)
86
+ reply.code(error.statusCode || 500)
87
+ return { success: false, error: error.message }
88
+ }
89
+ })
90
+
91
+ // Resolve thread
92
+ fastify.post('/api/help-threads/:id/resolve', async (request, reply) => {
93
+ if (!verifyToken(request)) {
94
+ reply.code(401)
95
+ return { success: false, error: 'Unauthorized' }
96
+ }
97
+
98
+ try {
99
+ const { resolution } = request.body || {}
100
+ const data = await api.resolveHelpThread(request.params.id, resolution)
101
+ return { success: true, thread: data.thread }
102
+ } catch (error) {
103
+ console.error(`[HelpThreads] Resolve error: ${error.message}`)
104
+ reply.code(error.statusCode || 500)
105
+ return { success: false, error: error.message }
106
+ }
107
+ })
108
+
109
+ // Permanently delete thread (PM only)
110
+ fastify.delete('/api/help-threads/:id', async (request, reply) => {
111
+ if (!verifyToken(request)) {
112
+ reply.code(401)
113
+ return { success: false, error: 'Unauthorized' }
114
+ }
115
+
116
+ try {
117
+ const data = await api.deleteHelpThread(request.params.id)
118
+ return { success: true, deleted: data.deleted }
119
+ } catch (error) {
120
+ console.error(`[HelpThreads] Delete error: ${error.message}`)
121
+ reply.code(error.statusCode || 500)
122
+ return { success: false, error: error.message }
123
+ }
124
+ })
125
+
126
+ // Cancel thread
127
+ fastify.post('/api/help-threads/:id/cancel', async (request, reply) => {
128
+ if (!verifyToken(request)) {
129
+ reply.code(401)
130
+ return { success: false, error: 'Unauthorized' }
131
+ }
132
+
133
+ try {
134
+ const { reason } = request.body || {}
135
+ const data = await api.cancelHelpThread(request.params.id, reason)
136
+ return { success: true, thread: data.thread }
137
+ } catch (error) {
138
+ console.error(`[HelpThreads] Cancel error: ${error.message}`)
139
+ reply.code(error.statusCode || 500)
140
+ return { success: false, error: error.message }
141
+ }
142
+ })
143
+
144
+ // Search project memories
145
+ fastify.get('/api/project-memories', async (request, reply) => {
146
+ if (!verifyToken(request)) {
147
+ reply.code(401)
148
+ return { success: false, error: 'Unauthorized' }
149
+ }
150
+
151
+ try {
152
+ const { project_id, search, category, tags, status } = request.query || {}
153
+ if (!project_id) {
154
+ reply.code(400)
155
+ return { success: false, error: 'project_id is required' }
156
+ }
157
+ const data = await api.searchProjectMemories(project_id, {
158
+ search,
159
+ category,
160
+ tags: tags ? tags.split(',') : undefined,
161
+ status,
162
+ })
163
+ return { success: true, memories: data.memories || [] }
164
+ } catch (error) {
165
+ console.error(`[ProjectMemories] Search error: ${error.message}`)
166
+ reply.code(error.statusCode || 500)
167
+ return { success: false, error: error.message }
168
+ }
169
+ })
170
+
171
+ // Create project memory
172
+ fastify.post('/api/project-memories', async (request, reply) => {
173
+ if (!verifyToken(request)) {
174
+ reply.code(401)
175
+ return { success: false, error: 'Unauthorized' }
176
+ }
177
+
178
+ try {
179
+ const data = await api.createProjectMemory(request.body)
180
+ return { success: true, memory: data.memory }
181
+ } catch (error) {
182
+ console.error(`[ProjectMemories] Create error: ${error.message}`)
183
+ reply.code(error.statusCode || 500)
184
+ return { success: false, error: error.message }
185
+ }
186
+ })
187
+ }
188
+
189
+ module.exports = { helpThreadRoutes }
@@ -244,6 +244,174 @@ Supported `cli_type`: `claude-code`, `gemini`, `codex`
244
244
 
245
245
  Note: Claude Code の場合、書き込み先は `settings.local.json`(サーバー再起動時に上書きされない)。
246
246
 
247
+ ### Daemon Status (デーモン監視)
248
+
249
+ バックグラウンドデーモンの稼働状態を確認する。
250
+
251
+ | Method | Endpoint | Description |
252
+ |--------|----------|-------------|
253
+ | GET | `/api/daemons/status` | 全デーモンのステータス一覧 |
254
+
255
+ GET `/api/daemons/status` response:
256
+ ```json
257
+ {
258
+ "success": true,
259
+ "daemons": {
260
+ "step_poller": { "running": true, "last_poll_at": "2026-03-19T15:30:00.000Z" },
261
+ "revision_watcher": { "running": true, "last_poll_at": "2026-03-19T15:30:05.000Z" },
262
+ "thread_watcher": { "running": true, "last_poll_at": "2026-03-19T15:29:52.000Z" },
263
+ "reflection_scheduler": { "running": true, "next_run": "2026-03-20T03:00:00.000Z" },
264
+ "heartbeat": { "running": true, "last_beat_at": "2026-03-19T15:30:10.000Z" }
265
+ }
266
+ }
267
+ ```
268
+
269
+ | Daemon | 概要 |
270
+ |--------|------|
271
+ | `step_poller` | ワークフローステップの取得・実行(30秒間隔) |
272
+ | `revision_watcher` | リビジョン要求の検知(30秒間隔、PMのみ) |
273
+ | `thread_watcher` | プロジェクトスレッドの監視・LLM評価(15秒間隔) |
274
+ | `reflection_scheduler` | 1日1回の振り返り(cron) |
275
+ | `heartbeat` | HQへのハートビート(30秒間隔) |
276
+
277
+ ### Project Threads (プロジェクトスレッド)
278
+
279
+ プロジェクト内のコミュニケーションチャネル。ブロッカー共有やチーム議論に使う。
280
+ リクエストは HQ にプロキシされる。
281
+
282
+ | Method | Endpoint | Description |
283
+ |--------|----------|-------------|
284
+ | GET | `/api/help-threads` | 参加プロジェクトのオープンスレッド一覧 |
285
+ | POST | `/api/help-threads` | スレッドを起票 |
286
+ | GET | `/api/help-threads/:id` | スレッド詳細 + メッセージ一覧 |
287
+ | POST | `/api/help-threads/:id/messages` | スレッドにメッセージを投稿 |
288
+ | POST | `/api/help-threads/:id/resolve` | スレッドを解決済みにする |
289
+ | POST | `/api/help-threads/:id/cancel` | スレッドをキャンセル |
290
+ | DELETE | `/api/help-threads/:id` | スレッドを完全削除(PMのみ) |
291
+
292
+ POST `/api/help-threads` body (ヘルプスレッド起票):
293
+ ```json
294
+ {
295
+ "project_id": "uuid",
296
+ "thread_type": "help",
297
+ "title": "ランサーズの2FA認証コードが必要",
298
+ "description": "ログイン時に2段階認証を要求された。メールで届く6桁コードの入力が必要。",
299
+ "mentions": ["role:pm"],
300
+ "context": {
301
+ "category": "auth",
302
+ "attempted_resolution": "保存済み認証情報で再ログイン試行済み、2FA必須"
303
+ }
304
+ }
305
+ ```
306
+
307
+ POST `/api/help-threads` body (ディスカッションスレッド起票):
308
+ ```json
309
+ {
310
+ "project_id": "uuid",
311
+ "thread_type": "discussion",
312
+ "title": "デプロイ手順の確認",
313
+ "description": "本番デプロイ前にステージングで確認するフローに変えたい。意見ある?",
314
+ "mentions": ["role:engineer"]
315
+ }
316
+ ```
317
+
318
+ | Field | Type | Required | Description |
319
+ |-------|------|----------|-------------|
320
+ | `project_id` | string | Yes | プロジェクト UUID |
321
+ | `thread_type` | string | No | `help`(デフォルト)or `discussion` |
322
+ | `title` | string | Yes | スレッドの要約 |
323
+ | `description` | string | Yes | 詳細説明 |
324
+ | `mentions` | string[] | No | メンション対象。形式: `role:engineer`, `role:pm`, `minion:<minion_id>`, `user` |
325
+ | `context` | object | No | 任意のメタデータ(category, urgency, workflow_execution_id等) |
326
+
327
+ **thread_type の違い:**
328
+ - `help`: ブロッカー解決。`resolve` で解決
329
+ - `discussion`: チーム内ディスカッション。`close` で完了
330
+
331
+ **メンションルール:**
332
+ - メンションされたミニオンは優先的にスレッドを評価する
333
+ - メンションがない場合、全チームメンバーがLLMで関連性を判定してから参加
334
+ - トークン消費を抑えるため、当事者が明確な場合はメンションを推奨
335
+ - ミニオンが自力で解決できない場合、`@user` メンション付きで返信して人間に助けを求める
336
+
337
+ POST `/api/help-threads/:id/messages` body:
338
+ ```json
339
+ {
340
+ "content": "関連するプロジェクトメモリーが見つかりました: ...",
341
+ "mentions": ["user"]
342
+ }
343
+ ```
344
+
345
+ | Field | Type | Required | Description |
346
+ |-------|------|----------|-------------|
347
+ | `content` | string | Yes | メッセージ本文 |
348
+ | `attachments` | object | No | 添付ファイル情報(JSON) |
349
+ | `mentions` | string[] | No | メンション対象 |
350
+
351
+ POST `/api/help-threads/:id/resolve` body:
352
+ ```json
353
+ {
354
+ "resolution": "ユーザーから2FAコード(123456)を受け取りログイン成功"
355
+ }
356
+ ```
357
+
358
+ POST `/api/help-threads/:id/cancel` body (optional):
359
+ ```json
360
+ {
361
+ "reason": "誤って起票したため取り消し"
362
+ }
363
+ ```
364
+
365
+ スレッドのステータスは Supabase Realtime で配信されるため、ポーリングに加えて
366
+ HQ ダッシュボードではリアルタイムに更新が表示される。
367
+
368
+ ### Project Memories (プロジェクト共有知識)
369
+
370
+ プロジェクトレベルのチーム共有知識ベース。ヘルプスレッドの解決知見を蓄積して再利用する。
371
+ リクエストは HQ にプロキシされる。
372
+
373
+ | Method | Endpoint | Description |
374
+ |--------|----------|-------------|
375
+ | GET | `/api/project-memories` | メモリー検索。Query: `?project_id=&search=&category=&tags=&status=` |
376
+ | POST | `/api/project-memories` | メモリーを投稿 |
377
+
378
+ GET Query Parameters:
379
+
380
+ | Param | Required | Description |
381
+ |-------|----------|-------------|
382
+ | `project_id` | Yes | プロジェクト UUID |
383
+ | `search` | No | タイトル・内容でのテキスト検索 |
384
+ | `category` | No | カテゴリフィルター |
385
+ | `tags` | No | タグフィルター(カンマ区切り) |
386
+ | `status` | No | `active`(デフォルト), `outdated`, `archived` |
387
+
388
+ POST `/api/project-memories` body:
389
+ ```json
390
+ {
391
+ "project_id": "uuid",
392
+ "title": "ランサーズは2FA必須",
393
+ "content": "ランサーズはログイン時に2段階認証を要求する。メールで届くコードが必要なため、人間へのエスカレーションが必須。team_timeout=30sが適切。",
394
+ "category": "auth",
395
+ "tags": ["lancers", "2fa", "login"],
396
+ "source_thread_id": "uuid"
397
+ }
398
+ ```
399
+
400
+ | Field | Type | Required | Description |
401
+ |-------|------|----------|-------------|
402
+ | `project_id` | string | Yes | プロジェクト UUID |
403
+ | `title` | string | Yes | 知見のタイトル |
404
+ | `content` | string | Yes | 知見の内容 |
405
+ | `category` | string | Yes | `auth`, `environment`, `know-how`, `decision`, `preference` |
406
+ | `tags` | string[] | No | 自由タグ(検索用) |
407
+ | `source_thread_id` | string | No | 知見の出典ヘルプスレッド UUID |
408
+
409
+ **推奨ワークフロー:**
410
+ 1. ブロッカー発生 → `GET /api/project-memories?project_id=...&category=auth&search=2fa` で既知の知見を検索
411
+ 2. 該当あり → 知見に基づいて自己解決 or 即エスカレーション
412
+ 3. 該当なし → `POST /api/help-threads` でブロッカー起票
413
+ 4. 解決後 → `POST /api/project-memories` で知見を蓄積
414
+
247
415
  ### Commands
248
416
 
249
417
  | Method | Endpoint | Description |
@@ -601,3 +769,30 @@ Response:
601
769
  | POST | `/api/minion/skills` | スキル登録/更新(新バージョン自動作成) |
602
770
 
603
771
  スキルはバージョン管理される。push ごとに新バージョンが作成され、ファイルは Supabase Storage に保存される。
772
+
773
+ ### Help Threads (HQ)
774
+
775
+ | Method | Endpoint | Description |
776
+ |--------|----------|-------------|
777
+ | POST | `/api/minion/help-threads` | ブロッカースレッドを起票 |
778
+ | GET | `/api/minion/help-threads/open` | 参加プロジェクトの未解決スレッド一覧 |
779
+ | GET | `/api/minion/help-threads/:id` | スレッド詳細 + メッセージ一覧 |
780
+ | POST | `/api/minion/help-threads/:id/messages` | スレッドにメッセージを投稿 |
781
+ | PATCH | `/api/minion/help-threads/:id/resolve` | スレッドを解決済みにする。Body: `{resolution}` |
782
+ | PATCH | `/api/minion/help-threads/:id/cancel` | スレッドをキャンセル。Body: `{reason?}` |
783
+ | DELETE | `/api/minion/help-threads/:id` | スレッドを完全削除(PMのみ)。メッセージもCASCADE削除 |
784
+ | PATCH | `/api/minion/help-threads/:id/escalate` | スレッドを手動エスカレーション |
785
+
786
+ ローカルエージェントの `/api/help-threads` は上記 HQ API へのプロキシ。
787
+ 詳細なリクエスト/レスポンス仕様はローカル API セクションの「Help Threads」を参照。
788
+
789
+ ### Project Memories (HQ)
790
+
791
+ | Method | Endpoint | Description |
792
+ |--------|----------|-------------|
793
+ | POST | `/api/minion/project-memories` | プロジェクトメモリーを投稿 |
794
+ | GET | `/api/minion/project-memories` | メモリー検索。Query: `?project_id=&search=&category=&tags=&status=` |
795
+ | PATCH | `/api/minion/project-memories/:id` | メモリーを更新。Body: `{title?, content?, category?, tags?, status?}` |
796
+
797
+ ローカルエージェントの `/api/project-memories` は上記 HQ API へのプロキシ。
798
+ 詳細なリクエスト/レスポンス仕様はローカル API セクションの「Project Memories」を参照。
@@ -28,9 +28,20 @@ requires:
28
28
 
29
29
  ## MCP サーバーの設定
30
30
 
31
- MCP サーバーは `~/.mcp.json` に JSON 形式で設定する。このファイルが Claude Code セッション起動時に読み込まれる。
31
+ MCPサーバーの設定ファイルは使用するCLIツールによって異なる。
32
+ いずれも LLM から直接編集可能(パーミッション設定とは異なり、API経由は不要)。
32
33
 
33
- ### ファイル形式
34
+ ### CLI別の設定ファイル
35
+
36
+ | CLI | 設定ファイル | フォーマット | 編集方法 |
37
+ |-----|------------|------------|---------|
38
+ | Claude Code | `.mcp.json`(プロジェクトルート) | JSON | Edit/Write ツールで直接編集 |
39
+ | Gemini CLI | `.gemini/settings.json` | JSON | Edit/Write ツールで直接編集 |
40
+ | Codex CLI | `~/.codex/config.toml` | TOML | `codex mcp add/remove` コマンド推奨 |
41
+
42
+ ### Claude Code
43
+
44
+ 設定ファイル: `.mcp.json`(プロジェクトルート)または `~/.mcp.json`(ユーザーレベル)
34
45
 
35
46
  ```json
36
47
  {
@@ -43,9 +54,8 @@ MCP サーバーは `~/.mcp.json` に JSON 形式で設定する。このファ
43
54
  }
44
55
  ```
45
56
 
46
- ### 設定の追加・変更手順
47
-
48
- 1. `~/.mcp.json` が存在しない場合は新規作成する
57
+ **設定の追加・変更手順:**
58
+ 1. `.mcp.json` が存在しない場合は新規作成する
49
59
  2. 既存の場合は内容を読み取り、`mcpServers` オブジェクトにエントリを追加する
50
60
  3. 既存エントリを壊さないよう注意する
51
61
 
@@ -54,10 +64,54 @@ MCP サーバーは `~/.mcp.json` に JSON 形式で設定する。このファ
54
64
  cat ~/.mcp.json 2>/dev/null || echo '(not found)'
55
65
  ```
56
66
 
67
+ **スコープの優先順:**
68
+ 1. ローカル (`~/.claude.json` 内のプロジェクト固有設定) — 最優先
69
+ 2. プロジェクト (`.mcp.json`) — チーム共有
70
+ 3. ユーザー (`~/.claude.json`) — 全プロジェクト共通
71
+
72
+ ### Gemini CLI
73
+
74
+ 設定ファイル: `.gemini/settings.json`(プロジェクト)または `~/.gemini/settings.json`(ユーザーレベル)
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "<server-name>": {
80
+ "command": "<起動コマンド>",
81
+ "args": ["<引数1>", "<引数2>"],
82
+ "timeout": 30000
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ 追加フィールド: `cwd`(作業ディレクトリ)、`timeout`(ミリ秒)、`trust`(確認プロンプト省略)、`includeTools`/`excludeTools`(ツールフィルタ)
89
+
90
+ ### Codex CLI
91
+
92
+ 設定ファイル: `.codex/config.toml`(プロジェクト)または `~/.codex/config.toml`(ユーザーレベル)
93
+
94
+ ```toml
95
+ [mcp_servers.<server-name>]
96
+ command = "<起動コマンド>"
97
+ args = ["<引数1>", "<引数2>"]
98
+ startup_timeout_sec = 10.0
99
+ tool_timeout_sec = 60.0
100
+ ```
101
+
102
+ **注意:** `.codex/` ディレクトリは LLM からの直接編集が制限される場合がある。以下のコマンドを使用すること:
103
+
104
+ ```bash
105
+ codex mcp add <name> --command "<command>" --args "<arg1>,<arg2>"
106
+ codex mcp remove <name>
107
+ codex mcp list
108
+ ```
109
+
57
110
  ### よく使う MCP サーバーの設定例
58
111
 
59
112
  #### Playwright(ブラウザ自動化)
60
113
 
114
+ **Claude Code / Gemini CLI:**
61
115
  ```json
62
116
  {
63
117
  "mcpServers": {
@@ -69,10 +123,18 @@ cat ~/.mcp.json 2>/dev/null || echo '(not found)'
69
123
  }
70
124
  ```
71
125
 
126
+ **Codex CLI:**
127
+ ```toml
128
+ [mcp_servers.playwright]
129
+ command = "npx"
130
+ args = ["-y", "@playwright/mcp@latest"]
131
+ ```
132
+
72
133
  `npx -y` により、未インストールでも自動ダウンロード・実行される。事前の `npm install` は不要。
73
134
 
74
135
  #### Supabase(データベース)
75
136
 
137
+ **Claude Code / Gemini CLI:**
76
138
  ```json
77
139
  {
78
140
  "mcpServers": {
@@ -83,14 +145,20 @@ cat ~/.mcp.json 2>/dev/null || echo '(not found)'
83
145
  }
84
146
  ```
85
147
 
148
+ **Codex CLI:**
149
+ ```toml
150
+ [mcp_servers.supabase]
151
+ url = "http://<supabase-host>:54321/mcp?read_only=true&features=database,docs"
152
+ ```
153
+
86
154
  URL ベースの MCP サーバーは `url` フィールドで指定する(`command`/`args` は不要)。
87
155
 
88
156
  ### 注意事項
89
157
 
90
- - `~/.mcp.json` は手動で編集する。Claude Code の設定 UI からは変更できない
158
+ - MCP設定ファイルはLLMから直接編集できる(パーミッション設定ファイルとは異なり書き込み保護がない)
91
159
  - `npx -y <package>` 形式を使えば、グローバルインストールなしで MCP サーバーを起動できる
92
160
  - サーバー名はスキルの `requires.mcp_servers` と一致させる必要がある(例: `playwright`)
93
- - `claude-settings.json`(`~/.claude/settings.json`)の `mcpServers` に設定しても同様に動作するが、`~/.mcp.json` が推奨
161
+ - Claude Code では `.mcp.json`(プロジェクトルート)が推奨。`~/.claude/settings.json` `mcpServers` には設定しない
94
162
 
95
163
  ---
96
164
 
@@ -12,6 +12,7 @@
12
12
  # sudo minion-cli restart # Restart agent service (root)
13
13
  # minion-cli status # Get current status
14
14
  # minion-cli health # Health check
15
+ # minion-cli daemons # Daemon status
15
16
  # minion-cli diagnose # Run full service diagnostics
16
17
  # minion-cli set-status busy "Running X" # Set status and task
17
18
  # minion-cli set-status online # Set status only
@@ -814,6 +815,7 @@ CFEOF
814
815
  echo "Useful commands:"
815
816
  echo " minion-cli status # Agent status"
816
817
  echo " minion-cli health # Health check"
818
+ echo " minion-cli daemons # Daemon status"
817
819
  echo " sudo minion-cli restart # Restart agent"
818
820
  echo " sudo minion-cli stop # Stop agent"
819
821
  if [ "$PROC_MGR" = "systemd" ]; then
@@ -1219,6 +1221,10 @@ case "${1:-}" in
1219
1221
  curl -s "$AGENT_URL/api/health" | jq .
1220
1222
  ;;
1221
1223
 
1224
+ daemons)
1225
+ curl -s "$AGENT_URL/api/daemons/status" | jq .
1226
+ ;;
1227
+
1222
1228
  diagnose)
1223
1229
  echo "Running diagnostics..."
1224
1230
  echo ""
package/linux/server.js CHANGED
@@ -43,6 +43,7 @@ const routineStore = require('../core/stores/routine-store')
43
43
  // Heartbeat interval: fixed at 30s (not user-configurable)
44
44
  const HEARTBEAT_INTERVAL_MS = 30_000
45
45
  let heartbeatTimer = null
46
+ let lastBeatAt = null
46
47
 
47
48
  // Linux-specific modules
48
49
  const workflowRunner = require('./workflow-runner')
@@ -50,10 +51,14 @@ const routineRunner = require('./routine-runner')
50
51
  const { cleanupTtyd, killStaleTtydProcesses } = require('./routes/terminal')
51
52
  const { startTerminalProxy, stopTerminalProxy } = require('./terminal-proxy')
52
53
 
54
+ // Config warnings (included in heartbeat)
55
+ const { getConfigWarnings } = require('../core/lib/config-warnings')
56
+
53
57
  // Pull-model daemons (from core/)
54
58
  const stepPoller = require('../core/lib/step-poller')
55
59
  const revisionWatcher = require('../core/lib/revision-watcher')
56
60
  const reflectionScheduler = require('../core/lib/reflection-scheduler')
61
+ const threadWatcher = require('../core/lib/thread-watcher')
57
62
  const runningTasks = require('../core/lib/running-tasks')
58
63
 
59
64
  // Shared routes (from core/)
@@ -68,6 +73,8 @@ const { memoryRoutes } = require('../core/routes/memory')
68
73
  const { dailyLogRoutes } = require('../core/routes/daily-logs')
69
74
  const { sudoersRoutes } = require('../core/routes/sudoers')
70
75
  const { permissionRoutes } = require('../core/routes/permissions')
76
+ const { helpThreadRoutes } = require('../core/routes/help-threads')
77
+ const { daemonRoutes } = require('../core/routes/daemons')
71
78
 
72
79
  // Linux-specific routes
73
80
  const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
@@ -113,6 +120,7 @@ async function shutdown(signal) {
113
120
  stepPoller.stop()
114
121
  revisionWatcher.stop()
115
122
  reflectionScheduler.stop()
123
+ threadWatcher.stop()
116
124
  workflowRunner.stopAll()
117
125
  routineRunner.stopAll()
118
126
 
@@ -270,6 +278,8 @@ async function registerAllRoutes(app) {
270
278
  await app.register(dailyLogRoutes)
271
279
  await app.register(sudoersRoutes)
272
280
  await app.register(permissionRoutes)
281
+ await app.register(helpThreadRoutes)
282
+ await app.register(daemonRoutes, { heartbeatStatus: () => ({ running: !!heartbeatTimer, last_beat_at: lastBeatAt }) })
273
283
 
274
284
  // Linux-specific routes
275
285
  await app.register(commandRoutes)
@@ -353,14 +363,16 @@ async function start() {
353
363
  // Send initial online heartbeat
354
364
  const { getStatus } = require('../core/routes/health')
355
365
  const { currentTask } = getStatus()
356
- sendHeartbeat({ status: 'online', current_task: currentTask, running_tasks: runningTasks.getAll(), version }).catch(err => {
366
+ sendHeartbeat({ status: 'online', current_task: currentTask, running_tasks: runningTasks.getAll(), config_warnings: getConfigWarnings(), version }).catch(err => {
357
367
  console.error('[Heartbeat] Initial heartbeat failed:', err.message)
358
368
  })
359
369
 
360
370
  // Start periodic heartbeat
361
371
  heartbeatTimer = setInterval(() => {
362
372
  const { currentStatus, currentTask } = getStatus()
363
- sendHeartbeat({ status: currentStatus, current_task: currentTask, running_tasks: runningTasks.getAll(), version }).catch(err => {
373
+ sendHeartbeat({ status: currentStatus, current_task: currentTask, running_tasks: runningTasks.getAll(), config_warnings: getConfigWarnings(), version }).then(() => {
374
+ lastBeatAt = new Date().toISOString()
375
+ }).catch(err => {
364
376
  console.error('[Heartbeat] Periodic heartbeat failed:', err.message)
365
377
  })
366
378
  }, HEARTBEAT_INTERVAL_MS)
@@ -369,6 +381,7 @@ async function start() {
369
381
  // Start Pull-model daemons
370
382
  stepPoller.start()
371
383
  revisionWatcher.start()
384
+ threadWatcher.start(runQuickLlmCall)
372
385
  } else {
373
386
  console.log('[Server] Running in standalone mode (no HQ connection)')
374
387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "2.60.0",
3
+ "version": "2.67.0",
4
4
  "description": "AI Agent runtime for Minion - manages status and skill deployment on VPS",
5
5
  "main": "linux/server.js",
6
6
  "bin": {
package/rules/core.md CHANGED
@@ -67,6 +67,51 @@ curl -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application
67
67
 
68
68
  対応CLI: `claude-code` (.claude/settings.local.json), `gemini` (.gemini/settings.json), `codex` (.codex/config.toml)
69
69
 
70
+ #### MCP Server Configuration
71
+
72
+ MCPサーバーの設定は各CLIの設定ファイルを直接編集して行う(APIは不要)。
73
+ パーミッション(allow/deny)とは異なり、MCP設定ファイルはLLMから直接編集可能。
74
+
75
+ | CLI | 設定ファイル | 編集方法 |
76
+ |-----|------------|---------|
77
+ | Claude Code | `.mcp.json`(プロジェクトルート) | 直接編集(Write/Edit ツール) |
78
+ | Gemini CLI | `.gemini/settings.json` | 直接編集 |
79
+ | Codex CLI | `.codex/config.toml` | `codex mcp add/remove` コマンドを使用 |
80
+
81
+ **Claude Code** — `.mcp.json` を編集:
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "playwright": {
86
+ "command": "npx",
87
+ "args": ["-y", "@playwright/mcp@latest"]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ **Gemini CLI** — `.gemini/settings.json` の `mcpServers` を編集:
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "playwright": {
98
+ "command": "npx",
99
+ "args": ["-y", "@playwright/mcp@latest"],
100
+ "timeout": 30000
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
106
+ **Codex CLI** — `codex mcp add` コマンド、または `~/.codex/config.toml` を編集:
107
+ ```toml
108
+ [mcp_servers.playwright]
109
+ command = "npx"
110
+ args = ["-y", "@playwright/mcp@latest"]
111
+ ```
112
+
113
+ Note: Codex CLI の `.codex/` ディレクトリはLLMからの直接編集が制限される場合がある。その場合は `codex mcp add` コマンドを使用すること。
114
+
70
115
  ### HQ API
71
116
 
72
117
  `$HQ_URL/api/minion/*` — 認証: `Authorization: Bearer $API_TOKEN`