claude-task-worker 0.100.0 → 0.102.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/README.md +134 -242
- package/dist/index.js +99 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
GitHub Issues/PRを定期ポーリングし、Claude Codeに処理を委譲するCLIツール。
|
|
4
4
|
|
|
5
|
-
同梱の
|
|
5
|
+
同梱の Claude Code プラグイン(`plugin/`)と組み合わせることで、Issue の実装からPRのレビュー対応、Dependabot PR の対応までを自動化する。CLI 本体(npm パッケージ)とプラグイン(Claude Code マーケットプレイス)は同じリポジトリ・同じ名前で提供される。
|
|
6
6
|
|
|
7
7
|
## アーキテクチャ
|
|
8
8
|
|
|
@@ -42,23 +42,10 @@ CLI が GitHub ラベルを検知してタスクを起動し、プラグイン
|
|
|
42
42
|
共通の挙動:
|
|
43
43
|
|
|
44
44
|
- 処理中は `cc-in-progress` を付与し、同一 Issue/PR の重複実行を防ぐ
|
|
45
|
-
- `cc-need-human-check` が付いた Issue
|
|
46
|
-
- Issue 系ワーカーは `-is:blocked` 検索 qualifier で絞り込むため、未解決の blockedBy を持つ Issue は対象外
|
|
45
|
+
- `cc-need-human-check` が付いた Issue、未解決の blockedBy を持つ Issue は対象外
|
|
47
46
|
- 完了時にトリガーラベルを除去し、次のワーカーへ引き継ぐラベルを付与する
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
- `update-design-md` は `uiDesign.enabled` が `true` のときだけ起動する
|
|
51
|
-
|
|
52
|
-
### プラグインの構成
|
|
53
|
-
|
|
54
|
-
| ディレクトリ | 内容 |
|
|
55
|
-
|---|---|
|
|
56
|
-
| `plugin/skills/` | ワーカーが呼ぶスキル群と、対話セッション用の補助スキル(`commit-push` / `create-pr` / `breakdown-issues` / `edit-pencil-design` など) |
|
|
57
|
-
| `plugin/agents/` | サブエージェント定義(`explore-agent` / `frontend-implementer` / `general-purpose-assistant` / `lightweight-assistant` / `pencil-design-updater` / `requirement-todo-organizer`) |
|
|
58
|
-
| `plugin/hooks/` | `SessionStart`(worktree セットアップ / CodeGraph インデックス構築(クラウドのみ) / `git fetch --prune`)と `UserPromptSubmit`(`codegraph prompt-hook`)のフック定義 |
|
|
59
|
-
| `plugin/scripts/` | フック・スキルから呼ばれるスクリプト(`setup-worktree.sh` / `setup-codegraph.sh` / `stop-servers.mjs` / `resolve-pr-comments.sh` — `resolve-pr-comments` スキルが GitHub MCP を使えない場合の `gh` フォールバック) |
|
|
60
|
-
| `plugin/references/` | 複数スキルが共有する参照ドキュメント(GitHub アクセス方針など) |
|
|
61
|
-
| `plugin/.mcp.json` | MCP サーバー定義(`codegraph` / `context7` / `next-devtools` / `shadcn` / `playwright`) |
|
|
47
|
+
- 定期ワーカー3つ(`update-*`)はラベルではなく時刻を条件に24時間おきに1回動く。表の「間隔」は24時間経過したかを確認する頻度
|
|
48
|
+
- UIデザイン系3ワーカー(`create-ui-design` / `apply-ui-design` / `update-design-md`)は `uiDesign.enabled` が `true` のときだけ起動する(既定 `false`)
|
|
62
49
|
|
|
63
50
|
## セットアップ
|
|
64
51
|
|
|
@@ -69,16 +56,15 @@ CLI が GitHub ラベルを検知してタスクを起動し、プラグイン
|
|
|
69
56
|
| [Node.js](https://nodejs.org/) >= 22.6.0 | CLI の実行ランタイム |
|
|
70
57
|
| [GitHub CLI (`gh`)](https://cli.github.com/) | 全 GitHub 操作(認証済みであること) |
|
|
71
58
|
| [Claude Code (`claude`)](https://docs.anthropic.com/en/docs/claude-code) | タスク実行エンジン |
|
|
72
|
-
| [Git](https://git-scm.com/) | worktree
|
|
73
|
-
| [
|
|
74
|
-
| [
|
|
75
|
-
| [
|
|
76
|
-
| [
|
|
77
|
-
| [
|
|
78
|
-
| [
|
|
79
|
-
| [GitHub MCP](https://github.com/github/github-mcp-server) | GitHub アクセスの高速化・クラウド実行時のプロキシ制限回避。Claude 側のコネクタで有効化する(任意。未設定でも `gh` へフォールバックする) |
|
|
59
|
+
| [Git](https://git-scm.com/) / [jq](https://jqlang.org/) | worktree 操作 / スキル内での JSON 加工 |
|
|
60
|
+
| [CodeGraph](https://www.npmjs.com/package/@colbymchenry/codegraph) | コード探索用インデックス(任意。未導入ならテキスト検索に落ちる) |
|
|
61
|
+
| [Pen CLI](https://docs.pen.dev/for-developers/pen-cli) | `.pen` の編集・参照。UIデザイン先行ワークフロー使用時のみ(要ログイン) |
|
|
62
|
+
| [Playwright](https://playwright.dev/) のブラウザ | Playwright MCP でのブラウザ確認 |
|
|
63
|
+
| [DESIGN.md CLI](https://github.com/google-labs-code/design.md) | `DESIGN.md` の lint。`update-design-md` 使用時のみ(未導入でも動く) |
|
|
64
|
+
| [herdr](https://herdr.dev) | `--project` / `mode: "herdr"` 使用時のみ(`--cloud` は herdr に依存しない) |
|
|
65
|
+
| [GitHub MCP](https://github.com/github/github-mcp-server) | GitHub アクセスの高速化・クラウド実行時のプロキシ制限回避(任意。Claude 側のコネクタで有効化) |
|
|
80
66
|
|
|
81
|
-
CLI 本体に npm の実行時依存はない(
|
|
67
|
+
CLI 本体に npm の実行時依存はない(Node.js 標準モジュールのみで動作する)。
|
|
82
68
|
|
|
83
69
|
### インストール
|
|
84
70
|
|
|
@@ -86,7 +72,7 @@ CLI 本体に npm の実行時依存はない(esbuild で `dist/index.js` に
|
|
|
86
72
|
npx claude-task-worker install
|
|
87
73
|
```
|
|
88
74
|
|
|
89
|
-
|
|
75
|
+
マーケットプレイス追加・プラグイン導入・CLI 本体のグローバルインストール・各種 CLI(CodeGraph / DESIGN.md / Pen)と Playwright ブラウザの取得を一括で行う。いずれかが失敗しても処理は継続する。インストール後、Claude Code のセッションを再起動するとプラグインが有効になる。
|
|
90
76
|
|
|
91
77
|
個別にやる場合:
|
|
92
78
|
|
|
@@ -96,47 +82,7 @@ claude plugin marketplace add getty104/claude-task-worker
|
|
|
96
82
|
claude plugin install claude-task-worker@claude-task-worker
|
|
97
83
|
```
|
|
98
84
|
|
|
99
|
-
herdr
|
|
100
|
-
|
|
101
|
-
クラウド実行(`--cloud` フラグ)を使う場合は、クラウド VM(Claude Code on the web)側にもプラグイン・CLI が必要になる。claude.ai の環境設定(Environment setup script / セットアップスクリプト欄)に次の2行を記載しておく。
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npx claude-task-worker install
|
|
105
|
-
npx claude-task-worker cloud-setup
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
`cloud-setup` は VM 側の `~/.claude/settings.json` に権限モード(`permissions.defaultMode: "auto"`)・出力スタイル(`outputStyle: "Proactive"`)・言語(`language: "Japanese"`)を書き込み、あわせてグローバル gitignore へ `.codegraph/` を登録する(CodeGraph のインデックス構築自体はプラグインの `SessionStart` フックが毎セッション行う)。クラウドセッションは起動フラグの `--permission-mode` を受理するだけで反映しないため、この設定ファイルが権限モードを指定する唯一の経路になる(設定しないと「編集を受け入れる」で動く)。書き込みはキー単位のマージで、既存の設定は消さない。
|
|
109
|
-
|
|
110
|
-
あわせて、クラウドセッションが push / PR 作成を行うには対象リポジトリの GitHub App 連携が必要。
|
|
111
|
-
|
|
112
|
-
UIデザイン先行ワークフローを使う場合は、同じ claude.ai の環境設定の環境変数欄に `PEN_CLI_KEY` も設定する。`.pen` を扱うスキル(`edit-pencil-design` / `inspect-pencil-node` / `resolve-pencil-conflict`)の Pen CLI 認証に使うもので、クラウド VM では対話ログインができないため。キーの発行元と値の形式は後述の「[Pen CLI のログイン](#pen-cli-のログイン)」を参照。
|
|
113
|
-
|
|
114
|
-
詳細は後述の「[`--cloud`](#--cloud)」を参照。
|
|
115
|
-
|
|
116
|
-
### GitHub コネクタの有効化
|
|
117
|
-
|
|
118
|
-
GitHub MCP は Claude 側のコネクタとして有効化する(本プラグインは `.mcp.json` で宣言しない)。claude.ai の設定 > コネクタ、または Claude Code の `/mcp` から GitHub コネクタを有効化する。
|
|
119
|
-
|
|
120
|
-
任意の設定であり、未設定でもスキルは `gh` へフォールバックして動作する。対応表は [`plugin/references/github-access.md`](./plugin/references/github-access.md) を参照。
|
|
121
|
-
|
|
122
|
-
### Pen CLI のログイン
|
|
123
|
-
|
|
124
|
-
`.pen` を扱うスキル(`edit-pencil-design` / `inspect-pencil-node` / `resolve-pencil-conflict`)は Pen CLI の認証を必要とする。未ログインだと `.pen` の読み書きが失敗するため、UIデザイン先行ワークフローを使うなら**インストール後に一度ログインしておく**。
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
pencil login # メールアドレス + パスワード、またはメールアドレス + OTP コード
|
|
128
|
-
pencil status # 認証状態の確認
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
セッショントークンは `~/.pencil/session-cli.json` に保存され、以降のコマンドで再利用される。
|
|
132
|
-
|
|
133
|
-
CI やワーカーを実行するマシンなど対話ログインできない環境では、環境変数 `PEN_CLI_KEY`(pen.dev の組織設定 > Developer Keys で発行)を使う。保存済みトークンより優先される。
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
export PEN_CLI_KEY=pencil_cli_...
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
詳細は [Pen CLI のドキュメント](https://docs.pen.dev/for-developers/pen-cli)を参照。
|
|
85
|
+
herdr は `curl -fsSL https://herdr.dev/install.sh | sh` または `brew install herdr`([ドキュメント](https://herdr.dev/docs/install/))。
|
|
140
86
|
|
|
141
87
|
### 更新
|
|
142
88
|
|
|
@@ -144,11 +90,11 @@ export PEN_CLI_KEY=pencil_cli_...
|
|
|
144
90
|
claude-task-worker update
|
|
145
91
|
```
|
|
146
92
|
|
|
147
|
-
マーケットプレイス・プラグイン・CLI
|
|
93
|
+
マーケットプレイス・プラグイン・CLI 本体・各種 CLI をまとめて更新する。プラグインの反映にはセッション再起動が必要。
|
|
148
94
|
|
|
149
95
|
### 初期化
|
|
150
96
|
|
|
151
|
-
対象リポジトリで実行すると、GitHub ラベル・Issue テンプレート・GitHub Actions
|
|
97
|
+
対象リポジトリで実行すると、GitHub ラベル・Issue テンプレート・GitHub Actions ワークフロー・設定ファイル(`claude-task-worker.json`)が作成され、CodeGraph のインデックスが構築される。
|
|
152
98
|
|
|
153
99
|
```bash
|
|
154
100
|
claude-task-worker init # 既存ファイルは保護
|
|
@@ -160,30 +106,17 @@ claude-task-worker init --force # 強制上書き
|
|
|
160
106
|
| ラベル | 用途 |
|
|
161
107
|
|---|---|
|
|
162
108
|
| `cc-triage-scope` | トリアージ対象マーク(Issue/PR) |
|
|
163
|
-
| `cc-issue-created` | `create-issue` 由来の Issue
|
|
164
|
-
| `cc-update-issue` | Issue
|
|
165
|
-
| `cc-
|
|
166
|
-
| `cc-exec-issue` | Issue 実行トリガー |
|
|
167
|
-
| `cc-fix-onetime` | PR 修正トリガー(1回) |
|
|
168
|
-
| `cc-resolve-conflict` | PR コンフリクト解消トリガー |
|
|
109
|
+
| `cc-issue-created` | `create-issue` 由来の Issue マーク |
|
|
110
|
+
| `cc-update-issue` / `cc-answer-issue-questions` / `cc-exec-issue` | Issue の更新 / 確認事項回答 / 実行トリガー |
|
|
111
|
+
| `cc-fix-onetime` / `cc-resolve-conflict` | PR の修正 / コンフリクト解消トリガー |
|
|
169
112
|
| `cc-in-progress` | 処理中ステータス |
|
|
170
|
-
| `cc-need-human-check` |
|
|
113
|
+
| `cc-need-human-check` | 人間の確認が必要(付与中はワーカーの対象外) |
|
|
171
114
|
| `cc-pr-created` | PR 作成完了マーク |
|
|
172
115
|
| `cc-epic-issue` | エピックマーク(Issue: サブ全 Close で `epic-issue` 起動 / PR: リリースゲート対象) |
|
|
173
|
-
| `cc-release-ready` | エピックPR
|
|
174
|
-
| `cc-create-ui-design` | UI
|
|
175
|
-
| `cc-ui-design
|
|
176
|
-
| `cc-
|
|
177
|
-
| `cc-ui-design` | デザインPRのマーカー(`triage-pr` のレビュー観点切り替え用) |
|
|
178
|
-
| `cc-cloud-done` | クラウド実行タスクの完了マーク(セッションが最後に付与し、ワーカーが検知して除去する。人が手動で付与しても同じ経路で完了扱いになるため、張り付いたクラウドタスクの救済手段としても使える) |
|
|
179
|
-
|
|
180
|
-
作成されるファイル:
|
|
181
|
-
|
|
182
|
-
- `.github/ISSUE_TEMPLATE/cc-triage-scope.yml` — `cc-triage-scope` 付き Issue 作成用テンプレート
|
|
183
|
-
- `.github/workflows/assign-creator-on-cc-triage-scope.yml` — Issue 作成者の自動アサイン
|
|
184
|
-
- `claude-task-worker.json` — 設定ファイル。**ワーカーごとの既定値は書き出さない**(写経するとプラグイン更新で既定が変わっても古い値に固定されるため)。上書きしたいワーカーだけ手で追記する
|
|
185
|
-
|
|
186
|
-
CodeGraph のセットアップとして、グローバル gitignore(`~/.config/git/ignore`)へ `.codegraph/` を冪等に追記し、`codegraph init` を実行する。CodeGraph 未インストールでも `init` 全体は失敗しない。
|
|
116
|
+
| `cc-release-ready` | エピックPRがリリース可能と判定されたマーク(マージは人間が実施) |
|
|
117
|
+
| `cc-create-ui-design` / `cc-ui-design-pr-created` / `cc-ui-design-ready` | UIデザイン先行ワークフローの各段階 |
|
|
118
|
+
| `cc-ui-design` | デザインPRのマーカー |
|
|
119
|
+
| `cc-cloud-done` | クラウド実行タスクの完了マーク(セッションが付与し、ワーカーが検知して除去する。手動付与で張り付いたタスクを救済できる) |
|
|
187
120
|
|
|
188
121
|
## コマンド
|
|
189
122
|
|
|
@@ -193,28 +126,28 @@ claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--
|
|
|
193
126
|
|
|
194
127
|
| コマンド | 内容 |
|
|
195
128
|
|---|---|
|
|
196
|
-
| 各ワーカー名 | 単一ワーカーを起動(`exec-issue` / `triage-pr`
|
|
197
|
-
| `all` | 通常ワーカー9つ + 定期ワーカー3
|
|
198
|
-
| `yolo` |
|
|
129
|
+
| 各ワーカー名 | 単一ワーカーを起動(`exec-issue` / `triage-pr` など) |
|
|
130
|
+
| `all` | 通常ワーカー9つ + 定期ワーカー3つ(`triage-created-issue` / `triage-pr` / `check-dependabot` を除く) |
|
|
131
|
+
| `yolo` | 全ワーカーを同時にポーリング |
|
|
199
132
|
| `init` | ラベル・テンプレート・設定ファイルの作成と CodeGraph セットアップ |
|
|
200
133
|
| `install` / `update` | 上記「セットアップ」を参照 |
|
|
201
|
-
| `cloud-setup [--force]` |
|
|
134
|
+
| `cloud-setup [--force]` | クラウド VM 側の準備(下記「`--cloud`」を参照) |
|
|
202
135
|
| `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack にも通知 |
|
|
203
136
|
| `version` | CLI のバージョンを表示(`--version` / `-v` も可) |
|
|
204
137
|
|
|
205
138
|
### `--epic <issue-number>`
|
|
206
139
|
|
|
207
|
-
指定したエピック Issue のサブ Issue
|
|
140
|
+
指定したエピック Issue のサブ Issue のみを処理対象に絞る。複数指定は OR。
|
|
208
141
|
|
|
209
142
|
```bash
|
|
210
143
|
claude-task-worker all --epic 100 --epic 200
|
|
211
144
|
```
|
|
212
145
|
|
|
213
|
-
`epic-issue`
|
|
146
|
+
`epic-issue` ワーカーではエピック Issue 自身の番号として照合される。
|
|
214
147
|
|
|
215
148
|
### `--label <label>`
|
|
216
149
|
|
|
217
|
-
トリガーラベルに加えて指定ラベルが付いた Issue
|
|
150
|
+
トリガーラベルに加えて指定ラベルが付いた Issue のみに絞る。複数指定は AND。`--epic` と併用可能。
|
|
218
151
|
|
|
219
152
|
```bash
|
|
220
153
|
claude-task-worker all --label priority-high --label needs-design
|
|
@@ -222,117 +155,103 @@ claude-task-worker all --label priority-high --label needs-design
|
|
|
222
155
|
|
|
223
156
|
### `--project <name>`
|
|
224
157
|
|
|
225
|
-
指定したプロジェクト(またはグループ、`all`)へ [herdr](https://herdr.dev)
|
|
158
|
+
指定したプロジェクト(またはグループ、`all`)へ [herdr](https://herdr.dev) 経由でコマンドをディスパッチする。CLI はワーカーを直接実行せず、プロジェクトごとに独立した herdr ワークスペースを作ってそこでコマンドを実行し、稼働状況をステータステーブルに表示する。SIGTERM/SIGINT で全セッションを一括停止する。
|
|
226
159
|
|
|
227
160
|
```bash
|
|
228
161
|
claude-task-worker all --project all
|
|
229
|
-
claude-task-worker
|
|
230
|
-
claude-task-worker exec-issue --project app-a --epic 100 --label priority-high
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
プロジェクト名・グループ名は `$XDG_CONFIG_HOME/claude-task-worker/config.json`(未設定なら `~/.config/claude-task-worker/config.json`)で定義する。`all` は全プロジェクトを指す予約語。
|
|
234
|
-
|
|
235
|
-
```json
|
|
236
|
-
{
|
|
237
|
-
"mode": "default",
|
|
238
|
-
"advisor": false,
|
|
239
|
-
"permission": "bypassPermissions",
|
|
240
|
-
"projects": {
|
|
241
|
-
"app-a": "/Users/me/repos/app-a",
|
|
242
|
-
"app-b": "/Users/me/repos/app-b"
|
|
243
|
-
},
|
|
244
|
-
"projectGroups": {
|
|
245
|
-
"frontend": ["app-a", "app-b"]
|
|
246
|
-
}
|
|
247
|
-
}
|
|
162
|
+
claude-task-worker exec-issue --project app-a --epic 100
|
|
248
163
|
```
|
|
249
164
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
- **一斉起動**: プロジェクトごとに `ctw:<プロジェクト名>` ラベルのワークスペースを作り、そこで(`--project` を除いた)同じコマンドを実行する。ワーカーが実際に起動したかを確認し、起動しなければ再送・失敗判定する
|
|
253
|
-
- **稼働一覧**: プロジェクト名・ワークスペースID・ペインID・ステータス・稼働時間をステータステーブルに描画する
|
|
254
|
-
- **一括停止**: SIGTERM/SIGINT で全セッションへ ctrl-c を送り、終了を待ってワークスペースを閉じる。もう一度送ると強制終了
|
|
165
|
+
プロジェクト名・グループ名は `config.json` で定義する(下記「設定ファイル」)。`all` は全プロジェクトを指す予約語。
|
|
255
166
|
|
|
256
167
|
`--project` と併用できないコマンド: `init` / `install` / `update` / `usage` / `version`
|
|
257
168
|
|
|
258
169
|
### `--cloud`
|
|
259
170
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
許可リスト方式にしているのは、クラウド実行が「成果ゼロでも完了扱いになり、トリガーラベルの再付与で再起動され続ける」失敗の仕方をするため。`triage-pr` などは GraphQL ゲートで判断材料を取得できず空振りするが、完了扱いになるとポーリング間隔ごとにクラウドセッションを焼き続ける。
|
|
171
|
+
タスクを Claude Code on the web(クラウド VM)で実行する。プロセス単位のフラグで既定は無効。
|
|
263
172
|
|
|
264
173
|
```bash
|
|
265
174
|
claude-task-worker exec-issue --cloud
|
|
266
175
|
claude-task-worker all --cloud
|
|
267
176
|
```
|
|
268
177
|
|
|
178
|
+
**クラウドで実行されるのは `exec-issue` と `fix-review-point` の2ワーカーだけ**で、それ以外は `--cloud` を付けてもローカル実行のまま残る(`all` / `yolo` にそのまま付けられる)。どのワーカーがクラウドで走るかは起動時にログへ出る。
|
|
179
|
+
|
|
269
180
|
前提条件:
|
|
270
181
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
182
|
+
| 前提 | 備考 |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `script` コマンドが使える環境(macOS / Linux) | クラウドセッションの作成には TTY が必要で、それを `script` コマンドの疑似 pty で供給する。platform が darwin/linux でない、または `script` が PATH に無い場合はタスクを1件も起動せずエラー終了する(フォールバックしない) |
|
|
185
|
+
| claude.ai アカウントでのサインイン | API キー認証・第三者プロバイダ(Bedrock / Vertex)構成では利用不可。`--cloud` 指定時のみ検査される |
|
|
186
|
+
| 対象リポジトリの GitHub App 連携 | クラウド VM から push / PR 作成を行うため |
|
|
187
|
+
| claude.ai の「プルリクエストを自動的に作成する」「プルリクエストの自動修正」が **OFF** | 下記 |
|
|
188
|
+
| VM 側のセットアップスクリプト | 下記 |
|
|
189
|
+
| `PEN_CLI_KEY` 環境変数 | UIデザイン先行ワークフローを使う場合のみ(下記「Pen CLI のログイン」) |
|
|
190
|
+
|
|
191
|
+
claude.ai の設定にある **「プルリクエストを自動的に作成する」「プルリクエストの自動修正」は必ず OFF にする**。どちらもタスクワーカーの制御と競合する。前者はスキルの `create-pr`(`Closes #<N>`・ベースブランチ・ラベル付与)とは別に PR を作るため PR が重複し、後者はセッションが PR 作成後に終了せずレビューを待って修正を続けるため、`cc-cloud-done` による完了検知が 4 時間のタイムアウトまで効かない。
|
|
276
192
|
|
|
277
|
-
|
|
193
|
+
claude.ai の環境設定(セットアップスクリプト欄)に次の2行を記載しておく。
|
|
278
194
|
|
|
279
|
-
|
|
195
|
+
```bash
|
|
196
|
+
npx claude-task-worker install
|
|
197
|
+
npx claude-task-worker cloud-setup
|
|
198
|
+
```
|
|
280
199
|
|
|
281
|
-
|
|
200
|
+
`cloud-setup` は VM 側の `~/.claude/settings.json` に権限モード・出力スタイル・言語を書き込み、グローバル gitignore へ `.codegraph/` を登録する。クラウドセッションは起動フラグの `--permission-mode` を反映しないため、この設定ファイルが権限モードを指定する唯一の経路になる。書き込みはキー単位のマージで既存の設定を消さない(`--force` で上書き)。
|
|
282
201
|
|
|
283
|
-
|
|
202
|
+
補足:
|
|
284
203
|
|
|
285
|
-
|
|
204
|
+
- クラウド実行のタスクは worktree を作らない(VM が自前でリポジトリを持つため)
|
|
205
|
+
- 完了は `cc-cloud-done` ラベルで検知する。4時間で応答がなければ打ち切り、`cc-need-human-check` を付けて失敗通知する
|
|
206
|
+
- `--project` と併用した場合、`--cloud` は各プロジェクトへそのまま転送される
|
|
207
|
+
- `--cloud` と併用できないコマンド: `init` / `install` / `update` / `usage` / `version`
|
|
208
|
+
- `--cloud` は `mode`(`default` / `herdr`)に依存しない。クラウドセッションの作成は `script` コマンドの疑似 pty で完結し、herdr のペインを使わないため、どちらの `mode` でも同じ経路を通る
|
|
286
209
|
|
|
287
210
|
詳細は [`docs/prd-cloud-worker-execution.md`](./docs/prd-cloud-worker-execution.md) を参照。
|
|
288
211
|
|
|
212
|
+
### Pen CLI のログイン
|
|
213
|
+
|
|
214
|
+
`.pen` を扱うスキルは Pen CLI の認証を必要とする。UIデザイン先行ワークフローを使うなら一度ログインしておく。
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pencil login # メールアドレス + パスワード、またはメールアドレス + OTP コード
|
|
218
|
+
pencil status # 認証状態の確認
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
CI やクラウド VM など対話ログインできない環境では、環境変数 `PEN_CLI_KEY`(pen.dev の組織設定 > Developer Keys で発行)を使う。保存済みトークンより優先される。
|
|
222
|
+
|
|
289
223
|
## 設定ファイル
|
|
290
224
|
|
|
291
|
-
グローバル設定は
|
|
225
|
+
グローバル設定は `$XDG_CONFIG_HOME/claude-task-worker/config.json`(未設定なら `~/.config/claude-task-worker/config.json`)、リポジトリ設定は実行ディレクトリ直下の `claude-task-worker.json`。
|
|
292
226
|
|
|
293
227
|
### `config.json`(グローバル)
|
|
294
228
|
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"mode": "default",
|
|
232
|
+
"advisor": false,
|
|
233
|
+
"permission": "bypassPermissions",
|
|
234
|
+
"projects": {
|
|
235
|
+
"app-a": "/Users/me/repos/app-a",
|
|
236
|
+
"app-b": "/Users/me/repos/app-b"
|
|
237
|
+
},
|
|
238
|
+
"projectGroups": {
|
|
239
|
+
"frontend": ["app-a", "app-b"]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
295
244
|
| キー | 既定 | 説明 |
|
|
296
245
|
|---|---|---|
|
|
297
246
|
| `projects` | - | プロジェクト名 → 絶対パス |
|
|
298
247
|
| `projectGroups` | `{}` | グループ名 → プロジェクト名配列 |
|
|
299
|
-
| `mode` | `"default"` |
|
|
300
|
-
| `advisor` | `false` | `--advisor`
|
|
301
|
-
| `permission` | `"bypassPermissions"` | Claude CLI
|
|
302
|
-
|
|
303
|
-
#### `mode`(タスクの実行形態)
|
|
304
|
-
|
|
305
|
-
全ワーカー・全プロジェクトに一括適用される(個別指定は不可)。
|
|
306
|
-
|
|
307
|
-
| `mode` | 挙動 |
|
|
308
|
-
|---|---|
|
|
309
|
-
| `"default"` | タスクを `claude -p`(非対話 print モード)の子プロセスとして実行 |
|
|
310
|
-
| `"herdr"` | タスクを herdr のタブ内で TUI セッションとして実行。実行中の様子を herdr で覗ける |
|
|
311
|
-
|
|
312
|
-
`"herdr"` では、worktree 作成後に `ctw:<プロジェクト名>:#<番号>` ラベルのタブを作り、そのルートペインで claude を TUI 起動する。agent ステータスを監視して完了を検知し、セッション transcript から最終レポートを回収して通知に使う。`blocked`(claude が入力待ち)になっても自動失敗にせず待機し、ステータステーブルに `running:blocked` と表示するので herdr のタブを開いて直接対応できる。herdr が未インストール・未起動なら起動時にエラー終了する(`"default"` へフォールバックしない)。
|
|
313
|
-
|
|
314
|
-
> ℹ️ タスク完了時の通知音はワーカー側から止められない(音を鳴らすのは herdr サーバープロセスで、`HERDR_DISABLE_SOUND` もそのプロセスの環境変数として読まれるため)。無音にするには `~/.config/herdr/config.toml` に `[ui.sound] enabled = false` を書いて `herdr server reload-config` する。ただし herdr サーバー全体に効くため、対話セッションの完了音も鳴らなくなる。
|
|
315
|
-
|
|
316
|
-
#### `advisor`(アドバイザーモデル)
|
|
317
|
-
|
|
318
|
-
`true` にすると、タスク起動時に Claude CLI へ `--advisor <model>` を渡す。渡すモデルは `claude-task-worker.json` の `workers.<名前>.advisorModel`。`mode` と同じくトップレベル一括で、プロジェクト単位・ワーカー単位のオン/オフはできない。空文字が指定されたワーカーには渡さない。
|
|
319
|
-
|
|
320
|
-
advisor は main モデル以上の能力が必要(Claude CLI の制約)。`model` が `opus` のワーカーに `opus` advisor を付けても意味がなく、既定で `sonnet` のワーカーも `opus` advisor を付けると下げたぶんのコスト削減を打ち消すため、`advisorModel` の既定値は全ワーカー空文字(advisor なし)。`sonnet` のワーカーの品質が落ちた場合の調整弁として `advisorModel: "opus"` を指定できる。
|
|
321
|
-
|
|
322
|
-
#### `permission`(権限モード)
|
|
323
|
-
|
|
324
|
-
タスク起動時に Claude CLI へ渡す[権限モード](https://code.claude.com/docs/ja/permission-modes)。`mode` / `advisor` と同じくトップレベル一括で、プロジェクト単位・ワーカー単位の指定はできない。
|
|
248
|
+
| `mode` | `"default"` | `"default"`: `claude -p` の子プロセスとして実行 / `"herdr"`: herdr のタブ内で TUI 起動し、実行中の様子を覗ける(`--project` はこちらが必要) |
|
|
249
|
+
| `advisor` | `false` | `true` で `--advisor <model>` を渡す。モデルは `claude-task-worker.json` の `advisorModel` |
|
|
250
|
+
| `permission` | `"bypassPermissions"` | Claude CLI の[権限モード](https://code.claude.com/docs/ja/permission-modes)(`bypassPermissions` / `dontAsk` / `auto` / `acceptEdits` / `manual` / `plan`) |
|
|
325
251
|
|
|
326
|
-
|
|
327
|
-
|---|---|
|
|
328
|
-
| `"bypassPermissions"`(既定) | 全許可。承認するユーザーが常駐しない自律実行のため既定 |
|
|
329
|
-
| `"dontAsk"` | 許可されていない操作は確認せずスキップする |
|
|
330
|
-
| `"auto"` | 安全な操作は自動承認、危険な操作のみ確認 |
|
|
331
|
-
| `"acceptEdits"` | ファイル編集は自動承認、それ以外は都度確認 |
|
|
332
|
-
| `"manual"` | 標準の権限確認 |
|
|
333
|
-
| `"plan"` | 読み取りのみ。変更は行わない |
|
|
252
|
+
いずれもトップレベル一括で、プロジェクト単位・ワーカー単位の指定はできない。`permission` はワーカーに承認するユーザーがいないため、`bypassPermissions` / `dontAsk` 以外ではタスクが承認待ちで止まりうる。
|
|
334
253
|
|
|
335
|
-
|
|
254
|
+
> ℹ️ `mode: "herdr"` の完了通知音はワーカー側から止められない。無音にするには `~/.config/herdr/config.toml` に `[ui.sound] enabled = false` を書いて `herdr server reload-config` する(herdr サーバー全体に効く)。
|
|
336
255
|
|
|
337
256
|
### `claude-task-worker.json`(リポジトリ)
|
|
338
257
|
|
|
@@ -341,32 +260,34 @@ advisor は main モデル以上の能力が必要(Claude CLI の制約)。`
|
|
|
341
260
|
| `fixReviewPointCallbackCommentMessage` | string | - | `fix-review-point` 完了時に PR へ投稿するコメント(未設定なら投稿しない) |
|
|
342
261
|
| `uiDesign` | object | `{ "enabled": false, "designDir": "designs", "yolo": false }` | UIデザイン先行ワークフロー(下記) |
|
|
343
262
|
| `workers` | object | `{}` | ワーカーごとの上書き設定(下記) |
|
|
344
|
-
| `lastRun` | object | `{}` |
|
|
263
|
+
| `lastRun` | object | `{}` | 定期ワーカーの最終実行時刻。ワーカーが自動更新するため手で編集しない |
|
|
345
264
|
|
|
346
265
|
#### ワーカーごとの設定
|
|
347
266
|
|
|
348
267
|
未指定のワーカー・フィールドは既定値にフォールバックする。
|
|
349
268
|
|
|
350
|
-
| フィールド |
|
|
351
|
-
|
|
352
|
-
| `skill` |
|
|
353
|
-
| `model` |
|
|
354
|
-
| `advisorModel` |
|
|
355
|
-
| `effort` |
|
|
356
|
-
| `pollingIntervalSeconds` |
|
|
357
|
-
| `cooldownSeconds` |
|
|
358
|
-
| `maxConcurrentTasks` |
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
| ワーカー | `model` | `effort` | `
|
|
363
|
-
|
|
364
|
-
| `exec-issue` / `fix-review-point` / `answer-issue-questions` / `create-issue` / `create-ui-design` / `triage-pr` | `opus` | `high` |
|
|
365
|
-
| `update-issue` / `triage-created-issue` / `resolve-conflict` | `sonnet` | `high` |
|
|
366
|
-
| `
|
|
367
|
-
| `
|
|
368
|
-
| `update-coding-guidelines` / `update-requirement-rules` / `update-design-md` | `opus` | `high` |
|
|
369
|
-
| (未知のワーカー名) | `opus` | `high` |
|
|
269
|
+
| フィールド | 説明 |
|
|
270
|
+
|---|---|
|
|
271
|
+
| `skill` | Claude CLI の `-p` に渡すスラッシュコマンド(`"<skill> <番号>"` の形で起動) |
|
|
272
|
+
| `model` | `--model` の値(`sonnet` / `opus` / `haiku`) |
|
|
273
|
+
| `advisorModel` | `--advisor` の値。空文字なら advisor なし。`config.json` の `advisor: true` のときだけ参照される |
|
|
274
|
+
| `effort` | `--effort` の値(`high` / `medium` / `low`) |
|
|
275
|
+
| `pollingIntervalSeconds` | ポーリング間隔(秒) |
|
|
276
|
+
| `cooldownSeconds` | タスク完了後にポーリングを止める時間(秒)。既定 `0` |
|
|
277
|
+
| `maxConcurrentTasks` | 同時実行できるタスクの最大数。既定 `1` |
|
|
278
|
+
|
|
279
|
+
既定値:
|
|
280
|
+
|
|
281
|
+
| ワーカー | `model` | `effort` | `pollingIntervalSeconds` |
|
|
282
|
+
|---|---|---|---|
|
|
283
|
+
| `exec-issue` / `fix-review-point` / `answer-issue-questions` / `create-issue` / `create-ui-design` / `triage-pr` | `opus` | `high` | 60 |
|
|
284
|
+
| `update-issue` / `triage-created-issue` / `resolve-conflict` | `sonnet` | `high` | 60 |
|
|
285
|
+
| `epic-issue` / `apply-ui-design` | `sonnet` | `medium` | 300 |
|
|
286
|
+
| `check-dependabot` | `sonnet` | `high` | 3600 |
|
|
287
|
+
| `update-coding-guidelines` / `update-requirement-rules` / `update-design-md` | `opus` | `high` | 3600 |
|
|
288
|
+
| (未知のワーカー名) | `opus` | `high` | 60 |
|
|
289
|
+
|
|
290
|
+
`advisorModel` の既定は全ワーカー空文字(advisor なし)。
|
|
370
291
|
|
|
371
292
|
設定例:
|
|
372
293
|
|
|
@@ -375,8 +296,7 @@ advisor は main モデル以上の能力が必要(Claude CLI の制約)。`
|
|
|
375
296
|
"workers": {
|
|
376
297
|
"exec-issue": { "model": "opus", "cooldownSeconds": 600, "maxConcurrentTasks": 3 },
|
|
377
298
|
"fix-review-point": { "model": "sonnet", "advisorModel": "opus", "maxConcurrentTasks": 2 },
|
|
378
|
-
"triage-pr": { "effort": "medium", "pollingIntervalSeconds": 120 }
|
|
379
|
-
"check-dependabot": { "model": "haiku", "pollingIntervalSeconds": 7200 }
|
|
299
|
+
"triage-pr": { "effort": "medium", "pollingIntervalSeconds": 120 }
|
|
380
300
|
}
|
|
381
301
|
}
|
|
382
302
|
```
|
|
@@ -385,86 +305,58 @@ advisor は main モデル以上の能力が必要(Claude CLI の制約)。`
|
|
|
385
305
|
|
|
386
306
|
### Epic(親Issue)連携
|
|
387
307
|
|
|
388
|
-
親 Issue
|
|
308
|
+
親 Issue を持つサブ Issue は、デフォルトブランチではなく `cc-epic-<親Issue番号>` ブランチから worktree を作って処理される。サブ Issue ごとのPRを単一の統合ブランチへ集約するため。エピックブランチが remote に無ければ自動で派生・push される。
|
|
389
309
|
|
|
390
|
-
サブ Issue がすべて Close されると `epic-issue`
|
|
310
|
+
サブ Issue がすべて Close されると `epic-issue` ワーカーがエピックブランチからまとめてPRを作る。エピックPRは `triage-pr` がマージ可能と判定してもマージせず `cc-release-ready` を付けるだけで、実際のマージ(リリース)は人間に委ねられる。
|
|
391
311
|
|
|
392
312
|
### UIデザイン先行ワークフロー
|
|
393
313
|
|
|
394
|
-
UI実装 Issue について、実装の前に Pencil(`.pen`)でデザインを作り、独立したPR
|
|
395
|
-
|
|
396
|
-
`uiDesign.enabled` によるオプトインで、既定(`false`)では2つのワーカーが起動しないため、Pencil を使っていないリポジトリの挙動は本機能の追加前と完全に一致する。
|
|
314
|
+
UI実装 Issue について、実装の前に Pencil(`.pen`)でデザインを作り、独立したPRとしてマージしてから実装へ進むフロー。`uiDesign.enabled` によるオプトインで、既定(`false`)では関連ワーカーが起動しない。
|
|
397
315
|
|
|
398
316
|
| キー | 既定 | 意味 |
|
|
399
317
|
|---|---|---|
|
|
400
|
-
| `uiDesign.enabled` | `false` |
|
|
318
|
+
| `uiDesign.enabled` | `false` | 有効化 |
|
|
401
319
|
| `uiDesign.designDir` | `"designs"` | `.pen` とスナップショットの配置先(リポジトリルートからの相対パス) |
|
|
402
|
-
| `uiDesign.yolo` | `false` |
|
|
320
|
+
| `uiDesign.yolo` | `false` | `true` でデザインPRに `cc-triage-scope` を付け、既存フローで自動レビュー・自動マージへ流す |
|
|
403
321
|
|
|
404
322
|
```text
|
|
405
|
-
|
|
406
|
-
├─ UI実装タスクでない → cc-exec-issue
|
|
323
|
+
triage-created-issue(ルーティング)
|
|
324
|
+
├─ UI実装タスクでない → cc-exec-issue
|
|
407
325
|
└─ UI実装タスク → cc-create-ui-design
|
|
408
|
-
→ create-ui-design
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
→ yolo: true → triage-pr / fix-review-point / resolve-conflict(既存フローでレビュー・マージ)
|
|
413
|
-
yolo: false → 人がデザインPRをレビュー・マージ
|
|
414
|
-
→ apply-ui-design ワーカー
|
|
415
|
-
・デザインPRが MERGED になるまで skip
|
|
416
|
-
・Issue description に「## UIデザイン」セクションを追記
|
|
417
|
-
・cc-ui-design-ready + cc-exec-issue を付与
|
|
418
|
-
→ exec-issue(デザインを参照元として実装)
|
|
326
|
+
→ create-ui-design: .pen + snapshots を作り、デザインPR(cc-ui-design)を作成
|
|
327
|
+
→ yolo: true なら自動レビュー・マージ / false なら人がレビュー・マージ
|
|
328
|
+
→ apply-ui-design: マージ後に Issue description へ「## UIデザイン」を追記し cc-exec-issue を付与
|
|
329
|
+
→ exec-issue: デザインを参照元として実装
|
|
419
330
|
```
|
|
420
331
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
デザインが不要と判明した場合は `create-ui-design` が理由をコメントして `cc-ui-design-ready` + `cc-exec-issue` を付与し、人手を介さず実装へ復帰する。Pencil が使えない環境やデザインPRが却下された場合は `cc-need-human-check` で停止する。
|
|
332
|
+
デザインが不要と判明した場合は `create-ui-design` が理由をコメントして実装へ復帰する。Pencil が使えない環境やデザインPRが却下された場合は `cc-need-human-check` で停止する。
|
|
424
333
|
|
|
425
334
|
## Slack通知
|
|
426
335
|
|
|
427
|
-
環境変数 `CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL` に
|
|
336
|
+
環境変数 `CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL` に Incoming Webhook URL を設定すると、タスクの完了時・失敗時に通知が送られる。未設定なら送信されない。
|
|
428
337
|
|
|
429
338
|
```bash
|
|
430
339
|
export CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
|
|
431
340
|
claude-task-worker all
|
|
432
341
|
```
|
|
433
342
|
|
|
434
|
-
通知には Claude API
|
|
435
|
-
|
|
436
|
-
クラウド実行(`--cloud`)のタスクは、通知の先頭行にクラウドセッションのURL(`https://claude.ai/code/<id>`)が入る。Slack で本文が折りたたまれても先頭行は見えるため。
|
|
343
|
+
通知には Claude API の使用状況も含まれる。あわせて [RunCat Neo](https://kyome.io/runcat/) 用のスナップショットを `~/.claude/runcat-usage.json`(`RUNCAT_OUT_FILE` で変更可)へ書き出す。クラウド実行のタスクは通知の先頭行にセッションURLが入る。
|
|
437
344
|
|
|
438
345
|
## プロセス管理
|
|
439
346
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
- タスクID・タイトル・ステータス(running/completed/failed)・開始時刻・経過時間を表示
|
|
443
|
-
- `mode: "herdr"` では実行中の行に agent ステータスが併記される(`running:working` / `running:blocked`)
|
|
444
|
-
- 同一 Issue/PR の重複実行を自動防止
|
|
445
|
-
- SIGTERM/SIGINT で全子プロセスを graceful shutdown(もう一度送ると強制終了し、ラベル・worktree の後片付けを試みる)
|
|
446
|
-
- 前回の異常終了で残った worktree はワーカー起動時に自動回収される(実行中タスク・対話セッションが掴んでいるものは保護される)
|
|
447
|
-
|
|
448
|
-
### タスク実行のガード
|
|
449
|
-
|
|
450
|
-
ワーカーは応答するユーザーがいない状態でスキルを起動するため、処理が未完のままセッションが終了してラベルだけ進む事故を防ぐガードを持つ。
|
|
347
|
+
実行中のタスクはリアルタイムのステータステーブルで表示される(タスクID・タイトル・ステータス・開始時刻・経過時間)。SIGTERM/SIGINT で graceful shutdown し、もう一度送ると強制終了してラベル・worktree の後片付けを試みる。前回の異常終了で残った worktree はワーカー起動時に自動回収される。
|
|
451
348
|
|
|
452
|
-
|
|
453
|
-
- **ツールの無効化**: `--disallowedTools` で `Monitor` / `ScheduleWakeup` / `AskUserQuestion` / `EnterPlanMode` / `Cron*` / `RemoteTrigger` / `EnterWorktree` を無効化する
|
|
454
|
-
- **自律実行原則の注入**: `--append-system-prompt` で「ユーザーに質問しない・全ステップを完遂してから終了する・曖昧なら安全側を選ぶ・サブエージェントの完了報告を検証する」および CodeGraph 優先のコード探索方針を注入する
|
|
455
|
-
- **完了検証**: `exec-issue` / `epic-issue` は PR の実在(または Issue のクローズ)を確認できるまで `cc-pr-created` を付けず、確認できなければ `cc-need-human-check` を付けて Issue にコメントを残す
|
|
456
|
-
- **空振り検知**: 正常終了しても出力が空のセッションは失敗として分類し、失敗通知(stderr の末尾を含む)を送る
|
|
457
|
-
- **起動プロセスの後片付け**: スキル終了時に `Stop` フックが `docker compose down` と、worktree を作業ディレクトリに持つ残留プロセスの `SIGTERM` をベストエフォートで実行する(worktree はスキル完了直後に削除されるため、残留プロセスが削除の妨げになるのを防ぐ)
|
|
349
|
+
ワーカーは応答するユーザーがいない状態でスキルを起動するため、処理が未完のままラベルだけ進む事故を防ぐガードを持つ(バックグラウンド実行と対話系ツールの無効化、自律実行原則のシステムプロンプト注入、PR 実在の完了検証、空出力セッションの失敗扱い、`Stop` フックによる残留プロセスの停止)。
|
|
458
350
|
|
|
459
351
|
## 開発
|
|
460
352
|
|
|
461
353
|
```bash
|
|
462
354
|
npm install
|
|
463
|
-
npm run build #
|
|
355
|
+
npm run build # 型チェック + esbuild で dist/index.js にバンドル
|
|
464
356
|
npm run dev # 型チェックの watch モード
|
|
465
|
-
npm test #
|
|
357
|
+
npm test # ユニットテスト
|
|
466
358
|
npm run lint # ESLint(--fix で自動修正)
|
|
467
|
-
npm run format # Prettier
|
|
359
|
+
npm run format # Prettier で整形
|
|
468
360
|
```
|
|
469
361
|
|
|
470
362
|
開発版をローカルから使う場合は `npm install && npm run build && npm link`。
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ var __export = (target, all) => {
|
|
|
12
12
|
// src/table.ts
|
|
13
13
|
var table_exports = {};
|
|
14
14
|
__export(table_exports, {
|
|
15
|
+
CLOUD_WORKTREE_LABEL: () => CLOUD_WORKTREE_LABEL,
|
|
16
|
+
CONTROL_CHARS: () => CONTROL_CHARS,
|
|
15
17
|
LOG_DISPLAY_LIMIT: () => LOG_DISPLAY_LIMIT,
|
|
16
18
|
TASK_DISPLAY_LIMIT: () => TASK_DISPLAY_LIMIT,
|
|
17
19
|
buildLogTableLines: () => buildLogTableLines,
|
|
@@ -28,7 +30,7 @@ __export(table_exports, {
|
|
|
28
30
|
import { format } from "node:util";
|
|
29
31
|
function getDisplayWidth(str) {
|
|
30
32
|
let width = 0;
|
|
31
|
-
for (const char of str) {
|
|
33
|
+
for (const char of str.replace(CONTROL_CHARS, "")) {
|
|
32
34
|
const code = char.codePointAt(0);
|
|
33
35
|
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141) {
|
|
34
36
|
width += 2;
|
|
@@ -180,17 +182,19 @@ function buildLogTableLines(entries) {
|
|
|
180
182
|
});
|
|
181
183
|
return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
|
|
182
184
|
}
|
|
183
|
-
var TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, logLines, rawLog, rawClear, consoleCaptured, CONTROL_CHARS;
|
|
185
|
+
var CLOUD_WORKTREE_LABEL, TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, logLines, rawLog, rawClear, consoleCaptured, CONTROL_CHARS;
|
|
184
186
|
var init_table = __esm({
|
|
185
187
|
"src/table.ts"() {
|
|
186
188
|
"use strict";
|
|
189
|
+
CLOUD_WORKTREE_LABEL = "\x1B[36m\u2601 running in cloud\x1B[0m";
|
|
187
190
|
TASK_DISPLAY_LIMIT = 20;
|
|
188
191
|
LOG_DISPLAY_LIMIT = 20;
|
|
189
192
|
logLines = [];
|
|
190
193
|
rawLog = console.log.bind(console);
|
|
191
194
|
rawClear = console.clear.bind(console);
|
|
192
195
|
consoleCaptured = false;
|
|
193
|
-
CONTROL_CHARS =
|
|
196
|
+
CONTROL_CHARS = // eslint-disable-next-line no-control-regex -- ANSI/制御文字を意図的に対象にする
|
|
197
|
+
/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
194
198
|
}
|
|
195
199
|
});
|
|
196
200
|
|
|
@@ -588,6 +592,7 @@ __export(herdr_runner_exports, {
|
|
|
588
592
|
buildHerdrTaskResult: () => buildHerdrTaskResult,
|
|
589
593
|
createCompletionTracker: () => createCompletionTracker,
|
|
590
594
|
extractCloudSessionId: () => extractCloudSessionId,
|
|
595
|
+
normalizePtyOutput: () => normalizePtyOutput,
|
|
591
596
|
observeAgentStatus: () => observeAgentStatus,
|
|
592
597
|
startHerdrTask: () => startHerdrTask,
|
|
593
598
|
stopHerdrTask: () => stopHerdrTask,
|
|
@@ -629,6 +634,9 @@ function observeAgentStatus(tracker, status) {
|
|
|
629
634
|
function extractCloudSessionId(text) {
|
|
630
635
|
return CLOUD_SESSION_URL_RE.exec(text)?.[1] ?? CLOUD_SESSION_CREATED_RE.exec(text)?.[1];
|
|
631
636
|
}
|
|
637
|
+
function normalizePtyOutput(text) {
|
|
638
|
+
return text.replace(CONTROL_CHARS, "");
|
|
639
|
+
}
|
|
632
640
|
function buildHerdrTaskResult(paneOutput, options) {
|
|
633
641
|
const report = options?.report?.trim() ?? "";
|
|
634
642
|
if (report !== "") {
|
|
@@ -810,6 +818,7 @@ var AGENT_POLL_INTERVAL_MS, PANE_OUTPUT_LINES, CLOUD_SESSION_URL_RE, CLOUD_SESSI
|
|
|
810
818
|
var init_herdr_runner = __esm({
|
|
811
819
|
"src/herdr-runner.ts"() {
|
|
812
820
|
"use strict";
|
|
821
|
+
init_table();
|
|
813
822
|
init_transcript();
|
|
814
823
|
AGENT_POLL_INTERVAL_MS = 3 * 1e3;
|
|
815
824
|
PANE_OUTPUT_LINES = 300;
|
|
@@ -1888,9 +1897,9 @@ function checkCloudAuth(input) {
|
|
|
1888
1897
|
function checkCloudConfig(input) {
|
|
1889
1898
|
if (!input.cloud) return [];
|
|
1890
1899
|
const errors = [];
|
|
1891
|
-
if (input.
|
|
1900
|
+
if (input.scriptAvailable === false) {
|
|
1892
1901
|
errors.push(
|
|
1893
|
-
`--cloud requires
|
|
1902
|
+
`--cloud requires a pty, provided via the "script" command (creating a new cloud session requires a TTY, which the worker's spawn does not have on its own). "script" is unavailable \u2014 either the platform is not darwin/linux, or "script" is not on PATH. Drop the --cloud flag, or run on darwin/linux where "script" is available.`
|
|
1894
1903
|
);
|
|
1895
1904
|
}
|
|
1896
1905
|
if (input.auth !== void 0) errors.push(...checkCloudAuth(input.auth));
|
|
@@ -2521,15 +2530,26 @@ ${principles}`;
|
|
|
2521
2530
|
function shellQuote2(value) {
|
|
2522
2531
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2523
2532
|
}
|
|
2533
|
+
function buildScriptCommand(command, args, platform = process.platform) {
|
|
2534
|
+
if (platform === "darwin") {
|
|
2535
|
+
return { command: "script", args: ["-q", "/dev/null", command, ...args] };
|
|
2536
|
+
}
|
|
2537
|
+
if (platform === "linux") {
|
|
2538
|
+
return { command: "script", args: ["-qec", [command, ...args].map(shellQuote2).join(" "), "/dev/null"] };
|
|
2539
|
+
}
|
|
2540
|
+
throw new Error(`unsupported platform for script(1): ${platform}`);
|
|
2541
|
+
}
|
|
2524
2542
|
function buildClaudeExecution(invocation) {
|
|
2525
2543
|
return {
|
|
2526
2544
|
command: CLAUDE_COMMAND,
|
|
2527
2545
|
args: buildClaudeArgs(invocation),
|
|
2528
|
-
|
|
2546
|
+
// クラウド実行では `-p` を付けない代わりに `--cloud` の値(初期プロンプト)として
|
|
2547
|
+
// 渡すため、mode に関わらずプロンプトを返す。
|
|
2548
|
+
...invocation.mode === "herdr" || invocation.cloud === true ? { prompt: invocation.prompt } : {}
|
|
2529
2549
|
};
|
|
2530
2550
|
}
|
|
2531
2551
|
function buildClaudeEnv(mode, cloud) {
|
|
2532
|
-
const base = mode === "herdr" ? { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS } : { ...CLAUDE_SPAWN_ENV };
|
|
2552
|
+
const base = mode === "herdr" || cloud ? { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS } : { ...CLAUDE_SPAWN_ENV };
|
|
2533
2553
|
if (!cloud) return base;
|
|
2534
2554
|
return { ...base, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
2535
2555
|
}
|
|
@@ -2827,13 +2847,55 @@ async function flagOrphanedCloudSession(target, id, reason, cloudSessionId) {
|
|
|
2827
2847
|
console.error(`[worker] failed to comment on ${target} #${id} about the orphaned cloud session: ${err}`);
|
|
2828
2848
|
});
|
|
2829
2849
|
}
|
|
2850
|
+
async function createCloudSession(args, initialPrompt, cwd, env) {
|
|
2851
|
+
const { extractCloudSessionId: extractCloudSessionId2, normalizePtyOutput: normalizePtyOutput2 } = await Promise.resolve().then(() => (init_herdr_runner(), herdr_runner_exports));
|
|
2852
|
+
const spec = buildScriptCommand("claude", buildCloudCreateArgs(args, initialPrompt));
|
|
2853
|
+
const child = spawn(spec.command, spec.args, {
|
|
2854
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2855
|
+
...cwd ? { cwd } : {},
|
|
2856
|
+
...env ? { env: { ...process.env, ...env } } : {}
|
|
2857
|
+
});
|
|
2858
|
+
let output = "";
|
|
2859
|
+
return await new Promise((resolve3, reject) => {
|
|
2860
|
+
let settled = false;
|
|
2861
|
+
const finish = (err, sessionId) => {
|
|
2862
|
+
if (settled) return;
|
|
2863
|
+
settled = true;
|
|
2864
|
+
clearTimeout(timer);
|
|
2865
|
+
clearInterval(abortCheck);
|
|
2866
|
+
if (err) {
|
|
2867
|
+
child.kill();
|
|
2868
|
+
reject(err);
|
|
2869
|
+
} else {
|
|
2870
|
+
resolve3(sessionId);
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
const scan = () => {
|
|
2874
|
+
const sessionId = extractCloudSessionId2(normalizePtyOutput2(output));
|
|
2875
|
+
if (sessionId) finish(void 0, sessionId);
|
|
2876
|
+
};
|
|
2877
|
+
child.stdout?.on("data", (chunk) => {
|
|
2878
|
+
output += chunk.toString("utf-8");
|
|
2879
|
+
scan();
|
|
2880
|
+
});
|
|
2881
|
+
child.on("error", (err) => finish(new Error(`failed to spawn the cloud session command: ${err.message}`)));
|
|
2882
|
+
child.on("close", () => {
|
|
2883
|
+
scan();
|
|
2884
|
+
finish(new Error(`the cloud session command exited without a session id (output tail: ${output.slice(-1e3)})`));
|
|
2885
|
+
});
|
|
2886
|
+
const timer = setTimeout(
|
|
2887
|
+
() => finish(new Error(`timed out waiting for the cloud session id (output tail: ${output.slice(-1e3)})`)),
|
|
2888
|
+
CLOUD_SESSION_TIMEOUT_MS
|
|
2889
|
+
);
|
|
2890
|
+
const abortCheck = setInterval(() => {
|
|
2891
|
+
if (herdrAbortSignal.aborted) {
|
|
2892
|
+
finish(new Error("the worker is shutting down before the cloud session could be created"));
|
|
2893
|
+
}
|
|
2894
|
+
}, CLOUD_SESSION_POLL_INTERVAL_MS);
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2830
2897
|
async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget, model) {
|
|
2831
|
-
const herdrRunnerMod = await Promise.resolve().then(() => (init_herdr_runner(), herdr_runner_exports));
|
|
2832
|
-
const { taskTabLabel: taskTabLabel2, waitForPaneReady: waitForPaneReady3, extractCloudSessionId: extractCloudSessionId2 } = herdrRunnerMod;
|
|
2833
|
-
const herdrMod = await Promise.resolve().then(() => (init_herdr(), herdr_exports));
|
|
2834
|
-
const { tabCreate: tabCreate2, tabClose: tabClose2, paneSendText: paneSendText2, paneSendKeys: paneSendKeys2, paneRead: paneRead2, getCurrentWorkspaceId: getCurrentWorkspaceId2 } = herdrMod;
|
|
2835
2898
|
herdrTasks.set(id, { paneId: "", tabId: "" });
|
|
2836
|
-
const label = taskTabLabel2(resolveProjectName(), id);
|
|
2837
2899
|
const initialPrompt = buildCloudPrompt(
|
|
2838
2900
|
prompt,
|
|
2839
2901
|
model ?? "",
|
|
@@ -2842,39 +2904,7 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
2842
2904
|
let result;
|
|
2843
2905
|
let cloudSessionId;
|
|
2844
2906
|
try {
|
|
2845
|
-
|
|
2846
|
-
herdrTasks.set(id, created);
|
|
2847
|
-
try {
|
|
2848
|
-
const ready = await waitForPaneReady3(created.paneId, herdrMod);
|
|
2849
|
-
if (!ready) {
|
|
2850
|
-
console.warn(`[worker] pane ${created.paneId} produced no prompt before the timeout, launching anyway`);
|
|
2851
|
-
}
|
|
2852
|
-
const command = ["claude", ...buildCloudCreateArgs(args, initialPrompt)].map(shellQuote2).join(" ");
|
|
2853
|
-
await paneSendText2(created.paneId, command);
|
|
2854
|
-
await paneSendKeys2(created.paneId, "enter");
|
|
2855
|
-
const deadline = Date.now() + CLOUD_SESSION_TIMEOUT_MS;
|
|
2856
|
-
for (; ; ) {
|
|
2857
|
-
if (herdrAbortSignal.aborted) {
|
|
2858
|
-
throw new Error("the worker is shutting down before the cloud session could be created");
|
|
2859
|
-
}
|
|
2860
|
-
let content = "";
|
|
2861
|
-
try {
|
|
2862
|
-
content = await paneRead2(created.paneId);
|
|
2863
|
-
} catch (err) {
|
|
2864
|
-
console.error(`[worker] failed to read pane ${created.paneId} while waiting for the cloud session: ${err}`);
|
|
2865
|
-
}
|
|
2866
|
-
cloudSessionId = extractCloudSessionId2(content);
|
|
2867
|
-
if (cloudSessionId) break;
|
|
2868
|
-
if (Date.now() >= deadline) {
|
|
2869
|
-
throw new Error(`timed out waiting for the cloud session id (pane tail: ${content.slice(-1e3)})`);
|
|
2870
|
-
}
|
|
2871
|
-
await new Promise((resolve3) => setTimeout(resolve3, CLOUD_SESSION_POLL_INTERVAL_MS));
|
|
2872
|
-
}
|
|
2873
|
-
} finally {
|
|
2874
|
-
await tabClose2(created.tabId).catch((err) => {
|
|
2875
|
-
console.error(`[worker] failed to close cloud task tab ${created.tabId}: ${err}`);
|
|
2876
|
-
});
|
|
2877
|
-
}
|
|
2907
|
+
cloudSessionId = await createCloudSession(args, initialPrompt, cwd, env);
|
|
2878
2908
|
const createOutput = `[worker] created cloud session ${cloudSessionId} with the task's initial prompt`;
|
|
2879
2909
|
if (!cloudTarget) {
|
|
2880
2910
|
console.warn(`[worker] #${id} has no completion-detection target, treating session creation as completion`);
|
|
@@ -2922,7 +2952,8 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
2922
2952
|
console.error(`[worker] failed to run #${id} via cloud: ${err}`);
|
|
2923
2953
|
let orphanNote = "[worker] note: with the 1-command launch, the cloud session may already have started working independently even though this task is being reported as failed locally (orphaned session).";
|
|
2924
2954
|
if (cloudTarget) {
|
|
2925
|
-
|
|
2955
|
+
const reason = herdrAbortSignal.aborted ? "shutdown" : "session-id";
|
|
2956
|
+
await flagOrphanedCloudSession(cloudTarget, id, reason, cloudSessionId);
|
|
2926
2957
|
orphanNote += " added cc-need-human-check so a human can verify whether it completed on its own.";
|
|
2927
2958
|
}
|
|
2928
2959
|
result = {
|
|
@@ -2943,16 +2974,18 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
|
|
|
2943
2974
|
title,
|
|
2944
2975
|
status: "running",
|
|
2945
2976
|
workerName,
|
|
2946
|
-
path
|
|
2977
|
+
// クラウド実行は worktree を持たない(ワーカーが path に undefined を渡す)ため、
|
|
2978
|
+
// 空欄ではなく実行場所が分かる表示を入れる。
|
|
2979
|
+
path: cloud ? CLOUD_WORKTREE_LABEL : path2,
|
|
2947
2980
|
startedAt: /* @__PURE__ */ new Date()
|
|
2948
2981
|
});
|
|
2949
2982
|
ensureRenderInterval();
|
|
2950
2983
|
renderTable();
|
|
2984
|
+
if (cloud) {
|
|
2985
|
+
void runViaCloud(args, prompt ?? "", id, onComplete, cwd, env, cloudTarget, model);
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2951
2988
|
if (getRunMode() === "herdr") {
|
|
2952
|
-
if (cloud) {
|
|
2953
|
-
void runViaCloud(args, prompt ?? "", id, onComplete, cwd, env, cloudTarget, model);
|
|
2954
|
-
return;
|
|
2955
|
-
}
|
|
2956
2989
|
void runViaHerdr(args, prompt ?? "", id, onComplete, cwd, env);
|
|
2957
2990
|
return;
|
|
2958
2991
|
}
|
|
@@ -5417,13 +5450,27 @@ async function readCloudAuthStatus() {
|
|
|
5417
5450
|
return { kind: "unknown" };
|
|
5418
5451
|
}
|
|
5419
5452
|
}
|
|
5453
|
+
async function resolveScriptAvailable() {
|
|
5454
|
+
try {
|
|
5455
|
+
buildScriptCommand("true", []);
|
|
5456
|
+
} catch {
|
|
5457
|
+
return false;
|
|
5458
|
+
}
|
|
5459
|
+
try {
|
|
5460
|
+
await execFileAsync4("which", ["script"]);
|
|
5461
|
+
return true;
|
|
5462
|
+
} catch {
|
|
5463
|
+
return false;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5420
5466
|
async function assertCloudAvailable() {
|
|
5421
5467
|
const cloud = hasCloudFlag();
|
|
5422
5468
|
const labelReady = cloud ? await createLabel(CLOUD_DONE_LABEL, "33cfff", true) : true;
|
|
5423
5469
|
const status = cloud ? await readCloudAuthStatus() : void 0;
|
|
5470
|
+
const scriptAvailable = cloud ? await resolveScriptAvailable() : void 0;
|
|
5424
5471
|
const errors = checkCloudConfig({
|
|
5425
5472
|
cloud,
|
|
5426
|
-
|
|
5473
|
+
scriptAvailable,
|
|
5427
5474
|
auth: status ? { status, baseUrl: process.env.ANTHROPIC_BASE_URL } : void 0
|
|
5428
5475
|
});
|
|
5429
5476
|
if (!labelReady) {
|