@geekbeer/minion 3.9.0 → 3.11.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/core/api.js +6 -5
- package/core/lib/thread-watcher.js +46 -22
- package/docs/api-reference.md +20 -22
- package/docs/environment-setup.md +80 -0
- package/package.json +1 -1
- package/rules/core.md +9 -10
- package/win/bin/wsl-session-entry.js +89 -0
- package/win/minion-cli.ps1 +34 -0
- package/win/routes/chat.js +103 -2
- package/win/routes/terminal.js +121 -3
- package/win/server.js +22 -0
- package/win/terminal-server.js +62 -0
- package/win/wsl-session-server.js +503 -0
package/core/api.js
CHANGED
|
@@ -87,8 +87,8 @@ async function sendHeartbeat(data) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* Create a thread
|
|
91
|
-
* @param {object} data - { project_id?,
|
|
90
|
+
* Create a thread on HQ (workspace-scoped, optionally linked to projects).
|
|
91
|
+
* @param {object} data - { project_id?, project_ids?, category?, title, content, thread_type?, mentions?, context? }
|
|
92
92
|
* @returns {Promise<{ thread: object }>}
|
|
93
93
|
*/
|
|
94
94
|
async function createThread(data) {
|
|
@@ -100,11 +100,12 @@ async function createThread(data) {
|
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
102
|
* Get open threads accessible to this minion.
|
|
103
|
-
* @param {
|
|
103
|
+
* @param {string} [projectId] - Optional project ID to filter by
|
|
104
104
|
* @returns {Promise<{ threads: object[] }>}
|
|
105
105
|
*/
|
|
106
|
-
async function getOpenThreads(
|
|
107
|
-
|
|
106
|
+
async function getOpenThreads(projectId) {
|
|
107
|
+
const params = projectId ? `?project_id=${projectId}` : ''
|
|
108
|
+
return request(`/threads/open${params}`)
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Project Thread Watcher
|
|
3
3
|
*
|
|
4
4
|
* Polling daemon that monitors open help/discussion threads in the minion's
|
|
5
|
-
*
|
|
5
|
+
* workspace. Uses LLM to decide whether to participate in conversations.
|
|
6
6
|
*
|
|
7
7
|
* Token-saving strategies:
|
|
8
8
|
* 1. Read tracking: only evaluate threads with new messages since last check
|
|
@@ -68,7 +68,7 @@ async function getMyProjects() {
|
|
|
68
68
|
*
|
|
69
69
|
* @param {object} thread
|
|
70
70
|
* @param {object[]} messages - Only new messages to check
|
|
71
|
-
* @param {string} myRole - This minion's role in the project
|
|
71
|
+
* @param {string} myRole - This minion's role in the relevant project (or 'engineer' default)
|
|
72
72
|
* @returns {boolean}
|
|
73
73
|
*/
|
|
74
74
|
function isMentioned(thread, messages, myRole) {
|
|
@@ -130,14 +130,20 @@ async function pollOnce() {
|
|
|
130
130
|
* Process a single thread: check for new activity and evaluate if needed.
|
|
131
131
|
*/
|
|
132
132
|
async function processThread(thread, myProjects, now) {
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
let
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
// Determine role from linked projects (use first matching project)
|
|
134
|
+
let myRole = 'engineer'
|
|
135
|
+
let projectContext = null
|
|
136
|
+
const linkedProjects = thread.linked_projects || []
|
|
137
|
+
|
|
138
|
+
if (linkedProjects.length > 0) {
|
|
139
|
+
for (const lp of linkedProjects) {
|
|
140
|
+
const myProject = myProjects.find(p => p.id === lp.id)
|
|
141
|
+
if (myProject) {
|
|
142
|
+
myRole = myProject.role || 'engineer'
|
|
143
|
+
projectContext = myProject
|
|
144
|
+
break
|
|
145
|
+
}
|
|
146
|
+
}
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
const state = readState.get(thread.id) || { lastMessageCount: 0, lastEvalAt: 0 }
|
|
@@ -172,7 +178,7 @@ async function processThread(thread, myProjects, now) {
|
|
|
172
178
|
readState.set(thread.id, { ...state, lastMessageCount: messageCount })
|
|
173
179
|
|
|
174
180
|
// Check mentions first (no LLM needed to decide relevance)
|
|
175
|
-
const mentioned = isMentioned(thread, newMessages,
|
|
181
|
+
const mentioned = isMentioned(thread, newMessages, myRole)
|
|
176
182
|
|
|
177
183
|
// If not mentioned and cooldown not expired, skip LLM evaluation
|
|
178
184
|
if (!mentioned && now - state.lastEvalAt < EVAL_COOLDOWN_MS) {
|
|
@@ -187,14 +193,14 @@ async function processThread(thread, myProjects, now) {
|
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
// LLM evaluation
|
|
190
|
-
await evaluateWithLlm(thread, detail.thread, messages, newMessages,
|
|
196
|
+
await evaluateWithLlm(thread, detail.thread, messages, newMessages, myRole, projectContext, mentioned)
|
|
191
197
|
readState.set(thread.id, { lastMessageCount: messageCount, lastEvalAt: now })
|
|
192
198
|
}
|
|
193
199
|
|
|
194
200
|
/**
|
|
195
201
|
* Use LLM to evaluate thread and potentially respond.
|
|
196
202
|
*/
|
|
197
|
-
async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMessages,
|
|
203
|
+
async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMessages, myRole, projectContext, mentioned) {
|
|
198
204
|
// Build conversation context (limit to last 20 messages to control tokens)
|
|
199
205
|
const recentMessages = allMessages.slice(-20)
|
|
200
206
|
const messageHistory = recentMessages
|
|
@@ -218,10 +224,21 @@ async function evaluateWithLlm(threadSummary, threadDetail, allMessages, newMess
|
|
|
218
224
|
ctx.attempted_resolution ? `試行済み: ${ctx.attempted_resolution}` : '',
|
|
219
225
|
].filter(Boolean).join('\n')
|
|
220
226
|
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
// Build scope context from linked projects
|
|
228
|
+
const linkedProjects = threadDetail.linked_projects || []
|
|
229
|
+
let scopeContext
|
|
230
|
+
if (projectContext) {
|
|
231
|
+
scopeContext = `あなたはプロジェクト「${projectContext.name}」のチームメンバー(ロール: ${myRole}、ID: ${config.MINION_ID})です。`
|
|
232
|
+
} else if (linkedProjects.length > 0) {
|
|
233
|
+
const projectNames = linkedProjects.map(p => p.name).join(', ')
|
|
234
|
+
scopeContext = `あなたはワークスペースのメンバー(ID: ${config.MINION_ID})です。\nこのスレッドは以下のプロジェクトに紐づいています: ${projectNames}`
|
|
235
|
+
} else {
|
|
236
|
+
scopeContext = `あなたはワークスペースのメンバー(ID: ${config.MINION_ID})です。\nこれはプロジェクトに紐づかないワークスペーススレッドです(カテゴリ: ${threadDetail.category || 'general'})。`
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const roleGuidance = projectContext
|
|
240
|
+
? `- 自分のロール(${myRole})に関連する話題か`
|
|
241
|
+
: '- ワークスペーススレッドではすべてのミニオンが参加可能。自分が貢献できる場合は積極的に参加する'
|
|
225
242
|
|
|
226
243
|
const prompt = `${scopeContext}
|
|
227
244
|
以下のスレッドに対して、あなたが返信すべきかどうかを判断し、返信する場合はその内容を生成してください。
|
|
@@ -247,7 +264,7 @@ ${messageHistory || '(メッセージなし)'}
|
|
|
247
264
|
判断基準:
|
|
248
265
|
- 自分が起票したスレッドの場合、他のメンバーの回答を待つべき(追加情報がある場合を除く)
|
|
249
266
|
- メンション対象が特定のロールやミニオンに限定されている場合、自分が対象でなければ静観する
|
|
250
|
-
${
|
|
267
|
+
${roleGuidance}
|
|
251
268
|
- 自分が貢献できる知見や意見があるか
|
|
252
269
|
- 既に十分な回答がある場合は重複を避ける
|
|
253
270
|
- 人間に聞くべき場合は @user メンションを含めて返信する
|
|
@@ -289,13 +306,17 @@ ${isWorkspace ? '- ワークスペーススレッドではすべてのミニオ
|
|
|
289
306
|
if (parsed.todo && parsed.todo.title) {
|
|
290
307
|
try {
|
|
291
308
|
const todoStore = require('../stores/todo-store')
|
|
309
|
+
// Use first linked project ID if available
|
|
310
|
+
const linkedProjectId = (threadSummary.linked_projects && threadSummary.linked_projects.length > 0)
|
|
311
|
+
? threadSummary.linked_projects[0].id
|
|
312
|
+
: null
|
|
292
313
|
todoStore.add({
|
|
293
314
|
title: parsed.todo.title,
|
|
294
315
|
priority: parsed.todo.priority || 'normal',
|
|
295
316
|
due_at: parsed.todo.due_at || null,
|
|
296
317
|
source_type: 'thread',
|
|
297
318
|
source_id: threadSummary.id,
|
|
298
|
-
project_id:
|
|
319
|
+
project_id: linkedProjectId,
|
|
299
320
|
})
|
|
300
321
|
console.log(`[ThreadWatcher] Created TODO from thread: "${parsed.todo.title}"`)
|
|
301
322
|
} catch (err) {
|
|
@@ -309,17 +330,20 @@ ${isWorkspace ? '- ワークスペーススレッドではすべてのミニオ
|
|
|
309
330
|
|
|
310
331
|
/**
|
|
311
332
|
* Fallback: memory-only matching when LLM is not available.
|
|
333
|
+
* Uses linked projects to search for relevant memories.
|
|
312
334
|
*/
|
|
313
335
|
async function fallbackMemoryMatch(thread) {
|
|
314
336
|
try {
|
|
315
|
-
|
|
316
|
-
if (
|
|
337
|
+
const linkedProjects = thread.linked_projects || []
|
|
338
|
+
if (linkedProjects.length === 0) return
|
|
317
339
|
|
|
340
|
+
// Search memories from the first linked project
|
|
341
|
+
const projectId = linkedProjects[0].id
|
|
318
342
|
const ctx = thread.context || {}
|
|
319
343
|
const category = ctx.category || ''
|
|
320
344
|
const searchParam = category ? `&category=${category}` : ''
|
|
321
345
|
const memData = await api.request(
|
|
322
|
-
`/project-memories?project_id=${
|
|
346
|
+
`/project-memories?project_id=${projectId}${searchParam}`
|
|
323
347
|
)
|
|
324
348
|
|
|
325
349
|
if (!memData.memories || memData.memories.length === 0) return
|
package/docs/api-reference.md
CHANGED
|
@@ -287,27 +287,26 @@ GET `/api/daemons/status` response:
|
|
|
287
287
|
|
|
288
288
|
### Threads (スレッド)
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
ワークスペースレベルのコミュニケーションチャネル。ブロッカー共有やチーム議論に使う。
|
|
291
291
|
リクエストは HQ にプロキシされる。
|
|
292
292
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
- **Workspace** (`scope: "workspace"`): プロジェクトに属さないスレッド。ルーティンのブロッカー、全体連絡等。オーナーの全ミニオンが参加。
|
|
293
|
+
すべてのスレッドはワークスペースに所属し、プロジェクトへの紐づけは多対多(`thread_project_links`)で管理される。
|
|
294
|
+
1つのスレッドを複数プロジェクトにリンクしたり、プロジェクトに紐づけずにワークスペース全体のスレッドとして使うこともできる。
|
|
296
295
|
|
|
297
296
|
| Method | Endpoint | Description |
|
|
298
297
|
|--------|----------|-------------|
|
|
299
|
-
| GET | `/api/threads` |
|
|
298
|
+
| GET | `/api/threads` | オープンスレッド一覧 |
|
|
300
299
|
| POST | `/api/threads` | スレッドを起票(初回メッセージを同時作成) |
|
|
301
300
|
| GET | `/api/threads/:id` | スレッド詳細 + メッセージ一覧 |
|
|
302
301
|
| POST | `/api/threads/:id/messages` | スレッドにメッセージを投稿 |
|
|
303
302
|
| POST | `/api/threads/:id/resolve` | スレッドを解決済みにする |
|
|
304
303
|
| POST | `/api/threads/:id/cancel` | スレッドをキャンセル |
|
|
305
|
-
| DELETE | `/api/threads/:id` |
|
|
304
|
+
| DELETE | `/api/threads/:id` | スレッドを完全削除 |
|
|
306
305
|
|
|
307
|
-
POST `/api/threads` body (
|
|
306
|
+
POST `/api/threads` body (プロジェクト紐づきヘルプスレッド):
|
|
308
307
|
```json
|
|
309
308
|
{
|
|
310
|
-
"
|
|
309
|
+
"project_ids": ["uuid"],
|
|
311
310
|
"thread_type": "help",
|
|
312
311
|
"title": "ランサーズの2FA認証コードが必要",
|
|
313
312
|
"content": "ログイン時に2段階認証を要求された。メールで届く6桁コードの入力が必要。",
|
|
@@ -322,7 +321,6 @@ POST `/api/threads` body (プロジェクトスレッド — ヘルプ):
|
|
|
322
321
|
POST `/api/threads` body (ワークスペーススレッド — ルーティンのブロッカー):
|
|
323
322
|
```json
|
|
324
323
|
{
|
|
325
|
-
"scope": "workspace",
|
|
326
324
|
"category": "general",
|
|
327
325
|
"thread_type": "help",
|
|
328
326
|
"title": "朝作業ルーティン: APIキーの期限切れ",
|
|
@@ -335,10 +333,10 @@ POST `/api/threads` body (ワークスペーススレッド — ルーティン
|
|
|
335
333
|
}
|
|
336
334
|
```
|
|
337
335
|
|
|
338
|
-
POST `/api/threads` body (
|
|
336
|
+
POST `/api/threads` body (プロジェクト紐づきディスカッション):
|
|
339
337
|
```json
|
|
340
338
|
{
|
|
341
|
-
"
|
|
339
|
+
"project_ids": ["uuid"],
|
|
342
340
|
"thread_type": "discussion",
|
|
343
341
|
"title": "デプロイ手順の確認",
|
|
344
342
|
"content": "本番デプロイ前にステージングで確認するフローに変えたい。意見ある?",
|
|
@@ -348,19 +346,19 @@ POST `/api/threads` body (プロジェクトスレッド — ディスカッシ
|
|
|
348
346
|
|
|
349
347
|
| Field | Type | Required | Description |
|
|
350
348
|
|-------|------|----------|-------------|
|
|
351
|
-
| `
|
|
352
|
-
| `
|
|
353
|
-
| `category` | string | No |
|
|
349
|
+
| `project_id` | string | No | リンクするプロジェクトUUID(単一、後方互換) |
|
|
350
|
+
| `project_ids` | string[] | No | リンクするプロジェクトUUID(複数対応) |
|
|
351
|
+
| `category` | string | No | カテゴリ: `general`(デフォルト), `ops`, `standup` |
|
|
354
352
|
| `thread_type` | string | No | `help`(デフォルト)or `discussion` |
|
|
355
353
|
| `title` | string | Yes | スレッドの要約 |
|
|
356
354
|
| `content` | string | Yes | スレッド本文(thread_messagesの最初のメッセージとして保存) |
|
|
357
355
|
| `mentions` | string[] | No | メンション対象。形式: `role:engineer`, `role:pm`, `minion:<minion_id>`, `user` |
|
|
358
356
|
| `context` | object | No | 任意のメタデータ(category, urgency, workflow_execution_id等) |
|
|
359
357
|
|
|
360
|
-
|
|
361
|
-
- ワークフロー実行中(プロジェクトあり)→ `
|
|
362
|
-
- ルーティン実行中(プロジェクトなし)→ `
|
|
363
|
-
- プロジェクト外の一般的な質問・報告 → `
|
|
358
|
+
**プロジェクト紐づけの使い分け:**
|
|
359
|
+
- ワークフロー実行中(プロジェクトあり)→ `project_ids: ["uuid"]`
|
|
360
|
+
- ルーティン実行中(プロジェクトなし)→ `project_ids` を省略
|
|
361
|
+
- プロジェクト外の一般的な質問・報告 → `project_ids` を省略 + `category`
|
|
364
362
|
|
|
365
363
|
**thread_type の違い:**
|
|
366
364
|
- `help`: ブロッカー解決。`resolve` で解決
|
|
@@ -447,8 +445,8 @@ POST `/api/project-memories` body:
|
|
|
447
445
|
**推奨ワークフロー:**
|
|
448
446
|
1. ブロッカー発生 → プロジェクトコンテキストの場合は `GET /api/project-memories?project_id=...&category=auth&search=2fa` で既知の知見を検索
|
|
449
447
|
2. 該当あり → 知見に基づいて自己解決 or 即エスカレーション
|
|
450
|
-
3. 該当なし → `POST /api/threads`
|
|
451
|
-
4. 解決後 →
|
|
448
|
+
3. 該当なし → `POST /api/threads` でブロッカー起票(プロジェクトありなら `project_ids: ["uuid"]`、なしなら省略)
|
|
449
|
+
4. 解決後 → プロジェクト紐づきスレッドの場合は `POST /api/project-memories` で知見を蓄積
|
|
452
450
|
|
|
453
451
|
### Email
|
|
454
452
|
|
|
@@ -909,8 +907,8 @@ Response:
|
|
|
909
907
|
|
|
910
908
|
| Method | Endpoint | Description |
|
|
911
909
|
|--------|----------|-------------|
|
|
912
|
-
| POST | `/api/minion/threads` | スレッドを起票(
|
|
913
|
-
| GET | `/api/minion/threads/open` | 未解決スレッド一覧(`?
|
|
910
|
+
| POST | `/api/minion/threads` | スレッドを起票(project_ids, category対応。初回メッセージを同時作成) |
|
|
911
|
+
| GET | `/api/minion/threads/open` | 未解決スレッド一覧(`?project_id=uuid` で絞り込み可) |
|
|
914
912
|
| GET | `/api/minion/threads/:id` | スレッド詳細 + メッセージ一覧 |
|
|
915
913
|
| POST | `/api/minion/threads/:id/messages` | スレッドにメッセージを投稿 |
|
|
916
914
|
| PATCH | `/api/minion/threads/:id/resolve` | スレッドを解決済みにする。Body: `{resolution}` |
|
|
@@ -162,6 +162,86 @@ URL ベースの MCP サーバーは `url` フィールドで指定する(`com
|
|
|
162
162
|
|
|
163
163
|
---
|
|
164
164
|
|
|
165
|
+
## WSL セッション(Windows ミニオン限定)
|
|
166
|
+
|
|
167
|
+
Windows ミニオンで WSL(Windows Subsystem for Linux)内の Docker やリポジトリ操作を行うための機能。
|
|
168
|
+
|
|
169
|
+
### 概要
|
|
170
|
+
|
|
171
|
+
minion-agent は LocalSystem (Session 0) で動作するため、ユーザー単位の WSL ディストリビューションに直接アクセスできない。WSL セッションサーバーがユーザーセッションで別プロセスとして動作し、minion-agent がリクエストをプロキシする。
|
|
172
|
+
|
|
173
|
+
### 前提条件
|
|
174
|
+
|
|
175
|
+
- WSL 2 がインストール済み(`wsl --install` で導入可能)
|
|
176
|
+
- Linux ディストリビューション(Ubuntu 等)がセットアップ済み
|
|
177
|
+
- Docker Desktop for Windows がインストール済み(WSL 2 バックエンド有効)
|
|
178
|
+
- ターゲットユーザーがログイン中(schtasks ONLOGON でサーバーが起動するため)
|
|
179
|
+
|
|
180
|
+
### セットアップ
|
|
181
|
+
|
|
182
|
+
`minion-cli setup` を実行すると、WSL が検出された場合に自動で登録される。手動で確認・起動する場合:
|
|
183
|
+
|
|
184
|
+
```powershell
|
|
185
|
+
# WSL セッションサーバーの状態確認
|
|
186
|
+
schtasks /Query /TN "MinionWSL" /FO LIST
|
|
187
|
+
|
|
188
|
+
# 手動起動(テスト用)
|
|
189
|
+
schtasks /Run /TN "MinionWSL"
|
|
190
|
+
|
|
191
|
+
# ヘルスチェック
|
|
192
|
+
curl http://localhost:7682/api/wsl/health
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 使い方
|
|
196
|
+
|
|
197
|
+
#### ターミナルセッション
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# WSL ターミナルセッション作成
|
|
201
|
+
curl -X POST http://localhost:8080/api/terminal/create \
|
|
202
|
+
-H "Authorization: Bearer $API_TOKEN" \
|
|
203
|
+
-H "Content-Type: application/json" \
|
|
204
|
+
-d '{"name": "wsl-dev", "type": "wsl"}'
|
|
205
|
+
|
|
206
|
+
# コマンド送信
|
|
207
|
+
curl -X POST http://localhost:8080/api/terminal/send \
|
|
208
|
+
-H "Authorization: Bearer $API_TOKEN" \
|
|
209
|
+
-H "Content-Type: application/json" \
|
|
210
|
+
-d '{"session": "wsl-dev", "input": "docker compose up -d", "enter": true}'
|
|
211
|
+
|
|
212
|
+
# WSL セッションサーバーの稼働状態確認
|
|
213
|
+
curl http://localhost:8080/api/terminal/wsl/status \
|
|
214
|
+
-H "Authorization: Bearer $API_TOKEN"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### チャット(WSL モード)
|
|
218
|
+
|
|
219
|
+
チャット API に `wsl_mode: true` を追加すると、Claude Code CLI がユーザーセッションで実行され、`wsl` コマンドを直接使用できる。
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
curl -X POST http://localhost:8080/api/chat \
|
|
223
|
+
-H "Authorization: Bearer $API_TOKEN" \
|
|
224
|
+
-H "Content-Type: application/json" \
|
|
225
|
+
-d '{"message": "WSL内でリポジトリをクローンしてDockerを起動して", "wsl_mode": true}'
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### ポート構成
|
|
229
|
+
|
|
230
|
+
| ポート | サービス | 備考 |
|
|
231
|
+
|--------|---------|------|
|
|
232
|
+
| 7682 | WSL session server (HTTP API) | localhost のみ |
|
|
233
|
+
| 7683 | WSL session server (WebSocket) | localhost のみ、ttyd プロトコル |
|
|
234
|
+
|
|
235
|
+
### トラブルシューティング
|
|
236
|
+
|
|
237
|
+
| 問題 | 原因 | 対処 |
|
|
238
|
+
|------|------|------|
|
|
239
|
+
| WSL session server is not running | ユーザーが未ログイン | RDP/コンソールでログイン |
|
|
240
|
+
| WSL not detected during setup | WSL 未インストール | `wsl --install` を実行後 `minion-cli setup` を再実行 |
|
|
241
|
+
| Connection refused on port 7682 | サーバー異常終了 | `schtasks /Run /TN "MinionWSL"` で再起動 |
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
165
245
|
## パッケージのインストール
|
|
166
246
|
|
|
167
247
|
スキルが `requires.packages` で宣言したパッケージは、対応するパッケージマネージャーの list コマンドで検出される。
|
package/package.json
CHANGED
package/rules/core.md
CHANGED
|
@@ -198,10 +198,10 @@ Routine 実行中は以下もtmuxセッション環境で利用可能:
|
|
|
198
198
|
|
|
199
199
|
2-b. 該当なし or 自己解決不可 → ヘルプスレッドを起票
|
|
200
200
|
|
|
201
|
-
■ ワークフロー実行中(プロジェクトあり)→
|
|
201
|
+
■ ワークフロー実行中(プロジェクトあり)→ プロジェクト紐づきスレッド:
|
|
202
202
|
POST /api/threads
|
|
203
203
|
{
|
|
204
|
-
"
|
|
204
|
+
"project_ids": ["..."],
|
|
205
205
|
"thread_type": "help",
|
|
206
206
|
"title": "問題の要約(1行)",
|
|
207
207
|
"content": "状況の詳細説明",
|
|
@@ -215,7 +215,6 @@ Routine 実行中は以下もtmuxセッション環境で利用可能:
|
|
|
215
215
|
■ ルーティン実行中(プロジェクトなし)→ ワークスペーススレッド:
|
|
216
216
|
POST /api/threads
|
|
217
217
|
{
|
|
218
|
-
"scope": "workspace",
|
|
219
218
|
"category": "general",
|
|
220
219
|
"thread_type": "help",
|
|
221
220
|
"title": "問題の要約(1行)",
|
|
@@ -229,7 +228,7 @@ Routine 実行中は以下もtmuxセッション環境で利用可能:
|
|
|
229
228
|
|
|
230
229
|
3. スレッドの返信を待つ(thread_watcher が自動監視)
|
|
231
230
|
|
|
232
|
-
4. 解決後 →
|
|
231
|
+
4. 解決後 → プロジェクト紐づきスレッドの場合はプロジェクトメモリーに知見を保存
|
|
233
232
|
POST /api/project-memories
|
|
234
233
|
{
|
|
235
234
|
"project_id": "...",
|
|
@@ -240,13 +239,13 @@ Routine 実行中は以下もtmuxセッション環境で利用可能:
|
|
|
240
239
|
}
|
|
241
240
|
```
|
|
242
241
|
|
|
243
|
-
###
|
|
242
|
+
### スレッドのプロジェクト紐づけ
|
|
244
243
|
|
|
245
|
-
| 状況 |
|
|
246
|
-
|
|
247
|
-
| ワークフロー実行中のブロッカー | `
|
|
248
|
-
| ルーティン実行中のブロッカー | `
|
|
249
|
-
| プロジェクト外の一般的な質問・報告 | `
|
|
244
|
+
| 状況 | 設定 | 説明 |
|
|
245
|
+
|------|------|------|
|
|
246
|
+
| ワークフロー実行中のブロッカー | `project_ids: ["uuid"]` | プロジェクトに紐づけ。プロジェクトビューに表示される |
|
|
247
|
+
| ルーティン実行中のブロッカー | `project_ids` を省略 | プロジェクトに紐づけない。ワークスペース全体に表示 |
|
|
248
|
+
| プロジェクト外の一般的な質問・報告 | `project_ids` を省略 + `category` | カテゴリ: `general`, `ops`, `standup` |
|
|
250
249
|
|
|
251
250
|
### メンションの使い分け
|
|
252
251
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* WSL Session Server Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Designed to be launched via schtasks ONLOGON so it runs in the
|
|
6
|
+
* target user's interactive session (not LocalSystem/Session 0).
|
|
7
|
+
*
|
|
8
|
+
* Reads configuration from .minion/.env, sets up environment,
|
|
9
|
+
* then starts the WSL session server.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs')
|
|
13
|
+
const path = require('path')
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Resolve HOME_DIR from .minion/.env or environment
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
function loadEnvFile(envPath) {
|
|
20
|
+
if (!fs.existsSync(envPath)) return {}
|
|
21
|
+
const env = {}
|
|
22
|
+
for (const line of fs.readFileSync(envPath, 'utf-8').split('\n')) {
|
|
23
|
+
const trimmed = line.trim()
|
|
24
|
+
if (!trimmed || trimmed.startsWith('#')) continue
|
|
25
|
+
const eqIdx = trimmed.indexOf('=')
|
|
26
|
+
if (eqIdx < 0) continue
|
|
27
|
+
const key = trimmed.slice(0, eqIdx).trim()
|
|
28
|
+
let value = trimmed.slice(eqIdx + 1).trim()
|
|
29
|
+
// Strip surrounding quotes
|
|
30
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
31
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
32
|
+
value = value.slice(1, -1)
|
|
33
|
+
}
|
|
34
|
+
env[key] = value
|
|
35
|
+
}
|
|
36
|
+
return env
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Determine HOME_DIR: prefer .minion/.env, fallback to env vars
|
|
40
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || ''
|
|
41
|
+
const dataDir = path.join(homeDir, '.minion')
|
|
42
|
+
const envFile = path.join(dataDir, '.env')
|
|
43
|
+
const envVars = loadEnvFile(envFile)
|
|
44
|
+
|
|
45
|
+
// Apply env vars that aren't already set
|
|
46
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
47
|
+
if (!(key in process.env)) process.env[key] = value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Ensure HOME/USERPROFILE are set
|
|
51
|
+
if (!process.env.HOME) process.env.HOME = homeDir
|
|
52
|
+
if (!process.env.USERPROFILE) process.env.USERPROFILE = homeDir
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Extend PATH (same logic as server.js)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const { buildExtendedPath } = require('../../core/lib/platform')
|
|
60
|
+
process.env.PATH = buildExtendedPath(homeDir)
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn('[WSL-Entry] Could not extend PATH:', err.message)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Load minion secrets into process.env
|
|
66
|
+
try {
|
|
67
|
+
const variableStore = require('../../core/stores/variable-store')
|
|
68
|
+
const minionEnv = variableStore.buildEnv()
|
|
69
|
+
for (const [key, value] of Object.entries(minionEnv)) {
|
|
70
|
+
if (!(key in process.env)) process.env[key] = value
|
|
71
|
+
}
|
|
72
|
+
console.log(`[WSL-Entry] Loaded ${Object.keys(minionEnv).length} minion secrets`)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.warn('[WSL-Entry] Could not load minion secrets:', err.message)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Start the server
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
console.log(`[WSL-Entry] HOME_DIR: ${homeDir}`)
|
|
82
|
+
console.log(`[WSL-Entry] PID: ${process.pid}`)
|
|
83
|
+
|
|
84
|
+
const { startServer } = require('../wsl-session-server')
|
|
85
|
+
|
|
86
|
+
startServer().catch((err) => {
|
|
87
|
+
console.error('[WSL-Entry] Failed to start WSL session server:', err)
|
|
88
|
+
process.exit(1)
|
|
89
|
+
})
|
package/win/minion-cli.ps1
CHANGED
|
@@ -800,6 +800,35 @@ function Invoke-Setup {
|
|
|
800
800
|
Write-Detail "TightVNC registered as logon task (user session, not service)"
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
+
# Step 9b: Register WSL session server (runs in user session for WSL access)
|
|
804
|
+
$wslServerJs = Join-Path $minionPkgDir 'win\bin\wsl-session-entry.js'
|
|
805
|
+
if (Test-Path $wslServerJs) {
|
|
806
|
+
# Generate shared auth token for minion-agent <-> wsl-session-server communication
|
|
807
|
+
$wslTokenFile = Join-Path $DataDir '.wsl-session-token'
|
|
808
|
+
if (-not (Test-Path $wslTokenFile)) {
|
|
809
|
+
$wslToken = [System.Guid]::NewGuid().ToString('N')
|
|
810
|
+
[System.IO.File]::WriteAllText($wslTokenFile, $wslToken)
|
|
811
|
+
Write-Detail "WSL session token generated"
|
|
812
|
+
} else {
|
|
813
|
+
Write-Detail "WSL session token already exists"
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
$wslAvailable = $false
|
|
817
|
+
if (Get-Command wsl.exe -ErrorAction SilentlyContinue) {
|
|
818
|
+
$wslAvailable = $true
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if ($wslAvailable) {
|
|
822
|
+
Remove-ScheduledTaskSilent "MinionWSL"
|
|
823
|
+
$wslTR = "'$nodePath' '$wslServerJs'"
|
|
824
|
+
schtasks /Create /TN "MinionWSL" /TR $wslTR /SC ONLOGON /RL HIGHEST /F | Out-Null
|
|
825
|
+
Write-Detail "WSL session server registered as logon task (user session)"
|
|
826
|
+
} else {
|
|
827
|
+
Write-Warn "WSL not detected. WSL session server not registered."
|
|
828
|
+
Write-Detail "Install WSL and re-run setup to enable WSL sessions."
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
803
832
|
# Step 10: Setup websockify (runs as LocalSystem, paired with VNC)
|
|
804
833
|
Write-Step 9 $totalSteps "Setting up websockify..."
|
|
805
834
|
[array]$wsCmd = Get-WebsockifyCommand
|
|
@@ -949,6 +978,7 @@ function Invoke-Setup {
|
|
|
949
978
|
$fwRules = @(
|
|
950
979
|
@{ Name = 'Minion Agent'; Port = 8080 },
|
|
951
980
|
@{ Name = 'Minion Terminal'; Port = 7681 },
|
|
981
|
+
@{ Name = 'Minion WSL Terminal'; Port = 7683 },
|
|
952
982
|
@{ Name = 'Minion VNC'; Port = 6080 }
|
|
953
983
|
)
|
|
954
984
|
foreach ($rule in $fwRules) {
|
|
@@ -1096,6 +1126,10 @@ function Invoke-Uninstall {
|
|
|
1096
1126
|
}
|
|
1097
1127
|
}
|
|
1098
1128
|
|
|
1129
|
+
# Remove WSL session server logon task
|
|
1130
|
+
Remove-ScheduledTaskSilent "MinionWSL"
|
|
1131
|
+
Write-Detail "WSL session server logon task removed"
|
|
1132
|
+
|
|
1099
1133
|
# Remove VNC logon task and legacy NSSM service
|
|
1100
1134
|
Remove-ScheduledTaskSilent "MinionVNC"
|
|
1101
1135
|
Invoke-Nssm stop minion-vnc
|