@geekbeer/minion 3.28.1 → 3.29.4

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.
@@ -1056,17 +1056,291 @@ POST `/api/minion/dag-workflows/:id/publish` (body なし):
1056
1056
  - OK なら新バージョン行を `dag_workflow_versions` に追加し、`current_version_id` を更新、ドラフトをクリア
1057
1057
  - NG なら 400 で `{ error, details: [...] }` を返却
1058
1058
 
1059
+ ### ノード/エッジ操作エンドポイント (PMロール限定)
1060
+
1061
+ **graph 全文を JSON で書く代わりに、ノード・エッジを1つずつ操作する**ためのAPI。各操作は自動でドラフトに保存され、レスポンスに `validation` フィールド(`validateDagGraph` によるフル検証結果)を含む。これにより、1操作ごとにバリデーションフィードバックを受け取れる。
1062
+
1063
+ | Method | Endpoint | Description |
1064
+ |--------|----------|-------------|
1065
+ | POST | `/api/minion/dag-workflows/:id/nodes` | ノードを追加(エッジ自動生成対応) |
1066
+ | PATCH | `/api/minion/dag-workflows/:id/nodes/:nodeId` | ノードのプロパティを更新 |
1067
+ | DELETE | `/api/minion/dag-workflows/:id/nodes/:nodeId` | ノードを削除(自動再接続対応) |
1068
+ | POST | `/api/minion/dag-workflows/:id/edges` | エッジを追加 |
1069
+ | PATCH | `/api/minion/dag-workflows/:id/edges/:edgeId` | エッジの kind / condition_label / contract を更新 |
1070
+ | DELETE | `/api/minion/dag-workflows/:id/edges/:edgeId` | エッジを削除 |
1071
+ | GET | `/api/minion/dag-workflows/:id/contracts` | 全contractsを取得 |
1072
+ | PUT | `/api/minion/dag-workflows/:id/contracts` | 全contractsを上書き |
1073
+ | POST | `/api/minion/dag-workflows/:id/contracts` | 個別contractを追加/更新 |
1074
+ | DELETE | `/api/minion/dag-workflows/:id/contracts` | 個別contractを削除 |
1075
+ | POST | `/api/minion/dag-workflows/:id/validate` | ドラフトをフル検証(公開せず) |
1076
+
1077
+ #### POST `/api/minion/dag-workflows/:id/nodes` — ノード追加
1078
+
1079
+ ```json
1080
+ {
1081
+ "type": "skill",
1082
+ "label": "Fetch Data",
1083
+ "after": "start",
1084
+ "before": "end",
1085
+ "skill_id": "<skill-uuid>",
1086
+ "skill_version_id": "<version-uuid>",
1087
+ "assigned_role": "engineer"
1088
+ }
1089
+ ```
1090
+
1091
+ - `type` (必須): `start` | `end` | `skill` | `review` | `fan_out` | `join` | `conditional` | `transform`
1092
+ - `id` (任意): 省略時は `<type>_1`, `<type>_2`, ... で自動採番
1093
+ - `label` (任意): 省略時は `New <type>`
1094
+ - `after` (任意): このノードの後ろに挿入。`after` → 新ノード へのエッジを自動生成
1095
+ - `before` (任意): このノードの前に挿入。新ノード → `before` へのエッジを自動生成
1096
+ - `after` と `before` を**両方指定**すると、既存の `after → before` エッジを削除して `after → 新ノード → before` に差し替え
1097
+
1098
+ **ノードタイプ別の必須フィールド(publish 時検証):**
1099
+
1100
+ | type | 必須フィールド | 備考 |
1101
+ |------|---------------|------|
1102
+ | `start` / `end` | なし | |
1103
+ | `skill` | `skill_id`, `skill_version_id`, `assigned_role` | |
1104
+ | `review` | `review: { reviewer_type, criteria, revision_target }` | `revision` エッジと `approved` エッジが**自動生成**される |
1105
+ | `fan_out` | `fan_out_source`, `template` | `template` は sub-graph `{ nodes, edges }` |
1106
+ | `join` | `join_mode`, `aggregation` | |
1107
+ | `conditional` | `condition_type`, `branches` or `default_branch` | |
1108
+ | `transform` | `transform_instruction`, `assigned_role` | |
1109
+
1110
+ **review ノード追加の例:**
1111
+ ```json
1112
+ {
1113
+ "type": "review",
1114
+ "label": "Quality Check",
1115
+ "after": "skill_1",
1116
+ "before": "end",
1117
+ "review": {
1118
+ "reviewer_type": "human",
1119
+ "criteria": "成果物の品質を確認",
1120
+ "revision_target": "skill_1"
1121
+ }
1122
+ }
1123
+ ```
1124
+ → `skill_1 → end` のエッジが削除され、以下が自動生成:
1125
+ - `skill_1 → review_1` (normal)
1126
+ - `review_1 → end` (approved)
1127
+ - `review_1 → skill_1` (revision)
1128
+
1129
+ #### PATCH `/api/minion/dag-workflows/:id/nodes/:nodeId` — ノード更新
1130
+
1131
+ ```json
1132
+ {
1133
+ "label": "Updated Label",
1134
+ "skill_version_id": "<new-version-uuid>",
1135
+ "position": { "x": 200, "y": 100 }
1136
+ }
1137
+ ```
1138
+
1139
+ - 指定したフィールドのみ上書き(未指定フィールドは変更しない)
1140
+ - `position` でノードのエディタ上の座標を更新可能
1141
+ - review ノードの `revision_target` を変更すると、revision エッジが自動で張り替えられる
1142
+ - fan_out ノードの `template` フィールドを上書きすることで、テンプレート sub-graph を更新可能(下記「fan_out テンプレートの編集」参照)
1143
+
1144
+ #### DELETE `/api/minion/dag-workflows/:id/nodes/:nodeId` — ノード削除
1145
+
1146
+ - start / end ノードは削除不可 (400)
1147
+ - 削除されたノードに接続されていたエッジも全て削除される
1148
+ - **自動再接続**: 削除ノードに incoming 1本 + outgoing 1本のエッジがあった場合、それらを直結する新エッジを自動生成
1149
+
1150
+ レスポンス `extra` フィールド:
1151
+ ```json
1152
+ {
1153
+ "removed_node": "skill_2",
1154
+ "removed_edges": ["edge_3", "edge_4"],
1155
+ "reconnected_edge": { "id": "edge_5", "source": "skill_1", "target": "end" }
1156
+ }
1157
+ ```
1158
+
1159
+ #### POST `/api/minion/dag-workflows/:id/edges` — エッジ追加
1160
+
1161
+ ```json
1162
+ {
1163
+ "source": "skill_1",
1164
+ "target": "skill_2",
1165
+ "kind": "normal",
1166
+ "condition_label": "success"
1167
+ }
1168
+ ```
1169
+
1170
+ - `source`, `target` (必須): 既存ノードのID
1171
+ - `kind` (任意): `normal` (default) | `approved` | `revision`
1172
+ - `condition_label` (任意): conditional ノードの分岐ラベル
1173
+ - `contract` (任意): `graph.contracts` 内の型名への参照
1174
+ - `id` (任意): 省略時は `edge_1`, `edge_2`, ... で自動採番
1175
+
1176
+ #### PATCH `/api/minion/dag-workflows/:id/edges/:edgeId` — エッジ更新
1177
+
1178
+ ```json
1179
+ {
1180
+ "kind": "approved",
1181
+ "condition_label": "success",
1182
+ "contract": "prototype"
1183
+ }
1184
+ ```
1185
+
1186
+ - `kind` (任意): `normal` | `approved` | `revision`
1187
+ - `condition_label` (任意): 分岐ラベル。`null` を渡すとクリア
1188
+ - `contract` (任意): contracts 内の型名。`null` を渡すとクリア
1189
+ - 指定したフィールドのみ上書き(未指定フィールドは変更しない)
1190
+
1191
+ #### DELETE `/api/minion/dag-workflows/:id/edges/:edgeId` — エッジ削除
1192
+
1193
+ - エッジIDを指定して削除
1194
+
1195
+ #### Contracts API
1196
+
1197
+ エッジを流れるデータの型を定義するための API。contracts は `graph.contracts` に保存される。
1198
+
1199
+ **Contract のスキーマ:**
1200
+
1201
+ ```typescript
1202
+ {
1203
+ description: string // contract の説明
1204
+ fields: [{
1205
+ key: string // フィールド名
1206
+ type: "string" | "number" | "boolean" | "url" | "array" | "object" // フィールド型
1207
+ description: string // フィールドの説明
1208
+ required?: boolean // 必須フラグ (省略時 false)
1209
+ }]
1210
+ }
1211
+ ```
1212
+
1213
+ **注意:**
1214
+ - エッジに設定する `contract` は `graph.contracts` に存在する名前でなければならない(存在しない名前を指定すると 400 エラー)
1215
+ - contract を削除すると、参照しているエッジの `contract` フィールドが自動でクリアされる(DELETE / PUT 共通)
1216
+ - `validate` エンドポイントはダングリング参照(存在しない contract への参照)をエラーとして報告する
1217
+
1218
+ ##### GET `/api/minion/dag-workflows/:id/contracts` — 全contracts取得
1219
+
1220
+ レスポンス: `{ "contracts": { "name": { "description": "...", "fields": [...] }, ... } }`
1221
+
1222
+ ##### PUT `/api/minion/dag-workflows/:id/contracts` — 全contracts上書き
1223
+
1224
+ ```json
1225
+ {
1226
+ "contracts": {
1227
+ "prototype": {
1228
+ "description": "プロトタイプ成果物",
1229
+ "fields": [
1230
+ { "key": "git_url", "type": "url", "description": "リポジトリURL", "required": true },
1231
+ { "key": "preview_url", "type": "url", "description": "プレビューURL" }
1232
+ ]
1233
+ }
1234
+ }
1235
+ }
1236
+ ```
1237
+
1238
+ ##### POST `/api/minion/dag-workflows/:id/contracts` — 個別contract追加/更新
1239
+
1240
+ ```json
1241
+ {
1242
+ "name": "prototype",
1243
+ "contract": {
1244
+ "description": "プロトタイプ成果物",
1245
+ "fields": [
1246
+ { "key": "git_url", "type": "url", "description": "リポジトリURL", "required": true }
1247
+ ]
1248
+ }
1249
+ }
1250
+ ```
1251
+
1252
+ ##### DELETE `/api/minion/dag-workflows/:id/contracts` — 個別contract削除
1253
+
1254
+ ```json
1255
+ { "name": "prototype" }
1256
+ ```
1257
+
1258
+ 参照しているエッジの `contract` フィールドも自動でクリアされる。レスポンスの `cleared_edges` にクリアされたエッジIDの一覧が含まれる。
1259
+
1260
+ #### POST `/api/minion/dag-workflows/:id/validate` — ドラフト検証
1261
+
1262
+ body なし。現在のドラフトを `validateDagGraph` でフル検証し結果を返す。**公開はしない**。
1263
+
1264
+ ```json
1265
+ {
1266
+ "valid": false,
1267
+ "errors": ["Skill node \"skill_1\" must have a skill selected"],
1268
+ "graph_errors": [],
1269
+ "node_errors": {
1270
+ "skill_1": ["Skill node \"skill_1\" must have a skill selected"]
1271
+ }
1272
+ }
1273
+ ```
1274
+
1275
+ #### 全操作共通のレスポンス形式
1276
+
1277
+ ノード/エッジ操作のレスポンスにはワークフロー全体 + `validation` フィールドが含まれる:
1278
+
1279
+ ```json
1280
+ {
1281
+ "id": "...",
1282
+ "draft_graph": { "nodes": [...], "edges": [...] },
1283
+ "...",
1284
+ "validation": {
1285
+ "valid": false,
1286
+ "errors": ["..."],
1287
+ "node_errors": { "node_id": ["..."] }
1288
+ },
1289
+ "added_node": { "id": "skill_1", "type": "skill", "..." }
1290
+ }
1291
+ ```
1292
+
1293
+ `validation.valid` が `true` になれば `hq publish dag-workflow <id>` で公開可能。
1294
+
1295
+ #### fan_out テンプレートの編集
1296
+
1297
+ fan_out ノードの `template`(sub-graph)にノードやエッジを追加するには、PATCH でテンプレート全体を上書きする:
1298
+
1299
+ ```bash
1300
+ # 1. 現在のドラフトを取得して fan_out ノードの template を確認
1301
+ hq fetch dag-workflow <wf-id> | jq '.draft_graph.nodes[] | select(.id == "fan_out_1") | .template'
1302
+
1303
+ # 2. template を編集して PATCH
1304
+ cat > /tmp/u.json <<'EOF'
1305
+ {
1306
+ "template": {
1307
+ "nodes": [
1308
+ { "id": "t_skill", "type": "skill", "label": "Process Item",
1309
+ "skill_id": "<uuid>", "skill_version_id": "<uuid>", "assigned_role": "engineer" }
1310
+ ],
1311
+ "edges": []
1312
+ }
1313
+ }
1314
+ EOF
1315
+ hq dag update-node <wf-id> fan_out_1 /tmp/u.json
1316
+ ```
1317
+
1318
+ テンプレート内部のノード/エッジを個別操作するAPIは現在ない。テンプレートは `template` フィールドの全体上書きで更新すること。テンプレートは start/end ノードを持たない sub-graph であり、バリデーション時に再帰的に検証される。
1319
+
1320
+ > **⚠️ 重要: テンプレート内に `start` / `end` ノードを配置してはいけない。**
1321
+ > テンプレートのエントリポイントとエグジットポイントは、エッジ構造から自動検出される(incoming edge がないノード = エントリ、outgoing edge がないノード = エグジット)。`start` / `end` はトップレベル DAG 専用のノードタイプであり、テンプレート内に含めるとバリデーションエラーになる。テンプレート内では `skill`, `conditional`, `transform`, `review` 等の実行ノードのみ使用すること。
1322
+
1059
1323
  ### ミニオン CLI ラッパー
1060
1324
 
1061
1325
  以下の `hq` サブコマンドが上記エンドポイントを呼び出す。いずれもリクエスト送信前にローカルで JSON 構文検証を行う。
1062
1326
 
1063
1327
  ```bash
1328
+ # 全文操作 (従来)
1064
1329
  hq list dag-workflows [project_id] # GET /api/minion/dag-workflows(?project_id=...)
1065
1330
  hq create dag-workflow <body.json> # POST /api/minion/dag-workflows
1066
1331
  hq put dag-workflow <id> <body.json> # PUT /api/minion/dag-workflows/:id
1067
1332
  hq publish dag-workflow <id> # POST /api/minion/dag-workflows/:id/publish
1068
1333
  hq fetch dag-workflow <id> # GET /api/minion/dag-workflows/:id
1069
1334
  hq fetch dag-execution <id> # GET /api/minion/dag-executions/:id
1335
+
1336
+ # ノード/エッジ操作 (推奨)
1337
+ hq dag add-node <wf-id> <body.json> # POST .../nodes
1338
+ hq dag update-node <wf-id> <node-id> <body.json> # PATCH .../nodes/:nodeId
1339
+ hq dag remove-node <wf-id> <node-id> # DELETE .../nodes/:nodeId
1340
+ hq dag add-edge <wf-id> <body.json> # POST .../edges
1341
+ hq dag update-edge <wf-id> <edge-id> <body.json> # PATCH .../edges/:edgeId
1342
+ hq dag remove-edge <wf-id> <edge-id> # DELETE .../edges/:edgeId
1343
+ hq dag validate <wf-id> # POST .../validate
1070
1344
  ```
1071
1345
 
1072
1346
  ### DAG Read Endpoints (チャットコンテキスト用)
@@ -258,11 +258,123 @@ hq put dag-workflow <dag-workflow-id> /tmp/dag-update.json
258
258
  hq publish dag-workflow <dag-workflow-id>
259
259
  ```
260
260
 
261
+ #### 5. ノード/エッジ操作 API(推奨)
262
+
263
+ graph 全文を JSON で書く代わりに、ノード・エッジを1つずつ操作するAPIが利用可能。**各操作のレスポンスにバリデーション結果が含まれる**ため、1ステップごとにエラーを確認・修正できる。
264
+
265
+ ```bash
266
+ # 1. 空の DAG を作成
267
+ cat > /tmp/dag-create.json <<'EOF'
268
+ {
269
+ "project_id": "<project-uuid>",
270
+ "name": "my-dag"
271
+ }
272
+ EOF
273
+ hq create dag-workflow /tmp/dag-create.json
274
+ # → id を控える
275
+
276
+ # 2. start/end ノードを追加
277
+ echo '{ "type": "start", "label": "Start" }' > /tmp/n.json
278
+ hq dag add-node <wf-id> /tmp/n.json
279
+
280
+ echo '{ "type": "end", "label": "End" }' > /tmp/n.json
281
+ hq dag add-node <wf-id> /tmp/n.json
282
+
283
+ # 3. start → end をつなぐエッジ
284
+ echo '{ "source": "start_1", "target": "end_1" }' > /tmp/e.json
285
+ hq dag add-edge <wf-id> /tmp/e.json
286
+
287
+ # 4. skill ノードを start と end の間に挿入
288
+ # after + before を指定すると、既存エッジを自動で差し替える
289
+ cat > /tmp/n.json <<'EOF'
290
+ {
291
+ "type": "skill",
292
+ "label": "Fetch Data",
293
+ "after": "start_1",
294
+ "before": "end_1",
295
+ "skill_id": "<skill-uuid>",
296
+ "skill_version_id": "<version-uuid>",
297
+ "assigned_role": "engineer"
298
+ }
299
+ EOF
300
+ hq dag add-node <wf-id> /tmp/n.json
301
+ # → start_1 → end_1 エッジが削除され、start_1 → skill_1 → end_1 に差し替え
302
+
303
+ # 5. review ノードを追加(approved + revision エッジが自動生成)
304
+ cat > /tmp/n.json <<'EOF'
305
+ {
306
+ "type": "review",
307
+ "label": "Quality Check",
308
+ "after": "skill_1",
309
+ "before": "end_1",
310
+ "review": {
311
+ "reviewer_type": "human",
312
+ "criteria": "成果物の品質を確認",
313
+ "revision_target": "skill_1"
314
+ }
315
+ }
316
+ EOF
317
+ hq dag add-node <wf-id> /tmp/n.json
318
+ # → skill_1 → review_1 (normal), review_1 → end_1 (approved), review_1 → skill_1 (revision)
319
+
320
+ # 6. バリデーション確認(公開せずにチェック)
321
+ hq dag validate <wf-id>
322
+ # → { "valid": true, "errors": [], ... } なら公開可能
323
+
324
+ # 7. 公開
325
+ hq publish dag-workflow <wf-id>
326
+ ```
327
+
328
+ **ノード更新・削除:**
329
+
330
+ ```bash
331
+ # ノードのプロパティを更新
332
+ echo '{ "label": "Updated Name", "skill_version_id": "<new-uuid>" }' > /tmp/u.json
333
+ hq dag update-node <wf-id> skill_1 /tmp/u.json
334
+
335
+ # ノード削除(自動再接続あり)
336
+ hq dag remove-node <wf-id> skill_2
337
+
338
+ # エッジの kind や condition_label を更新
339
+ echo '{ "kind": "approved" }' > /tmp/eu.json
340
+ hq dag update-edge <wf-id> edge_3 /tmp/eu.json
341
+
342
+ # エッジ削除
343
+ hq dag remove-edge <wf-id> edge_3
344
+ ```
345
+
346
+ **fan_out テンプレート編集:**
347
+
348
+ > **⚠️ テンプレート内に `start` / `end` ノードを入れないこと。** テンプレートのエントリ/エグジットはエッジ構造から自動検出される。`start` / `end` はトップレベル DAG 専用であり、テンプレート内に含めるとバリデーションエラーになる。`skill`, `conditional`, `transform`, `review` 等の実行ノードのみ使用すること。
349
+
350
+ ```bash
351
+ # fan_out ノードの template を PATCH で上書き
352
+ # ※ start/end ノードは不要。実行ノードのみ配置する
353
+ cat > /tmp/t.json <<'EOF'
354
+ {
355
+ "template": {
356
+ "nodes": [
357
+ { "id": "t_skill", "type": "skill", "label": "Process Item",
358
+ "skill_id": "<uuid>", "skill_version_id": "<uuid>", "assigned_role": "engineer" }
359
+ ],
360
+ "edges": []
361
+ }
362
+ }
363
+ EOF
364
+ hq dag update-node <wf-id> fan_out_1 /tmp/t.json
365
+ ```
366
+
367
+ **Tips:**
368
+ - `hq dag add-node` で review ノードを追加すると、`approved` エッジと `revision` エッジが自動生成される。手動でエッジを張る必要なし
369
+ - 各操作のレスポンスの `validation.valid` が `true` かどうかで、いつでも公開可能かを判断できる
370
+ - 複雑なグラフでも1ノードずつ積み上げれば、途中でバリデーションエラーの原因を特定しやすい
371
+
261
372
  #### 権限と注意事項
262
373
 
263
- - 書き込み(create / put / publish)は **PMロールのみ**。Engineer / accountant では 403 が返る。読み取り(fetch)は全メンバー可。
264
- - ドラフト保存は軽量(構造チェックのみ)。インクリメンタルに graph を組み立てていくのに適している。
265
- - セマンティックエラーは publish 時にまとめて検出されるため、途中保存と最終公開を使い分けること。
374
+ - 書き込み(create / put / publish / dag 操作)は **PMロールのみ**。Engineer / accountant では 403 が返る。読み取り(fetch)は全メンバー可。
375
+ - ノード/エッジ操作は各ステップでドラフトに自動保存され、バリデーション結果がレスポンスに含まれる。
376
+ - `hq dag validate` で公開前にフル検証できる。publish 時の想定外エラーを防止できる。
377
+ - 全文 JSON 操作(`hq put dag-workflow`)も引き続き利用可能だが、**ノード/エッジ操作APIの方が推奨**。
266
378
 
267
379
  ### 実行フロー(ランタイム側、参考)
268
380
 
package/linux/bin/hq CHANGED
@@ -28,6 +28,13 @@
28
28
  # hq create dag-workflow <body.json> - Create a DAG workflow (PM only). Body: {project_id, name, graph?, ...}
29
29
  # hq put dag-workflow <id> <body.json> - Update DAG workflow draft (PM only). Body: {graph?, content?, ...}
30
30
  # hq publish dag-workflow <id> - Publish the draft as a new version (PM only, validated)
31
+ # hq dag add-node <wf-id> <body.json> - Add a node to DAG draft. Body: {type, label?, after?, before?, ...}
32
+ # hq dag update-node <wf-id> <node-id> <body.json> - Update a node. Body: {label?, skill_version_id?, ...}
33
+ # hq dag remove-node <wf-id> <node-id> - Remove a node (auto-reconnects if possible)
34
+ # hq dag add-edge <wf-id> <body.json> - Add an edge. Body: {source, target, kind?, condition_label?}
35
+ # hq dag update-edge <wf-id> <edge-id> <body.json> - Update edge kind/condition_label
36
+ # hq dag remove-edge <wf-id> <edge-id> - Remove an edge
37
+ # hq dag validate <wf-id> - Validate draft without publishing
31
38
 
32
39
  set -euo pipefail
33
40
 
@@ -96,7 +103,7 @@ fetch_resource() {
96
103
  fi
97
104
  }
98
105
 
99
- # Send a JSON request body from a file. Method is POST or PUT.
106
+ # Send a JSON request body from a file. Method is POST, PUT, or PATCH.
100
107
  send_json_request() {
101
108
  local method="$1"
102
109
  local url="$2"
@@ -163,6 +170,15 @@ print_usage() {
163
170
  echo " hq create dag-workflow <body.json> - Create a DAG workflow (PM only)" >&2
164
171
  echo " hq put dag-workflow <id> <body.json> - Update DAG workflow draft (PM only)" >&2
165
172
  echo " hq publish dag-workflow <id> - Publish the draft as a new version (PM only)" >&2
173
+ echo "" >&2
174
+ echo " DAG node/edge operations (PM only, each returns validation warnings):" >&2
175
+ echo " hq dag add-node <wf-id> <body.json> - Add a node to DAG draft" >&2
176
+ echo " hq dag update-node <wf-id> <node-id> <body.json> - Update a node's properties" >&2
177
+ echo " hq dag remove-node <wf-id> <node-id> - Remove a node" >&2
178
+ echo " hq dag add-edge <wf-id> <body.json> - Add an edge" >&2
179
+ echo " hq dag update-edge <wf-id> <edge-id> <body.json> - Update edge kind/label" >&2
180
+ echo " hq dag remove-edge <wf-id> <edge-id> - Remove an edge" >&2
181
+ echo " hq dag validate <wf-id> - Validate draft (no publish)" >&2
166
182
  }
167
183
 
168
184
  # Main command dispatch
@@ -454,6 +470,112 @@ print(json.dumps(d))
454
470
  esac
455
471
  ;;
456
472
 
473
+ dag)
474
+ action="${2:-}"
475
+ case "$action" in
476
+ add-node)
477
+ wf_id="${3:-}"
478
+ body_file="${4:-}"
479
+ if [ -z "$wf_id" ] || [ -z "$body_file" ]; then
480
+ echo "Usage: hq dag add-node <wf-id> <body.json>" >&2
481
+ echo " body.json: { type, label?, after?, before?, skill_id?, skill_version_id?, assigned_role?, review?, ... }" >&2
482
+ exit 1
483
+ fi
484
+ validate_json_file "$body_file"
485
+ send_json_request POST "$BASE_URL/dag-workflows/$wf_id/nodes" "$body_file"
486
+ ;;
487
+ update-node)
488
+ wf_id="${3:-}"
489
+ node_id="${4:-}"
490
+ body_file="${5:-}"
491
+ if [ -z "$wf_id" ] || [ -z "$node_id" ] || [ -z "$body_file" ]; then
492
+ echo "Usage: hq dag update-node <wf-id> <node-id> <body.json>" >&2
493
+ echo " body.json: { label?, skill_version_id?, assigned_role?, review?, ... }" >&2
494
+ exit 1
495
+ fi
496
+ validate_json_file "$body_file"
497
+ send_json_request PATCH "$BASE_URL/dag-workflows/$wf_id/nodes/$node_id" "$body_file"
498
+ ;;
499
+ remove-node)
500
+ wf_id="${3:-}"
501
+ node_id="${4:-}"
502
+ if [ -z "$wf_id" ] || [ -z "$node_id" ]; then
503
+ echo "Usage: hq dag remove-node <wf-id> <node-id>" >&2
504
+ exit 1
505
+ fi
506
+ # DELETE with no body
507
+ response=$(curl -s -w "\n%{http_code}" -X DELETE \
508
+ -H "Authorization: Bearer $API_TOKEN" \
509
+ "$BASE_URL/dag-workflows/$wf_id/nodes/$node_id")
510
+ http_code=$(echo "$response" | tail -1)
511
+ body=$(echo "$response" | sed '$d')
512
+ if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
513
+ echo "$body" | format_json
514
+ else
515
+ echo "Error: HQ API returned HTTP $http_code" >&2
516
+ echo "$body" >&2
517
+ exit 1
518
+ fi
519
+ ;;
520
+ add-edge)
521
+ wf_id="${3:-}"
522
+ body_file="${4:-}"
523
+ if [ -z "$wf_id" ] || [ -z "$body_file" ]; then
524
+ echo "Usage: hq dag add-edge <wf-id> <body.json>" >&2
525
+ echo " body.json: { source, target, kind?, condition_label? }" >&2
526
+ exit 1
527
+ fi
528
+ validate_json_file "$body_file"
529
+ send_json_request POST "$BASE_URL/dag-workflows/$wf_id/edges" "$body_file"
530
+ ;;
531
+ update-edge)
532
+ wf_id="${3:-}"
533
+ edge_id="${4:-}"
534
+ body_file="${5:-}"
535
+ if [ -z "$wf_id" ] || [ -z "$edge_id" ] || [ -z "$body_file" ]; then
536
+ echo "Usage: hq dag update-edge <wf-id> <edge-id> <body.json>" >&2
537
+ echo " body.json: { kind?, condition_label? }" >&2
538
+ exit 1
539
+ fi
540
+ validate_json_file "$body_file"
541
+ send_json_request PATCH "$BASE_URL/dag-workflows/$wf_id/edges/$edge_id" "$body_file"
542
+ ;;
543
+ remove-edge)
544
+ wf_id="${3:-}"
545
+ edge_id="${4:-}"
546
+ if [ -z "$wf_id" ] || [ -z "$edge_id" ]; then
547
+ echo "Usage: hq dag remove-edge <wf-id> <edge-id>" >&2
548
+ exit 1
549
+ fi
550
+ response=$(curl -s -w "\n%{http_code}" -X DELETE \
551
+ -H "Authorization: Bearer $API_TOKEN" \
552
+ "$BASE_URL/dag-workflows/$wf_id/edges/$edge_id")
553
+ http_code=$(echo "$response" | tail -1)
554
+ body=$(echo "$response" | sed '$d')
555
+ if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
556
+ echo "$body" | format_json
557
+ else
558
+ echo "Error: HQ API returned HTTP $http_code" >&2
559
+ echo "$body" >&2
560
+ exit 1
561
+ fi
562
+ ;;
563
+ validate)
564
+ wf_id="${3:-}"
565
+ if [ -z "$wf_id" ]; then
566
+ echo "Usage: hq dag validate <wf-id>" >&2
567
+ exit 1
568
+ fi
569
+ send_empty_post "$BASE_URL/dag-workflows/$wf_id/validate"
570
+ ;;
571
+ *)
572
+ echo "Unknown dag action: $action" >&2
573
+ echo "Usage: hq dag {add-node|update-node|remove-node|add-edge|remove-edge|validate} ..." >&2
574
+ exit 1
575
+ ;;
576
+ esac
577
+ ;;
578
+
457
579
  *)
458
580
  print_usage
459
581
  exit 1
@@ -403,9 +403,15 @@ async function buildContextPrefix(message, context, sessionId, workspaceId) {
403
403
  ` hq fetch dag-workflow ${context.dagWorkflowId}`,
404
404
  `プロジェクトコンテキスト:`,
405
405
  ` hq fetch project-context ${context.projectId}`,
406
- `PMロールの場合、graph JSON を直接編集できます:`,
407
- ` hq put dag-workflow ${context.dagWorkflowId} <body.json> # ドラフト更新 (構造チェックのみ)`,
408
- ` hq publish dag-workflow ${context.dagWorkflowId} # ドラフトを新バージョンとして公開 (フル検証)`,
406
+ `PMロールの場合、ノード/エッジ操作APIでインクリメンタルに編集できます(推奨):`,
407
+ ` hq dag add-node ${context.dagWorkflowId} <body.json> # ノード追加`,
408
+ ` hq dag update-node ${context.dagWorkflowId} <node-id> <body.json> # ノード更新`,
409
+ ` hq dag remove-node ${context.dagWorkflowId} <node-id> # ノード削除`,
410
+ ` hq dag add-edge ${context.dagWorkflowId} <body.json> # エッジ追加`,
411
+ ` hq dag remove-edge ${context.dagWorkflowId} <edge-id> # エッジ削除`,
412
+ ` hq dag validate ${context.dagWorkflowId} # ドラフト検証(公開せず)`,
413
+ ` hq publish dag-workflow ${context.dagWorkflowId} # 公開`,
414
+ `graph JSON 全文操作も可能: hq put dag-workflow ${context.dagWorkflowId} <body.json>`,
409
415
  `新規作成は: hq create dag-workflow <body.json>`,
410
416
  `プロジェクト内の DAG ワークフロー一覧: hq list dag-workflows ${context.projectId}`,
411
417
  `DAG の構造(nodes/edges/node types/scope_path 等)や実行フローの詳細は ~/.minion/docs/api-reference.md の「DAG Workflows」セクション、および ~/.minion/docs/task-guides.md の「DAG ワークフロー」セクションを参照してください。`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekbeer/minion",
3
- "version": "3.28.1",
3
+ "version": "3.29.4",
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/win/bin/hq.ps1 CHANGED
@@ -22,6 +22,13 @@
22
22
  .\hq.ps1 create dag-workflow <body.json>
23
23
  .\hq.ps1 put dag-workflow <id> <body.json>
24
24
  .\hq.ps1 publish dag-workflow <id>
25
+ .\hq.ps1 dag add-node <wf-id> <body.json>
26
+ .\hq.ps1 dag update-node <wf-id> <node-id> <body.json>
27
+ .\hq.ps1 dag remove-node <wf-id> <node-id>
28
+ .\hq.ps1 dag add-edge <wf-id> <body.json>
29
+ .\hq.ps1 dag update-edge <wf-id> <edge-id> <body.json>
30
+ .\hq.ps1 dag remove-edge <wf-id> <edge-id>
31
+ .\hq.ps1 dag validate <wf-id>
25
32
  #>
26
33
 
27
34
  param(
@@ -35,7 +42,10 @@ param(
35
42
  [string]$Arg2,
36
43
 
37
44
  [Parameter(Position = 3)]
38
- [string]$Arg3
45
+ [string]$Arg3,
46
+
47
+ [Parameter(Position = 4)]
48
+ [string]$Arg4
39
49
  )
40
50
 
41
51
  $ErrorActionPreference = 'Stop'
@@ -133,6 +143,15 @@ function Show-Usage {
133
143
  Write-Host " hq create dag-workflow <body.json> - Create a DAG workflow (PM only)"
134
144
  Write-Host " hq put dag-workflow <id> <body.json> - Update DAG workflow draft (PM only)"
135
145
  Write-Host " hq publish dag-workflow <id> - Publish the draft as a new version (PM only)"
146
+ Write-Host ""
147
+ Write-Host " DAG node/edge operations (PM only, each returns validation warnings):"
148
+ Write-Host " hq dag add-node <wf-id> <body.json> - Add a node to DAG draft"
149
+ Write-Host " hq dag update-node <wf-id> <node-id> <body.json> - Update a node's properties"
150
+ Write-Host " hq dag remove-node <wf-id> <node-id> - Remove a node"
151
+ Write-Host " hq dag add-edge <wf-id> <body.json> - Add an edge"
152
+ Write-Host " hq dag update-edge <wf-id> <edge-id> <body.json> - Update edge kind/label"
153
+ Write-Host " hq dag remove-edge <wf-id> <edge-id> - Remove an edge"
154
+ Write-Host " hq dag validate <wf-id> - Validate draft (no publish)"
136
155
  }
137
156
 
138
157
  switch ($Command) {
@@ -251,6 +270,105 @@ switch ($Command) {
251
270
  }
252
271
  }
253
272
 
273
+ 'dag' {
274
+ $Action = $Arg1
275
+ switch ($Action) {
276
+ 'add-node' {
277
+ $WfId = $Arg2
278
+ $BodyFile = $Arg3
279
+ if (-not $WfId -or -not $BodyFile) {
280
+ Write-Error "Usage: hq dag add-node <wf-id> <body.json>"
281
+ exit 1
282
+ }
283
+ Assert-ValidJsonFile -Path $BodyFile
284
+ Send-JsonRequest -Method 'Post' -Url "$BaseUrl/dag-workflows/$WfId/nodes" -Path $BodyFile
285
+ }
286
+ 'update-node' {
287
+ $WfId = $Arg2
288
+ $NodeId = $Arg3
289
+ $BodyFile = $Arg4
290
+ if (-not $WfId -or -not $NodeId -or -not $BodyFile) {
291
+ Write-Error "Usage: hq dag update-node <wf-id> <node-id> <body.json>"
292
+ exit 1
293
+ }
294
+ Assert-ValidJsonFile -Path $BodyFile
295
+ Send-JsonRequest -Method 'Patch' -Url "$BaseUrl/dag-workflows/$WfId/nodes/$NodeId" -Path $BodyFile
296
+ }
297
+ 'remove-node' {
298
+ $WfId = $Arg2
299
+ $NodeId = $Arg3
300
+ if (-not $WfId -or -not $NodeId) {
301
+ Write-Error "Usage: hq dag remove-node <wf-id> <node-id>"
302
+ exit 1
303
+ }
304
+ try {
305
+ $response = Invoke-RestMethod -Uri "$BaseUrl/dag-workflows/$WfId/nodes/$NodeId" `
306
+ -Headers $Headers -Method Delete -ErrorAction Stop
307
+ $response | ConvertTo-Json -Depth 20
308
+ }
309
+ catch {
310
+ $statusCode = $_.Exception.Response.StatusCode.value__
311
+ Write-Error "Error: HQ API returned HTTP $statusCode"
312
+ Write-Error $_.ErrorDetails.Message
313
+ exit 1
314
+ }
315
+ }
316
+ 'add-edge' {
317
+ $WfId = $Arg2
318
+ $BodyFile = $Arg3
319
+ if (-not $WfId -or -not $BodyFile) {
320
+ Write-Error "Usage: hq dag add-edge <wf-id> <body.json>"
321
+ exit 1
322
+ }
323
+ Assert-ValidJsonFile -Path $BodyFile
324
+ Send-JsonRequest -Method 'Post' -Url "$BaseUrl/dag-workflows/$WfId/edges" -Path $BodyFile
325
+ }
326
+ 'update-edge' {
327
+ $WfId = $Arg2
328
+ $EdgeId = $Arg3
329
+ $BodyFile = $Arg4
330
+ if (-not $WfId -or -not $EdgeId -or -not $BodyFile) {
331
+ Write-Error "Usage: hq dag update-edge <wf-id> <edge-id> <body.json>"
332
+ exit 1
333
+ }
334
+ Assert-ValidJsonFile -Path $BodyFile
335
+ Send-JsonRequest -Method 'Patch' -Url "$BaseUrl/dag-workflows/$WfId/edges/$EdgeId" -Path $BodyFile
336
+ }
337
+ 'remove-edge' {
338
+ $WfId = $Arg2
339
+ $EdgeId = $Arg3
340
+ if (-not $WfId -or -not $EdgeId) {
341
+ Write-Error "Usage: hq dag remove-edge <wf-id> <edge-id>"
342
+ exit 1
343
+ }
344
+ try {
345
+ $response = Invoke-RestMethod -Uri "$BaseUrl/dag-workflows/$WfId/edges/$EdgeId" `
346
+ -Headers $Headers -Method Delete -ErrorAction Stop
347
+ $response | ConvertTo-Json -Depth 20
348
+ }
349
+ catch {
350
+ $statusCode = $_.Exception.Response.StatusCode.value__
351
+ Write-Error "Error: HQ API returned HTTP $statusCode"
352
+ Write-Error $_.ErrorDetails.Message
353
+ exit 1
354
+ }
355
+ }
356
+ 'validate' {
357
+ $WfId = $Arg2
358
+ if (-not $WfId) {
359
+ Write-Error "Usage: hq dag validate <wf-id>"
360
+ exit 1
361
+ }
362
+ Send-EmptyPost -Url "$BaseUrl/dag-workflows/$WfId/validate"
363
+ }
364
+ default {
365
+ Write-Error "Unknown dag action: $Action"
366
+ Write-Error "Usage: hq dag {add-node|update-node|remove-node|add-edge|remove-edge|validate} ..."
367
+ exit 1
368
+ }
369
+ }
370
+ }
371
+
254
372
  default {
255
373
  Show-Usage
256
374
  exit 1
@@ -464,9 +464,15 @@ async function buildContextPrefix(message, context, sessionId, workspaceId) {
464
464
  ` hq fetch dag-workflow ${context.dagWorkflowId}`,
465
465
  `プロジェクトコンテキスト:`,
466
466
  ` hq fetch project-context ${context.projectId}`,
467
- `PMロールの場合、graph JSON を直接編集できます:`,
468
- ` hq put dag-workflow ${context.dagWorkflowId} <body.json> # ドラフト更新 (構造チェックのみ)`,
469
- ` hq publish dag-workflow ${context.dagWorkflowId} # ドラフトを新バージョンとして公開 (フル検証)`,
467
+ `PMロールの場合、ノード/エッジ操作APIでインクリメンタルに編集できます(推奨):`,
468
+ ` hq dag add-node ${context.dagWorkflowId} <body.json> # ノード追加`,
469
+ ` hq dag update-node ${context.dagWorkflowId} <node-id> <body.json> # ノード更新`,
470
+ ` hq dag remove-node ${context.dagWorkflowId} <node-id> # ノード削除`,
471
+ ` hq dag add-edge ${context.dagWorkflowId} <body.json> # エッジ追加`,
472
+ ` hq dag remove-edge ${context.dagWorkflowId} <edge-id> # エッジ削除`,
473
+ ` hq dag validate ${context.dagWorkflowId} # ドラフト検証(公開せず)`,
474
+ ` hq publish dag-workflow ${context.dagWorkflowId} # 公開`,
475
+ `graph JSON 全文操作も可能: hq put dag-workflow ${context.dagWorkflowId} <body.json>`,
470
476
  `新規作成は: hq create dag-workflow <body.json>`,
471
477
  `プロジェクト内の DAG ワークフロー一覧: hq list dag-workflows ${context.projectId}`,
472
478
  `DAG の構造(nodes/edges/node types/scope_path 等)や実行フローの詳細は ~/.minion/docs/api-reference.md の「DAG Workflows」セクション、および ~/.minion/docs/task-guides.md の「DAG ワークフロー」セクションを参照してください。`,