claude-task-worker 0.116.0 → 0.118.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 +60 -9
- package/dist/chunk-3RY43CYR.js +41 -0
- package/dist/chunk-N42YU3GJ.js +184 -0
- package/dist/chunk-ULDG6SEE.js +3400 -0
- package/dist/claude-args.d.ts +62 -0
- package/dist/commands/cloud-setup.d.ts +14 -0
- package/dist/commands/codegraph.d.ts +39 -0
- package/dist/commands/design-md.d.ts +11 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/install.d.ts +14 -0
- package/dist/commands/pen.d.ts +15 -0
- package/dist/commands/playwright.d.ts +27 -0
- package/dist/commands/run-command.d.ts +10 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/version.d.ts +4 -0
- package/dist/config.d.ts +77 -0
- package/dist/custom-workers.d.ts +16 -0
- package/dist/dispatch-args.d.ts +13 -0
- package/dist/dispatcher-JKUMXTJ2.js +410 -0
- package/dist/dispatcher.d.ts +77 -0
- package/dist/gh.d.ts +112 -0
- package/dist/git.d.ts +7 -0
- package/dist/herdr-NDLUM6JF.js +309 -0
- package/dist/herdr-runner-33D6C2KI.js +307 -0
- package/dist/herdr-runner.d.ts +136 -0
- package/dist/herdr.d.ts +85 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +859 -4984
- package/dist/last-run-pr.d.ts +16 -0
- package/dist/lib.d.ts +20 -0
- package/dist/lib.js +123 -0
- package/dist/process-manager.d.ts +38 -0
- package/dist/random-name.d.ts +2 -0
- package/dist/run-command-O2TMRPZK.js +8 -0
- package/dist/runcat.d.ts +23 -0
- package/dist/slack.d.ts +46 -0
- package/dist/table-DWVMNDS3.js +32 -0
- package/dist/table.d.ts +55 -0
- package/dist/task-result.d.ts +25 -0
- package/dist/transcript.d.ts +27 -0
- package/dist/user-config.d.ts +30 -0
- package/dist/workers/answer-issue-questions.d.ts +1 -0
- package/dist/workers/apply-ui-design.d.ts +1 -0
- package/dist/workers/check-dependabot.d.ts +1 -0
- package/dist/workers/create-issue.d.ts +1 -0
- package/dist/workers/create-ui-design.d.ts +2 -0
- package/dist/workers/epic-issue.d.ts +2 -0
- package/dist/workers/exec-issue.d.ts +15 -0
- package/dist/workers/fix-review-point.d.ts +1 -0
- package/dist/workers/issue-worker.d.ts +18 -0
- package/dist/workers/pr-worker.d.ts +18 -0
- package/dist/workers/registry.d.ts +8 -0
- package/dist/workers/resolve-conflict.d.ts +2 -0
- package/dist/workers/scheduled-worker.d.ts +8 -0
- package/dist/workers/triage-created-issue.d.ts +1 -0
- package/dist/workers/triage-pr.d.ts +5 -0
- package/dist/workers/ui-design.d.ts +14 -0
- package/dist/workers/update-coding-guidelines.d.ts +1 -0
- package/dist/workers/update-design-md.d.ts +1 -0
- package/dist/workers/update-issue.d.ts +1 -0
- package/dist/workers/update-requirement-rules.d.ts +1 -0
- package/dist/workers/worker-definition.d.ts +18 -0
- package/dist/worktree.d.ts +21 -0
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ GitHub Issues/PRを定期ポーリングし、Claude Codeに処理を委譲す
|
|
|
4
4
|
|
|
5
5
|
同梱の Claude Code プラグイン(`plugin/`)と組み合わせることで、Issue の実装からPRのレビュー対応、Dependabot PR の対応までを自動化する。CLI 本体(npm パッケージ)とプラグイン(Claude Code マーケットプレイス)は同じリポジトリ・同じ名前で提供される。
|
|
6
6
|
|
|
7
|
+
使い方のマニュアル(ハンズオン形式の導入・カスタムワーカーの作り方・設定のパッケージ化など)は [`docs/`](./docs/README.md) にある。
|
|
8
|
+
|
|
7
9
|
## アーキテクチャ
|
|
8
10
|
|
|
9
11
|
CLI が GitHub ラベルを検知してタスクを起動し、プラグインのスキルが実際の処理を担う。
|
|
@@ -99,8 +101,11 @@ claude-task-worker update
|
|
|
99
101
|
```bash
|
|
100
102
|
claude-task-worker init # 既存ファイルは保護
|
|
101
103
|
claude-task-worker init --force # 強制上書き
|
|
104
|
+
claude-task-worker apply-labels # ラベルだけを作成(冪等)
|
|
102
105
|
```
|
|
103
106
|
|
|
107
|
+
`claude-task-worker.json` のトップレベル `labels`(文字列配列)にカスタムワーカー用のラベル名を書くと、`init` / `apply-labels` がプリセットのラベルに加えて作成する。色は名前から一意に決まり、プリセットと同名ならプリセットの色を使う。
|
|
108
|
+
|
|
104
109
|
作成されるラベル:
|
|
105
110
|
|
|
106
111
|
| ラベル | 用途 |
|
|
@@ -131,8 +136,10 @@ claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--
|
|
|
131
136
|
| `all` | 通常ワーカー9つ + 定期ワーカー3つ(`triage-created-issue` / `triage-pr` / `check-dependabot` を除く) |
|
|
132
137
|
| `yolo` | 全ワーカーを同時にポーリング |
|
|
133
138
|
| `init` | ラベル・テンプレート・設定ファイルの作成と CodeGraph セットアップ |
|
|
139
|
+
| `apply-labels` | プリセットと `labels` 宣言のラベルだけを作成 |
|
|
134
140
|
| `install` / `update` | 上記「セットアップ」を参照 |
|
|
135
141
|
| `cloud-setup [--force]` | クラウド VM 側の準備(下記「`--cloud`」を参照) |
|
|
142
|
+
| `list-workers` | プリセットとカスタムの全ワーカーを名前・種別・有効/無効・ロード元で表示(`workerFiles` のロード失敗時は exit 1) |
|
|
136
143
|
| `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack にも通知 |
|
|
137
144
|
| `version` | CLI のバージョンを表示(`--version` / `-v` も可) |
|
|
138
145
|
|
|
@@ -165,7 +172,7 @@ claude-task-worker exec-issue --project app-a --epic 100
|
|
|
165
172
|
|
|
166
173
|
プロジェクト名・グループ名は `config.json` で定義する(下記「設定ファイル」)。`all` は全プロジェクトを指す予約語。
|
|
167
174
|
|
|
168
|
-
`--project` と併用できないコマンド: `init` / `install` / `update` / `usage` / `version`
|
|
175
|
+
`--project` と併用できないコマンド: `init` / `apply-labels` / `install` / `update` / `cloud-setup` / `usage` / `version` / `list-workers`
|
|
169
176
|
|
|
170
177
|
### `--cloud`
|
|
171
178
|
|
|
@@ -176,7 +183,7 @@ claude-task-worker exec-issue --cloud
|
|
|
176
183
|
claude-task-worker all --cloud
|
|
177
184
|
```
|
|
178
185
|
|
|
179
|
-
|
|
186
|
+
`--cloud` を付けたプロセスが起動するワーカーはすべてクラウドで実行される。ワーカーごとの向き不向きは [`docs/cloud.md`](./docs/cloud.md) を参照。
|
|
180
187
|
|
|
181
188
|
前提条件:
|
|
182
189
|
|
|
@@ -205,10 +212,10 @@ npx claude-task-worker cloud-setup
|
|
|
205
212
|
- クラウド実行のタスクは worktree を作らない(VM が自前でリポジトリを持つため)
|
|
206
213
|
- 完了は `cc-cloud-done` ラベルで検知する。4時間で応答がなければ打ち切り、`cc-need-human-check` を付けて失敗通知する
|
|
207
214
|
- `--project` と併用した場合、`--cloud` は各プロジェクトへそのまま転送される
|
|
208
|
-
- `--cloud` と併用できないコマンド: `init` / `install` / `update` / `usage` / `version`
|
|
215
|
+
- `--cloud` と併用できないコマンド: `init` / `apply-labels` / `install` / `update` / `cloud-setup` / `usage` / `version` / `list-workers`
|
|
209
216
|
- `--cloud` は `mode`(`default` / `herdr`)に依存しない。クラウドセッションの作成は `script` コマンドの疑似 pty で完結し、herdr のペインを使わないため、どちらの `mode` でも同じ経路を通る
|
|
210
217
|
|
|
211
|
-
詳細は [`docs/
|
|
218
|
+
詳細は [`docs/cloud.md`](./docs/cloud.md) を参照。
|
|
212
219
|
|
|
213
220
|
### `--debug`
|
|
214
221
|
|
|
@@ -223,6 +230,25 @@ claude-task-worker exec-issue --cloud --debug
|
|
|
223
230
|
|
|
224
231
|
投稿の担当は実行形態で違う。ローカルはワーカーが報告そのもの(`claude -p` の stdout / transcript)を持っているのでワーカーが投稿し、クラウドはワーカーに報告が届かないのでセッション自身に投稿させる(ワーカーはそれを回収して Slack へ載せる)。定期ワーカー(`update-*`)の投稿先は実行記録PR(`ctw-last-run-<ワーカー名>`)。クラウド実行の完了検知(`cc-cloud-done`)はフラグの有無に関わらず従来どおり動く。
|
|
225
232
|
|
|
233
|
+
### `--inherit-config <path>`
|
|
234
|
+
|
|
235
|
+
指定した設定ファイルを土台として読み、その上に起動ディレクトリ(cwd)直下の `claude-task-worker.json` → `claude-task-worker.local.json` を重ねる(後が勝つ。マージ規則は下記「ローカル上書き」と同じ)。複数リポジトリで共通の設定を1ファイルにまとめ、リポジトリごとの差分だけを各リポジトリに置く用途。
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
claude-task-worker all --inherit-config ~/ctw/shared.json
|
|
239
|
+
claude-task-worker init --inherit-config ~/ctw/shared.json # 指定パスへ設定ファイルを生成する
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
- 未指定時は従来どおり cwd 直下の `claude-task-worker.json` / `claude-task-worker.local.json` だけを読む
|
|
243
|
+
- 値自体の相対パスは起動時の cwd 基準。ファイル内の相対パス(`workerFiles` / `uiDesign.designDir`)は**そのファイルの所在ディレクトリ基準**で解決する
|
|
244
|
+
- 配列(`labels` / `workerFiles` など)は置き換えではなく、cwd 側の値を土台の後ろへ追記する(重複は除く)。cwd の `claude-task-worker.json` と `claude-task-worker.local.json` 同士は従来どおり local が丸ごと置き換え、その結果を土台へ追記する
|
|
245
|
+
- `claude-task-worker.local.json` は常に cwd 直下のものを読む(指定ファイルと同じディレクトリのものは読まない)。cwd 直下の `claude-task-worker.json` は無くてもよい
|
|
246
|
+
- 指定ファイルが存在しなければエラー終了する(既定ファイルへは倒さない)。`--project` では絶対パスにして各プロジェクトへ転送する
|
|
247
|
+
- 定期ワーカーの `lastRun` は指定に関わらずリポジトリ直下の `claude-task-worker.json` で読み書きする。リポジトリ直下に同ファイルが無ければ `lastRun` だけを持つファイルが作られる(他のキーを持たないので土台の設定とは衝突しない)。`init` を `--inherit-config` 付きで実行した場合も、未記録のワーカーの `lastRun` はリポジトリ直下へ記録する(セットアップ直後に定期ワーカーが一斉に走らないようにするため)
|
|
248
|
+
- スキル(`create-ui-design` / `update-design-md` / `triage-created-issue`)はリポジトリ直下の `claude-task-worker.json` を直接読むため、**`uiDesign` の設定は `--inherit-config` のファイルに書いてもスキルには届かない**。`uiDesign` はリポジトリ直下に書くこと
|
|
249
|
+
|
|
250
|
+
設定・カスタムワーカー・スキルを1つのリポジトリにまとめて配る手順は [`docs/config-package.md`](./docs/config-package.md) を参照。
|
|
251
|
+
|
|
226
252
|
### Pen CLI のログイン
|
|
227
253
|
|
|
228
254
|
`.pen` を扱うスキルは Pen CLI の認証を必要とする。UIデザイン先行ワークフローを使うなら一度ログインしておく。
|
|
@@ -273,6 +299,8 @@ CI やクラウド VM など対話ログインできない環境では、環境
|
|
|
273
299
|
|---|---|---|---|
|
|
274
300
|
| `fixReviewPointCallbackCommentMessage` | string | - | `fix-review-point` 完了時に PR へ投稿するコメント(未設定なら投稿しない) |
|
|
275
301
|
| `remoteEnvId` | string \| null | `null` | クラウド実行(`--cloud`)時に `--environment` へ渡すクラウド環境ID。`null` なら渡さず claude 側の既定解決に任せる(下記) |
|
|
302
|
+
| `labels` | string[] | `[]` | カスタムワーカー用の GitHub ラベル名。`init` / `apply-labels` が作成する(ローカル上書きでは配列ごと置き換え) |
|
|
303
|
+
| `workerFiles` | string[] | `[]` | カスタムワーカーを定義した TS ファイルのパス(下記「カスタムワーカー」。ローカル上書きでは配列ごと置き換え) |
|
|
276
304
|
| `uiDesign` | object | `{ "enabled": false, "designDir": "designs", "yolo": false }` | UIデザイン先行ワークフロー(下記) |
|
|
277
305
|
| `workers` | object | `{}` | ワーカーごとの上書き設定(下記) |
|
|
278
306
|
| `lastRun` | object | `{}` | 定期ワーカーの最終実行時刻。ワーカーが自動更新するため手で編集しない |
|
|
@@ -300,21 +328,23 @@ CI やクラウド VM など対話ログインできない環境では、環境
|
|
|
300
328
|
| `skill` | Claude CLI の `-p` に渡すスラッシュコマンド(`"<skill> <番号>"` の形で起動) |
|
|
301
329
|
| `model` | `--model` の値(`sonnet` / `opus` / `haiku`) |
|
|
302
330
|
| `advisorModel` | `--advisor` の値。空文字なら advisor なし。`config.json` の `advisor: true` のときだけ参照される |
|
|
303
|
-
| `effort` | `--effort` の値(`high` / `medium` / `
|
|
331
|
+
| `effort` | `--effort` の値(`low` / `medium` / `high` / `xhigh` / `max`)。opus ワーカーの既定は `medium`(Opus 5.5 の既定。`medium` で Opus 5 の `high` 相当)。sonnet ワーカーは手順が確定したものが `medium`、コード変更の判断を伴うもの(`resolve-conflict` / `check-dependabot`)が `high`(Sonnet 5.5 ガイドの推奨) |
|
|
304
332
|
| `pollingIntervalSeconds` | ポーリング間隔(秒) |
|
|
305
333
|
| `cooldownSeconds` | タスク完了後にポーリングを止める時間(秒)。既定 `0` |
|
|
306
334
|
| `maxConcurrentTasks` | 同時実行できるタスクの最大数。既定 `1` |
|
|
335
|
+
| `enabled` | `false` で無効化する。`all` / `yolo` では起動せず除外したワーカー名を1行ログに出し、個別起動(`claude-task-worker <name>`)ではタスクを起動せず exit 1。既定 `true`(`claude-task-worker.local.json` でも上書きできる) |
|
|
307
336
|
|
|
308
337
|
既定値:
|
|
309
338
|
|
|
310
339
|
| ワーカー | `model` | `effort` | `pollingIntervalSeconds` |
|
|
311
340
|
|---|---|---|---|
|
|
312
|
-
| `exec-issue` / `fix-review-point` / `answer-issue-questions` / `create-issue` / `create-ui-design` / `triage-pr` | `opus` | `
|
|
313
|
-
| `update-issue` / `triage-created-issue`
|
|
341
|
+
| `exec-issue` / `fix-review-point` / `answer-issue-questions` / `create-issue` / `create-ui-design` / `triage-pr` | `opus` | `medium` | 60 |
|
|
342
|
+
| `update-issue` / `triage-created-issue` | `sonnet` | `medium` | 60 |
|
|
343
|
+
| `resolve-conflict` | `sonnet` | `high` | 60 |
|
|
314
344
|
| `epic-issue` / `apply-ui-design` | `sonnet` | `medium` | 300 |
|
|
315
345
|
| `check-dependabot` | `sonnet` | `high` | 3600 |
|
|
316
|
-
| `update-coding-guidelines` / `update-requirement-rules` / `update-design-md` | `opus` | `
|
|
317
|
-
| (未知のワーカー名) | `opus` | `
|
|
346
|
+
| `update-coding-guidelines` / `update-requirement-rules` / `update-design-md` | `opus` | `medium` | 3600 |
|
|
347
|
+
| (未知のワーカー名) | `opus` | `medium` | 60 |
|
|
318
348
|
|
|
319
349
|
`advisorModel` の既定は全ワーカー空文字(advisor なし)。
|
|
320
350
|
|
|
@@ -330,6 +360,27 @@ CI やクラウド VM など対話ログインできない環境では、環境
|
|
|
330
360
|
}
|
|
331
361
|
```
|
|
332
362
|
|
|
363
|
+
### カスタムワーカー
|
|
364
|
+
|
|
365
|
+
`workerFiles` に TS ファイルを列挙すると、起動時にロードしてプリセットと同じ扱いで起動できる(個別コマンド・`all`・`yolo`。`enabled` などの `workers.<name>` 設定も効く)。`--project` 指定時は読み込まない。
|
|
366
|
+
|
|
367
|
+
- パスは絶対パス、`~` / `~/...`(home 展開)、または設定ファイルのあるディレクトリからの相対パス
|
|
368
|
+
- TS は型を除去して読み込む(Node 22.13 以上。tsconfig・ビルド不要)。ファイル内の相対 import は拡張子付き(`./helper.ts`)で書く
|
|
369
|
+
- `claude-task-worker/lib` は実行中の CLI 自身の `lib` に解決される
|
|
370
|
+
- 1ファイルから複数の定義を export でき、`defineWorker` / `createIssuePollingWorker` / `createPrPollingWorker` / `createScheduledWorker` の戻り値だけが対象(それ以外の export は無視)
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
import { createIssuePollingWorker } from "claude-task-worker/lib";
|
|
374
|
+
|
|
375
|
+
export const myWorker = createIssuePollingWorker({
|
|
376
|
+
name: "my-worker",
|
|
377
|
+
command: "my-skill",
|
|
378
|
+
triggerLabels: ["cc-my-worker"],
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
起動時に全件を検証し、次のいずれかがあればタスクを1件も起動せず exit 1 する(メッセージにファイルパスと直し方を含む): ファイルが存在しない/読み込み・トランスパイルに失敗/定義が1つも export されていない/`name` がプリセットと衝突/`name` がカスタム同士で衝突。`claude-task-worker list-workers` でロード結果を確認できる。作り方の詳しい手順は [`docs/custom-worker.md`](./docs/custom-worker.md) を参照。
|
|
383
|
+
|
|
333
384
|
## ワークフロー
|
|
334
385
|
|
|
335
386
|
### Epic(親Issue)連携
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/commands/run-command.ts
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
function npmInstallGlobalLatest(pkg) {
|
|
4
|
+
return runCommand("npm", ["install", "-g", `${pkg}@latest`, "--prefer-online"]);
|
|
5
|
+
}
|
|
6
|
+
function runCommand(command, args) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const child = spawn(command, args, {
|
|
9
|
+
stdio: "inherit",
|
|
10
|
+
shell: process.platform === "win32"
|
|
11
|
+
});
|
|
12
|
+
const forwardSignal = (signal) => {
|
|
13
|
+
child.kill(signal);
|
|
14
|
+
};
|
|
15
|
+
const onSigint = () => forwardSignal("SIGINT");
|
|
16
|
+
const onSigterm = () => forwardSignal("SIGTERM");
|
|
17
|
+
const cleanup = () => {
|
|
18
|
+
process.removeListener("SIGINT", onSigint);
|
|
19
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
20
|
+
};
|
|
21
|
+
process.once("SIGINT", onSigint);
|
|
22
|
+
process.once("SIGTERM", onSigterm);
|
|
23
|
+
child.on("error", (err) => {
|
|
24
|
+
cleanup();
|
|
25
|
+
reject(err);
|
|
26
|
+
});
|
|
27
|
+
child.on("close", (code) => {
|
|
28
|
+
cleanup();
|
|
29
|
+
if (code === 0) {
|
|
30
|
+
resolve();
|
|
31
|
+
} else {
|
|
32
|
+
reject(new Error(`${command} ${args.join(" ")} exited with code ${code}`));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
npmInstallGlobalLatest,
|
|
40
|
+
runCommand
|
|
41
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// src/table.ts
|
|
2
|
+
import { format } from "node:util";
|
|
3
|
+
function getDisplayWidth(str) {
|
|
4
|
+
let width = 0;
|
|
5
|
+
for (const char of str.replace(CONTROL_CHARS, "")) {
|
|
6
|
+
const code = char.codePointAt(0);
|
|
7
|
+
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) {
|
|
8
|
+
width += 2;
|
|
9
|
+
} else {
|
|
10
|
+
width += 1;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return width;
|
|
14
|
+
}
|
|
15
|
+
function truncateToWidth(str, maxWidth) {
|
|
16
|
+
let width = 0;
|
|
17
|
+
let i = 0;
|
|
18
|
+
const chars = [...str];
|
|
19
|
+
while (i < chars.length) {
|
|
20
|
+
const charWidth = getDisplayWidth(chars[i]);
|
|
21
|
+
if (width + charWidth > maxWidth - 3) {
|
|
22
|
+
return chars.slice(0, i).join("") + "...";
|
|
23
|
+
}
|
|
24
|
+
width += charWidth;
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
return str;
|
|
28
|
+
}
|
|
29
|
+
function padToWidth(str, targetWidth) {
|
|
30
|
+
const currentWidth = getDisplayWidth(str);
|
|
31
|
+
const padding = targetWidth - currentWidth;
|
|
32
|
+
return padding > 0 ? str + " ".repeat(padding) : str;
|
|
33
|
+
}
|
|
34
|
+
var CLOUD_WORKTREE_LABEL = "\x1B[36m\u2601 running in cloud\x1B[0m";
|
|
35
|
+
function formatDuration(start, end = /* @__PURE__ */ new Date()) {
|
|
36
|
+
const diffMs = end.getTime() - start.getTime();
|
|
37
|
+
const totalSeconds = Math.floor(diffMs / 1e3);
|
|
38
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
39
|
+
const seconds = totalSeconds % 60;
|
|
40
|
+
return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
|
|
41
|
+
}
|
|
42
|
+
function formatTime(date) {
|
|
43
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
44
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
45
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
46
|
+
return `${h}:${m}:${s}`;
|
|
47
|
+
}
|
|
48
|
+
function renderBoxTable(headers, groups) {
|
|
49
|
+
const allRows = groups.flat();
|
|
50
|
+
const widths = headers.map(
|
|
51
|
+
(h, i) => Math.max(1, getDisplayWidth(h), ...allRows.map((r) => getDisplayWidth(r[i] ?? "")))
|
|
52
|
+
);
|
|
53
|
+
const border = (l, m, r) => `${l}${widths.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}`;
|
|
54
|
+
const row = (cells) => `\u2502 ${cells.map((c, i) => padToWidth(c ?? "", widths[i])).join(" \u2502 ")} \u2502`;
|
|
55
|
+
const lines = [];
|
|
56
|
+
lines.push(border("\u250C", "\u252C", "\u2510"));
|
|
57
|
+
lines.push(row(headers));
|
|
58
|
+
lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
59
|
+
const nonEmpty = groups.filter((g) => g.length > 0);
|
|
60
|
+
nonEmpty.forEach((group, gi) => {
|
|
61
|
+
if (gi > 0) lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
62
|
+
for (const r of group) lines.push(row(r));
|
|
63
|
+
});
|
|
64
|
+
lines.push(border("\u2514", "\u2534", "\u2518"));
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
var TASK_DISPLAY_LIMIT = 20;
|
|
68
|
+
var LOG_DISPLAY_LIMIT = 20;
|
|
69
|
+
function taskRecency(t) {
|
|
70
|
+
return (t.finishedAt ?? t.startedAt).getTime();
|
|
71
|
+
}
|
|
72
|
+
function selectRecentTasks(entries, limit = TASK_DISPLAY_LIMIT) {
|
|
73
|
+
const byId = /* @__PURE__ */ new Map();
|
|
74
|
+
for (const e of entries) byId.set(e.id, e);
|
|
75
|
+
const unique = [...byId.values()];
|
|
76
|
+
const running = unique.filter((t) => t.status === "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
77
|
+
const finished = unique.filter((t) => t.status !== "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
78
|
+
return [...running, ...finished].slice(0, limit);
|
|
79
|
+
}
|
|
80
|
+
function buildTaskTableLines(entries, now = /* @__PURE__ */ new Date()) {
|
|
81
|
+
if (entries.length === 0) return [];
|
|
82
|
+
const runningTasks = entries.filter((t) => t.status === "running");
|
|
83
|
+
const finishedTasks = entries.filter((t) => t.status !== "running");
|
|
84
|
+
const maxTitleWidth = 40;
|
|
85
|
+
const maxPathWidth = 40;
|
|
86
|
+
const truncTitle = (s) => getDisplayWidth(s) > maxTitleWidth ? truncateToWidth(s, maxTitleWidth) : s;
|
|
87
|
+
const truncPath = (p) => p ? getDisplayWidth(p) > maxPathWidth ? truncateToWidth(p, maxPathWidth) : p : "";
|
|
88
|
+
const hasPath = entries.some((t) => truncPath(t.path) !== "");
|
|
89
|
+
const runningRows = runningTasks.map(
|
|
90
|
+
(t) => taskRow(
|
|
91
|
+
t,
|
|
92
|
+
truncTitle(t.title),
|
|
93
|
+
truncPath(t.path),
|
|
94
|
+
hasPath,
|
|
95
|
+
t.agentStatus ? `${t.status}:${t.agentStatus}` : t.status,
|
|
96
|
+
formatTime(t.startedAt),
|
|
97
|
+
formatDuration(t.startedAt, now)
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
const finishedRows = finishedTasks.map(
|
|
101
|
+
(t) => taskRow(
|
|
102
|
+
t,
|
|
103
|
+
truncTitle(t.title),
|
|
104
|
+
truncPath(t.path),
|
|
105
|
+
hasPath,
|
|
106
|
+
t.status,
|
|
107
|
+
formatTime(t.finishedAt ?? t.startedAt),
|
|
108
|
+
formatDuration(t.startedAt, t.finishedAt ?? now)
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
const headers = hasPath ? ["#", "Title", "Worker", "Worktree", "Status", "Time", "Duration"] : ["#", "Title", "Worker", "Status", "Time", "Duration"];
|
|
112
|
+
return renderBoxTable(headers, [runningRows, finishedRows]);
|
|
113
|
+
}
|
|
114
|
+
function taskRow(t, title, path, hasPath, status, time, duration) {
|
|
115
|
+
return hasPath ? [`#${t.id}`, title, t.workerName, path, status, time, duration] : [`#${t.id}`, title, t.workerName, status, time, duration];
|
|
116
|
+
}
|
|
117
|
+
var logLines = [];
|
|
118
|
+
function pushLogLine(entry) {
|
|
119
|
+
if (entry.text.trim().length === 0) return;
|
|
120
|
+
logLines.push(entry);
|
|
121
|
+
if (logLines.length > LOG_DISPLAY_LIMIT) {
|
|
122
|
+
logLines.splice(0, logLines.length - LOG_DISPLAY_LIMIT);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
var rawLog = console.log.bind(console);
|
|
126
|
+
var rawClear = console.clear.bind(console);
|
|
127
|
+
function writeScreen(lines) {
|
|
128
|
+
rawClear();
|
|
129
|
+
rawLog(lines.join("\n"));
|
|
130
|
+
}
|
|
131
|
+
var consoleCaptured = false;
|
|
132
|
+
function captureConsole() {
|
|
133
|
+
if (consoleCaptured) return;
|
|
134
|
+
consoleCaptured = true;
|
|
135
|
+
process.on("exit", () => {
|
|
136
|
+
if (logLines.length > 0) rawLog(buildLogTableLines(logLines).join("\n"));
|
|
137
|
+
});
|
|
138
|
+
const streams = { log: "stdout", info: "stdout", warn: "stderr", error: "stderr" };
|
|
139
|
+
for (const [method, stream] of Object.entries(streams)) {
|
|
140
|
+
console[method] = (...args) => {
|
|
141
|
+
for (const line of format(...args).split("\n")) {
|
|
142
|
+
pushLogLine({ stream, text: line, time: /* @__PURE__ */ new Date() });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
var CONTROL_CHARS = (
|
|
148
|
+
// eslint-disable-next-line no-control-regex -- ANSI/制御文字を意図的に対象にする
|
|
149
|
+
/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/g
|
|
150
|
+
);
|
|
151
|
+
function sanitizeLogText(text) {
|
|
152
|
+
return text.replace(CONTROL_CHARS, " ").replace(/\t/g, " ");
|
|
153
|
+
}
|
|
154
|
+
function buildLogTableLines(entries) {
|
|
155
|
+
if (entries.length === 0) return [];
|
|
156
|
+
const maxTextWidth = 100;
|
|
157
|
+
const rows = [...entries].reverse().map((e) => {
|
|
158
|
+
const text = sanitizeLogText(e.text);
|
|
159
|
+
return [
|
|
160
|
+
formatTime(e.time),
|
|
161
|
+
e.id === void 0 ? "-" : `#${e.id}`,
|
|
162
|
+
e.stream,
|
|
163
|
+
getDisplayWidth(text) > maxTextWidth ? truncateToWidth(text, maxTextWidth) : text
|
|
164
|
+
];
|
|
165
|
+
});
|
|
166
|
+
return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
getDisplayWidth,
|
|
171
|
+
truncateToWidth,
|
|
172
|
+
padToWidth,
|
|
173
|
+
CLOUD_WORKTREE_LABEL,
|
|
174
|
+
TASK_DISPLAY_LIMIT,
|
|
175
|
+
LOG_DISPLAY_LIMIT,
|
|
176
|
+
selectRecentTasks,
|
|
177
|
+
buildTaskTableLines,
|
|
178
|
+
logLines,
|
|
179
|
+
pushLogLine,
|
|
180
|
+
writeScreen,
|
|
181
|
+
captureConsole,
|
|
182
|
+
CONTROL_CHARS,
|
|
183
|
+
buildLogTableLines
|
|
184
|
+
};
|