claude-task-worker 0.39.2 → 0.41.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 +40 -20
- package/dist/index.js +201 -67
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -335,6 +335,7 @@ claude-task-worker yolo --epic 100 --epic 200 --label priority-high
|
|
|
335
335
|
```json
|
|
336
336
|
{
|
|
337
337
|
"mode": "default",
|
|
338
|
+
"advisor": false,
|
|
338
339
|
"projects": {
|
|
339
340
|
"app-a": "/Users/me/repos/app-a",
|
|
340
341
|
"app-b": "/Users/me/repos/app-b",
|
|
@@ -346,7 +347,7 @@ claude-task-worker yolo --epic 100 --epic 200 --label priority-high
|
|
|
346
347
|
}
|
|
347
348
|
```
|
|
348
349
|
|
|
349
|
-
`mode` については [`mode`(タスクの実行形態)](#modeタスクの実行形態) を参照。
|
|
350
|
+
`mode` については [`mode`(タスクの実行形態)](#modeタスクの実行形態)、`advisor` については [`advisor`(アドバイザーモデル)](#advisorアドバイザーモデル) を参照。
|
|
350
351
|
|
|
351
352
|
`--project` は繰り返し指定可能で、複数指定した場合は解決後のプロジェクト集合の和集合が対象になる(重複は一意化される)。`--epic` / `--label` と併用でき、ディスパッチ先の各プロジェクトで実行されるコマンドにそのまま引き継がれる。
|
|
352
353
|
|
|
@@ -405,6 +406,24 @@ claude-task-worker exec-issue --project app-a --epic 100 --label priority-high
|
|
|
405
406
|
|
|
406
407
|
適用は `herdr server reload-config`。この設定は herdr サーバー全体に効くため、ワーカー以外の対話セッションの完了音も鳴らなくなる(`[ui.sound.agents] claude = "off"` でも実質同じ範囲)。ワーカーだけを無音にしたい場合は、`HERDR_DISABLE_SOUND=1 herdr --session <name>` で別セッションを起動し、その中でディスパッチャーを動かす
|
|
407
408
|
|
|
409
|
+
### `advisor`(アドバイザーモデル)
|
|
410
|
+
|
|
411
|
+
`config.json` のトップレベルに `advisor: true` を書くと、ワーカーがタスクを起動する際、Claude CLI へ `--advisor <model>` を渡すようになる。渡すモデルはリポジトリ直下の `claude-task-worker.json` の `workers.<ワーカー名>.advisorModel`([ワーカーごとの設定](#ワーカーごとの設定))で指定する。`mode` と同じくトップレベル一括で、プロジェクト単位・ワーカー単位のオン/オフはできない。
|
|
412
|
+
|
|
413
|
+
| `advisor` | 挙動 |
|
|
414
|
+
|---|---|
|
|
415
|
+
| `false`(既定) | `advisorModel` の指定に関わらず `--advisor` を渡さない |
|
|
416
|
+
| `true` | 各ワーカーの `advisorModel`(未指定時はワーカー既定値)を `--advisor` に渡す。空文字のワーカーには渡さない |
|
|
417
|
+
|
|
418
|
+
```json
|
|
419
|
+
{
|
|
420
|
+
"advisor": true,
|
|
421
|
+
"projects": { "app-a": "/Users/me/repos/app-a" }
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
advisor は main モデル以上の能力を持つモデルである必要がある(Claude CLI 側の制約)。そのため `advisorModel` の既定値は、`model` が `sonnet` のワーカーは `opus`、`model` が `opus` のワーカー(`answer-issue-questions`)は空文字(=advisor なし)にしてある。
|
|
426
|
+
|
|
408
427
|
### exec-issue
|
|
409
428
|
|
|
410
429
|
`cc-exec-issue` ラベルが付いた自分にアサインされたIssueを定期取得し、Claude Codeで処理を実行する。(デフォルト1分間隔)
|
|
@@ -562,26 +581,26 @@ claude-task-worker -v
|
|
|
562
581
|
|---|---|---|---|
|
|
563
582
|
| `fixReviewPointCallbackCommentMessage` | string | - | fix-review-point 完了時にPRへ投稿するコメント(未設定の場合は投稿しない) |
|
|
564
583
|
| `uiDesign` | object | `{ "enabled": false, "designDir": "designs" }` | UIデザイン先行ワークフローの設定(詳細は「[UIデザイン先行ワークフロー](#uiデザイン先行ワークフロー)」) |
|
|
565
|
-
| `workers` | object | `{}` | ワーカーごとに Claude CLI に渡すスキル、`--model` / `--effort`、ポーリング間隔、クールダウン時間、最大同時実行数を上書きする設定(詳細は下記) |
|
|
584
|
+
| `workers` | object | `{}` | ワーカーごとに Claude CLI に渡すスキル、`--model` / `--advisor` / `--effort`、ポーリング間隔、クールダウン時間、最大同時実行数を上書きする設定(詳細は下記) |
|
|
566
585
|
|
|
567
586
|
### ワーカーごとの設定
|
|
568
587
|
|
|
569
|
-
`workers` キーにワーカー名ごとの設定オブジェクトを指定することで、Claude CLI の `-p` に渡すスキル(スラッシュコマンド)、`--model` / `--effort`、ポーリング間隔、タスク完了後のクールダウン時間、最大同時実行数を個別に上書きできる。未指定のワーカー・フィールドは下記のワーカー別デフォルト値が使用される。
|
|
570
|
-
|
|
571
|
-
| ワーカー名 | デフォルト `skill` | デフォルト `model` | デフォルト `effort` | デフォルト `pollingIntervalSeconds` | デフォルト `cooldownSeconds` | デフォルト `maxConcurrentTasks` |
|
|
572
|
-
|
|
573
|
-
| `answer-issue-questions` | `/claude-task-worker:answer-issue-questions` | `opus` | `xhigh` | 60 | 0 | 1 |
|
|
574
|
-
| `create-issue` | `/claude-task-worker:create-issue-from-issue-number` | `sonnet` | `xhigh` | 60 | 0 | 1 |
|
|
575
|
-
| `update-issue` | `/claude-task-worker:update-issue` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
576
|
-
| `exec-issue` | `/claude-task-worker:exec-issue` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
577
|
-
| `fix-review-point` | `/claude-task-worker:fix-review-point` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
578
|
-
| `triage-created-issue` | `/claude-task-worker:triage-created-issue` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
579
|
-
| `triage-pr` | `/claude-task-worker:triage-pr` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
580
|
-
| `resolve-conflict` | `/claude-task-worker:resolve-pr-conflict` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
581
|
-
| `check-dependabot` | `/claude-task-worker:check-dependabot` | `sonnet` | `high` | 3600 | 0 | 1 |
|
|
582
|
-
| `epic-issue` | `/claude-task-worker:create-epic-pr` | `sonnet` | `high` | 300 | 0 | 1 |
|
|
583
|
-
| `create-ui-design` | `/claude-task-worker:create-ui-design` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
584
|
-
| `apply-ui-design` | `/claude-task-worker:apply-ui-design` | `sonnet` | `high` | 60 | 0 | 1 |
|
|
588
|
+
`workers` キーにワーカー名ごとの設定オブジェクトを指定することで、Claude CLI の `-p` に渡すスキル(スラッシュコマンド)、`--model` / `--advisor` / `--effort`、ポーリング間隔、タスク完了後のクールダウン時間、最大同時実行数を個別に上書きできる。未指定のワーカー・フィールドは下記のワーカー別デフォルト値が使用される。
|
|
589
|
+
|
|
590
|
+
| ワーカー名 | デフォルト `skill` | デフォルト `model` | デフォルト `advisorModel` | デフォルト `effort` | デフォルト `pollingIntervalSeconds` | デフォルト `cooldownSeconds` | デフォルト `maxConcurrentTasks` |
|
|
591
|
+
|---|---|---|---|---|---|---|---|
|
|
592
|
+
| `answer-issue-questions` | `/claude-task-worker:answer-issue-questions` | `opus` | (なし) | `xhigh` | 60 | 0 | 1 |
|
|
593
|
+
| `create-issue` | `/claude-task-worker:create-issue-from-issue-number` | `sonnet` | `opus` | `xhigh` | 60 | 0 | 1 |
|
|
594
|
+
| `update-issue` | `/claude-task-worker:update-issue` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
595
|
+
| `exec-issue` | `/claude-task-worker:exec-issue` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
596
|
+
| `fix-review-point` | `/claude-task-worker:fix-review-point` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
597
|
+
| `triage-created-issue` | `/claude-task-worker:triage-created-issue` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
598
|
+
| `triage-pr` | `/claude-task-worker:triage-pr` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
599
|
+
| `resolve-conflict` | `/claude-task-worker:resolve-pr-conflict` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
600
|
+
| `check-dependabot` | `/claude-task-worker:check-dependabot` | `sonnet` | `opus` | `high` | 3600 | 0 | 1 |
|
|
601
|
+
| `epic-issue` | `/claude-task-worker:create-epic-pr` | `sonnet` | `opus` | `high` | 300 | 0 | 1 |
|
|
602
|
+
| `create-ui-design` | `/claude-task-worker:create-ui-design` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
603
|
+
| `apply-ui-design` | `/claude-task-worker:apply-ui-design` | `sonnet` | `opus` | `high` | 60 | 0 | 1 |
|
|
585
604
|
|
|
586
605
|
各フィールドの値:
|
|
587
606
|
|
|
@@ -589,6 +608,7 @@ claude-task-worker -v
|
|
|
589
608
|
|---|---|---|
|
|
590
609
|
| `skill` | string | Claude CLI の `-p` に渡すスラッシュコマンド(例: `/claude-task-worker:exec-issue`, `/my-plugin:my-skill`)。ワーカーは `"<skill> <issue-or-pr-number>"` の形で Claude を起動する |
|
|
591
610
|
| `model` | string | Claude CLI の `--model` に渡す値(例: `sonnet`, `opus`, `haiku`) |
|
|
611
|
+
| `advisorModel` | string | Claude CLI の `--advisor` に渡す値(例: `opus`)。空文字を指定するとそのワーカーでは advisor を使わない。`config.json` の `advisor` が `true` のときだけ参照される(詳細は「[`advisor`(アドバイザーモデル)](#advisorアドバイザーモデル)」) |
|
|
592
612
|
| `effort` | string | Claude CLI の `--effort` に渡す値(例: `high`, `medium`, `low`) |
|
|
593
613
|
| `pollingIntervalSeconds` | number | GitHub をポーリングする間隔(秒)。正の数を指定する |
|
|
594
614
|
| `cooldownSeconds` | number | タスク完了後に次のポーリングを停止する時間(秒)。`0` でクールダウンなし |
|
|
@@ -599,8 +619,8 @@ claude-task-worker -v
|
|
|
599
619
|
```json
|
|
600
620
|
{
|
|
601
621
|
"workers": {
|
|
602
|
-
"exec-issue": { "skill": "/my-plugin:exec-issue", "model": "opus", "effort": "high", "pollingIntervalSeconds": 60, "cooldownSeconds": 600, "maxConcurrentTasks": 3 },
|
|
603
|
-
"fix-review-point": { "skill": "/claude-task-worker:fix-review-point", "model": "sonnet", "effort": "high", "maxConcurrentTasks": 2 },
|
|
622
|
+
"exec-issue": { "skill": "/my-plugin:exec-issue", "model": "opus", "advisorModel": "", "effort": "high", "pollingIntervalSeconds": 60, "cooldownSeconds": 600, "maxConcurrentTasks": 3 },
|
|
623
|
+
"fix-review-point": { "skill": "/claude-task-worker:fix-review-point", "model": "sonnet", "advisorModel": "opus", "effort": "high", "maxConcurrentTasks": 2 },
|
|
604
624
|
"triage-pr": { "effort": "medium", "pollingIntervalSeconds": 120 },
|
|
605
625
|
"check-dependabot": { "model": "haiku", "pollingIntervalSeconds": 7200 }
|
|
606
626
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,13 @@ var __export = (target, all) => {
|
|
|
12
12
|
// src/table.ts
|
|
13
13
|
var table_exports = {};
|
|
14
14
|
__export(table_exports, {
|
|
15
|
+
LOG_DISPLAY_LIMIT: () => LOG_DISPLAY_LIMIT,
|
|
16
|
+
TASK_DISPLAY_LIMIT: () => TASK_DISPLAY_LIMIT,
|
|
17
|
+
buildLogTableLines: () => buildLogTableLines,
|
|
15
18
|
buildTaskTableLines: () => buildTaskTableLines,
|
|
16
19
|
getDisplayWidth: () => getDisplayWidth,
|
|
17
20
|
padToWidth: () => padToWidth,
|
|
21
|
+
selectRecentTasks: () => selectRecentTasks,
|
|
18
22
|
truncateToWidth: () => truncateToWidth
|
|
19
23
|
});
|
|
20
24
|
function getDisplayWidth(str) {
|
|
@@ -61,74 +65,97 @@ function formatTime(date) {
|
|
|
61
65
|
const s = String(date.getSeconds()).padStart(2, "0");
|
|
62
66
|
return `${h}:${m}:${s}`;
|
|
63
67
|
}
|
|
68
|
+
function renderBoxTable(headers, groups) {
|
|
69
|
+
const allRows = groups.flat();
|
|
70
|
+
const widths = headers.map(
|
|
71
|
+
(h, i) => Math.max(1, getDisplayWidth(h), ...allRows.map((r) => getDisplayWidth(r[i] ?? "")))
|
|
72
|
+
);
|
|
73
|
+
const border = (l, m, r) => `${l}${widths.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}`;
|
|
74
|
+
const row = (cells) => `\u2502 ${cells.map((c, i) => padToWidth(c ?? "", widths[i])).join(" \u2502 ")} \u2502`;
|
|
75
|
+
const lines = [];
|
|
76
|
+
lines.push(border("\u250C", "\u252C", "\u2510"));
|
|
77
|
+
lines.push(row(headers));
|
|
78
|
+
lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
79
|
+
const nonEmpty = groups.filter((g) => g.length > 0);
|
|
80
|
+
nonEmpty.forEach((group, gi) => {
|
|
81
|
+
if (gi > 0) lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
82
|
+
for (const r of group) lines.push(row(r));
|
|
83
|
+
});
|
|
84
|
+
lines.push(border("\u2514", "\u2534", "\u2518"));
|
|
85
|
+
return lines;
|
|
86
|
+
}
|
|
87
|
+
function taskRecency(t) {
|
|
88
|
+
return (t.finishedAt ?? t.startedAt).getTime();
|
|
89
|
+
}
|
|
90
|
+
function selectRecentTasks(entries, limit = TASK_DISPLAY_LIMIT) {
|
|
91
|
+
const byId = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const e of entries) byId.set(e.id, e);
|
|
93
|
+
const unique = [...byId.values()];
|
|
94
|
+
const running2 = unique.filter((t) => t.status === "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
95
|
+
const finished = unique.filter((t) => t.status !== "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
96
|
+
return [...running2, ...finished].slice(0, limit);
|
|
97
|
+
}
|
|
64
98
|
function buildTaskTableLines(entries, now = /* @__PURE__ */ new Date()) {
|
|
65
99
|
if (entries.length === 0) return [];
|
|
66
100
|
const runningTasks = entries.filter((t) => t.status === "running");
|
|
67
101
|
const finishedTasks = entries.filter((t) => t.status !== "running");
|
|
68
102
|
const maxTitleWidth = 40;
|
|
69
103
|
const maxPathWidth = 40;
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
};
|
|
101
|
-
const pad = (s, w, useDisplayWidth = false) => useDisplayWidth ? padToWidth(s, w) : s + " ".repeat(w - s.length);
|
|
102
|
-
const cols = hasPath ? [
|
|
103
|
-
colWidths.id,
|
|
104
|
-
colWidths.title,
|
|
105
|
-
colWidths.worker,
|
|
106
|
-
colWidths.path,
|
|
107
|
-
colWidths.status,
|
|
108
|
-
colWidths.time,
|
|
109
|
-
colWidths.duration
|
|
110
|
-
] : [colWidths.id, colWidths.title, colWidths.worker, colWidths.status, colWidths.time, colWidths.duration];
|
|
111
|
-
const line = (l, m, r, f) => `${l}${cols.map((w) => f.repeat(w + 2)).join(m)}${r}`;
|
|
112
|
-
const row = (id, title, worker, path2, status, time, duration) => hasPath ? `\u2502 ${pad(id, colWidths.id)} \u2502 ${pad(title, colWidths.title, true)} \u2502 ${pad(worker, colWidths.worker)} \u2502 ${pad(path2, colWidths.path)} \u2502 ${pad(status, colWidths.status)} \u2502 ${pad(time, colWidths.time)} \u2502 ${pad(duration, colWidths.duration)} \u2502` : `\u2502 ${pad(id, colWidths.id)} \u2502 ${pad(title, colWidths.title, true)} \u2502 ${pad(worker, colWidths.worker)} \u2502 ${pad(status, colWidths.status)} \u2502 ${pad(time, colWidths.time)} \u2502 ${pad(duration, colWidths.duration)} \u2502`;
|
|
113
|
-
const lines = [];
|
|
114
|
-
lines.push(line("\u250C", "\u252C", "\u2510", "\u2500"));
|
|
115
|
-
lines.push(row("#", "Title", "Worker", "Worktree", "Status", "Time", "Duration"));
|
|
116
|
-
lines.push(line("\u251C", "\u253C", "\u2524", "\u2500"));
|
|
117
|
-
for (const r of runningRows) {
|
|
118
|
-
lines.push(row(r.id, r.title, r.worker, r.path, r.status, r.time, r.duration));
|
|
119
|
-
}
|
|
120
|
-
if (runningRows.length > 0 && finishedRows.length > 0) {
|
|
121
|
-
lines.push(line("\u251C", "\u253C", "\u2524", "\u2500"));
|
|
122
|
-
}
|
|
123
|
-
for (const r of finishedRows) {
|
|
124
|
-
lines.push(row(r.id, r.title, r.worker, r.path, r.status, r.time, r.duration));
|
|
125
|
-
}
|
|
126
|
-
lines.push(line("\u2514", "\u2534", "\u2518", "\u2500"));
|
|
127
|
-
return lines;
|
|
104
|
+
const truncTitle = (s) => getDisplayWidth(s) > maxTitleWidth ? truncateToWidth(s, maxTitleWidth) : s;
|
|
105
|
+
const truncPath = (p) => p ? getDisplayWidth(p) > maxPathWidth ? truncateToWidth(p, maxPathWidth) : p : "";
|
|
106
|
+
const hasPath = entries.some((t) => truncPath(t.path) !== "");
|
|
107
|
+
const runningRows = runningTasks.map(
|
|
108
|
+
(t) => taskRow(
|
|
109
|
+
t,
|
|
110
|
+
truncTitle(t.title),
|
|
111
|
+
truncPath(t.path),
|
|
112
|
+
hasPath,
|
|
113
|
+
t.agentStatus ? `${t.status}:${t.agentStatus}` : t.status,
|
|
114
|
+
formatTime(t.startedAt),
|
|
115
|
+
formatDuration(t.startedAt, now)
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
const finishedRows = finishedTasks.map(
|
|
119
|
+
(t) => taskRow(
|
|
120
|
+
t,
|
|
121
|
+
truncTitle(t.title),
|
|
122
|
+
truncPath(t.path),
|
|
123
|
+
hasPath,
|
|
124
|
+
t.status,
|
|
125
|
+
formatTime(t.finishedAt ?? t.startedAt),
|
|
126
|
+
formatDuration(t.startedAt, t.finishedAt ?? now)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
const headers = hasPath ? ["#", "Title", "Worker", "Worktree", "Status", "Time", "Duration"] : ["#", "Title", "Worker", "Status", "Time", "Duration"];
|
|
130
|
+
return renderBoxTable(headers, [runningRows, finishedRows]);
|
|
131
|
+
}
|
|
132
|
+
function taskRow(t, title, path2, hasPath, status, time, duration) {
|
|
133
|
+
return hasPath ? [`#${t.id}`, title, t.workerName, path2, status, time, duration] : [`#${t.id}`, title, t.workerName, status, time, duration];
|
|
128
134
|
}
|
|
135
|
+
function sanitizeLogText(text) {
|
|
136
|
+
return text.replace(CONTROL_CHARS, " ").replace(/\t/g, " ");
|
|
137
|
+
}
|
|
138
|
+
function buildLogTableLines(entries) {
|
|
139
|
+
if (entries.length === 0) return [];
|
|
140
|
+
const maxTextWidth = 100;
|
|
141
|
+
const rows = entries.map((e) => {
|
|
142
|
+
const text = sanitizeLogText(e.text);
|
|
143
|
+
return [
|
|
144
|
+
formatTime(e.time),
|
|
145
|
+
`#${e.id}`,
|
|
146
|
+
e.stream,
|
|
147
|
+
getDisplayWidth(text) > maxTextWidth ? truncateToWidth(text, maxTextWidth) : text
|
|
148
|
+
];
|
|
149
|
+
});
|
|
150
|
+
return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
|
|
151
|
+
}
|
|
152
|
+
var TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, CONTROL_CHARS;
|
|
129
153
|
var init_table = __esm({
|
|
130
154
|
"src/table.ts"() {
|
|
131
155
|
"use strict";
|
|
156
|
+
TASK_DISPLAY_LIMIT = 20;
|
|
157
|
+
LOG_DISPLAY_LIMIT = 20;
|
|
158
|
+
CONTROL_CHARS = /\x1b\[[0-9;?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
132
159
|
}
|
|
133
160
|
});
|
|
134
161
|
|
|
@@ -1515,7 +1542,8 @@ function systemPromptFilePath() {
|
|
|
1515
1542
|
cachedSystemPromptFilePath = target;
|
|
1516
1543
|
return target;
|
|
1517
1544
|
}
|
|
1518
|
-
function buildClaudeArgs({ mode, prompt, model, effort }) {
|
|
1545
|
+
function buildClaudeArgs({ mode, prompt, model, effort, advisorModel }) {
|
|
1546
|
+
const advisor = advisorModel?.trim() ?? "";
|
|
1519
1547
|
return [
|
|
1520
1548
|
...mode === "herdr" ? [] : ["-p"],
|
|
1521
1549
|
prompt,
|
|
@@ -1528,7 +1556,10 @@ function buildClaudeArgs({ mode, prompt, model, effort }) {
|
|
|
1528
1556
|
"--model",
|
|
1529
1557
|
model,
|
|
1530
1558
|
"--effort",
|
|
1531
|
-
effort
|
|
1559
|
+
effort,
|
|
1560
|
+
// advisor 未指定(空文字)ならフラグごと省く。値なしの `--advisor` を渡すと
|
|
1561
|
+
// 後続フラグを値として食われるため、必ずモデル名とセットでのみ付ける。
|
|
1562
|
+
...advisor === "" ? [] : ["--advisor", advisor]
|
|
1532
1563
|
];
|
|
1533
1564
|
}
|
|
1534
1565
|
function buildClaudeExecution(invocation) {
|
|
@@ -1548,6 +1579,7 @@ var DEFAULT_UI_DESIGN_CONFIG = {
|
|
|
1548
1579
|
var DEFAULT_WORKER_CONFIG = {
|
|
1549
1580
|
skill: "",
|
|
1550
1581
|
model: "sonnet",
|
|
1582
|
+
advisorModel: "opus",
|
|
1551
1583
|
effort: "high",
|
|
1552
1584
|
pollingIntervalSeconds: 60,
|
|
1553
1585
|
cooldownSeconds: 0,
|
|
@@ -1557,6 +1589,7 @@ var WORKER_DEFAULTS = {
|
|
|
1557
1589
|
"answer-issue-questions": {
|
|
1558
1590
|
skill: "/claude-task-worker:answer-issue-questions",
|
|
1559
1591
|
model: "opus",
|
|
1592
|
+
advisorModel: "",
|
|
1560
1593
|
effort: "xhigh",
|
|
1561
1594
|
pollingIntervalSeconds: 60,
|
|
1562
1595
|
cooldownSeconds: 0,
|
|
@@ -1565,6 +1598,7 @@ var WORKER_DEFAULTS = {
|
|
|
1565
1598
|
"create-issue": {
|
|
1566
1599
|
skill: "/claude-task-worker:create-issue-from-issue-number",
|
|
1567
1600
|
model: "sonnet",
|
|
1601
|
+
advisorModel: "opus",
|
|
1568
1602
|
effort: "xhigh",
|
|
1569
1603
|
pollingIntervalSeconds: 60,
|
|
1570
1604
|
cooldownSeconds: 0,
|
|
@@ -1573,6 +1607,7 @@ var WORKER_DEFAULTS = {
|
|
|
1573
1607
|
"update-issue": {
|
|
1574
1608
|
skill: "/claude-task-worker:update-issue",
|
|
1575
1609
|
model: "sonnet",
|
|
1610
|
+
advisorModel: "opus",
|
|
1576
1611
|
effort: "high",
|
|
1577
1612
|
pollingIntervalSeconds: 60,
|
|
1578
1613
|
cooldownSeconds: 0,
|
|
@@ -1581,6 +1616,7 @@ var WORKER_DEFAULTS = {
|
|
|
1581
1616
|
"exec-issue": {
|
|
1582
1617
|
skill: "/claude-task-worker:exec-issue",
|
|
1583
1618
|
model: "sonnet",
|
|
1619
|
+
advisorModel: "opus",
|
|
1584
1620
|
effort: "high",
|
|
1585
1621
|
pollingIntervalSeconds: 60,
|
|
1586
1622
|
cooldownSeconds: 0,
|
|
@@ -1589,6 +1625,7 @@ var WORKER_DEFAULTS = {
|
|
|
1589
1625
|
"fix-review-point": {
|
|
1590
1626
|
skill: "/claude-task-worker:fix-review-point",
|
|
1591
1627
|
model: "sonnet",
|
|
1628
|
+
advisorModel: "opus",
|
|
1592
1629
|
effort: "high",
|
|
1593
1630
|
pollingIntervalSeconds: 60,
|
|
1594
1631
|
cooldownSeconds: 0,
|
|
@@ -1597,6 +1634,7 @@ var WORKER_DEFAULTS = {
|
|
|
1597
1634
|
"triage-created-issue": {
|
|
1598
1635
|
skill: "/claude-task-worker:triage-created-issue",
|
|
1599
1636
|
model: "sonnet",
|
|
1637
|
+
advisorModel: "opus",
|
|
1600
1638
|
effort: "high",
|
|
1601
1639
|
pollingIntervalSeconds: 60,
|
|
1602
1640
|
cooldownSeconds: 0,
|
|
@@ -1605,6 +1643,7 @@ var WORKER_DEFAULTS = {
|
|
|
1605
1643
|
"triage-pr": {
|
|
1606
1644
|
skill: "/claude-task-worker:triage-pr",
|
|
1607
1645
|
model: "sonnet",
|
|
1646
|
+
advisorModel: "opus",
|
|
1608
1647
|
effort: "high",
|
|
1609
1648
|
pollingIntervalSeconds: 60,
|
|
1610
1649
|
cooldownSeconds: 0,
|
|
@@ -1613,6 +1652,7 @@ var WORKER_DEFAULTS = {
|
|
|
1613
1652
|
"resolve-conflict": {
|
|
1614
1653
|
skill: "/claude-task-worker:resolve-pr-conflict",
|
|
1615
1654
|
model: "sonnet",
|
|
1655
|
+
advisorModel: "opus",
|
|
1616
1656
|
effort: "high",
|
|
1617
1657
|
pollingIntervalSeconds: 60,
|
|
1618
1658
|
cooldownSeconds: 0,
|
|
@@ -1621,6 +1661,7 @@ var WORKER_DEFAULTS = {
|
|
|
1621
1661
|
"check-dependabot": {
|
|
1622
1662
|
skill: "/claude-task-worker:check-dependabot",
|
|
1623
1663
|
model: "sonnet",
|
|
1664
|
+
advisorModel: "opus",
|
|
1624
1665
|
effort: "high",
|
|
1625
1666
|
pollingIntervalSeconds: 3600,
|
|
1626
1667
|
cooldownSeconds: 0,
|
|
@@ -1629,6 +1670,7 @@ var WORKER_DEFAULTS = {
|
|
|
1629
1670
|
"epic-issue": {
|
|
1630
1671
|
skill: "/claude-task-worker:create-epic-pr",
|
|
1631
1672
|
model: "sonnet",
|
|
1673
|
+
advisorModel: "opus",
|
|
1632
1674
|
effort: "high",
|
|
1633
1675
|
pollingIntervalSeconds: 300,
|
|
1634
1676
|
cooldownSeconds: 0,
|
|
@@ -1637,6 +1679,7 @@ var WORKER_DEFAULTS = {
|
|
|
1637
1679
|
"create-ui-design": {
|
|
1638
1680
|
skill: "/claude-task-worker:create-ui-design",
|
|
1639
1681
|
model: "sonnet",
|
|
1682
|
+
advisorModel: "opus",
|
|
1640
1683
|
effort: "high",
|
|
1641
1684
|
pollingIntervalSeconds: 60,
|
|
1642
1685
|
cooldownSeconds: 0,
|
|
@@ -1645,6 +1688,7 @@ var WORKER_DEFAULTS = {
|
|
|
1645
1688
|
"apply-ui-design": {
|
|
1646
1689
|
skill: "/claude-task-worker:apply-ui-design",
|
|
1647
1690
|
model: "sonnet",
|
|
1691
|
+
advisorModel: "opus",
|
|
1648
1692
|
effort: "high",
|
|
1649
1693
|
pollingIntervalSeconds: 300,
|
|
1650
1694
|
cooldownSeconds: 0,
|
|
@@ -1682,6 +1726,15 @@ function parseWorkerEntry(name, val) {
|
|
|
1682
1726
|
console.warn(`[config] invalid workers.${name}.model: ${String(entry.model)}, using default ${base.model}`);
|
|
1683
1727
|
}
|
|
1684
1728
|
}
|
|
1729
|
+
if ("advisorModel" in entry) {
|
|
1730
|
+
if (typeof entry.advisorModel === "string") {
|
|
1731
|
+
result.advisorModel = entry.advisorModel;
|
|
1732
|
+
} else {
|
|
1733
|
+
console.warn(
|
|
1734
|
+
`[config] invalid workers.${name}.advisorModel: ${String(entry.advisorModel)}, using default ${JSON.stringify(base.advisorModel)}`
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1685
1738
|
if ("effort" in entry) {
|
|
1686
1739
|
if (typeof entry.effort === "string" && entry.effort.length > 0) {
|
|
1687
1740
|
result.effort = entry.effort;
|
|
@@ -1843,6 +1896,7 @@ async function assertRemoteTrackingExists(epicBranch) {
|
|
|
1843
1896
|
// src/process-manager.ts
|
|
1844
1897
|
import { spawn } from "node:child_process";
|
|
1845
1898
|
import { basename } from "node:path";
|
|
1899
|
+
import { StringDecoder } from "node:string_decoder";
|
|
1846
1900
|
init_table();
|
|
1847
1901
|
|
|
1848
1902
|
// src/task-result.ts
|
|
@@ -1870,6 +1924,7 @@ import { homedir } from "node:os";
|
|
|
1870
1924
|
import { isAbsolute as isAbsolute2, join as join2, resolve } from "node:path";
|
|
1871
1925
|
var RESERVED_ALL = "all";
|
|
1872
1926
|
var DEFAULT_RUN_MODE = "default";
|
|
1927
|
+
var DEFAULT_ADVISOR_ENABLED = false;
|
|
1873
1928
|
var UserConfigError = class extends Error {
|
|
1874
1929
|
constructor(message) {
|
|
1875
1930
|
super(message);
|
|
@@ -1918,6 +1973,13 @@ function parseMode(raw, path2) {
|
|
|
1918
1973
|
console.warn(`[config] invalid mode: ${JSON.stringify(value)} in ${path2}, using "${DEFAULT_RUN_MODE}"`);
|
|
1919
1974
|
return DEFAULT_RUN_MODE;
|
|
1920
1975
|
}
|
|
1976
|
+
function parseAdvisor(raw, path2) {
|
|
1977
|
+
if (!("advisor" in raw)) return DEFAULT_ADVISOR_ENABLED;
|
|
1978
|
+
const value = raw["advisor"];
|
|
1979
|
+
if (typeof value === "boolean") return value;
|
|
1980
|
+
console.warn(`[config] invalid advisor: ${JSON.stringify(value)} in ${path2}, using ${DEFAULT_ADVISOR_ENABLED}`);
|
|
1981
|
+
return DEFAULT_ADVISOR_ENABLED;
|
|
1982
|
+
}
|
|
1921
1983
|
function loadUserConfig() {
|
|
1922
1984
|
const path2 = getUserConfigPath();
|
|
1923
1985
|
const raw = readRawConfig();
|
|
@@ -1934,6 +1996,7 @@ function loadUserConfig() {
|
|
|
1934
1996
|
throw new UserConfigError(`config.json "projectGroups" must be an object: ${path2}`);
|
|
1935
1997
|
}
|
|
1936
1998
|
const mode = parseMode(raw, path2);
|
|
1999
|
+
const advisor = parseAdvisor(raw, path2);
|
|
1937
2000
|
const rawProjects = raw["projects"];
|
|
1938
2001
|
const rawProjectGroups = "projectGroups" in raw ? raw["projectGroups"] : {};
|
|
1939
2002
|
const projectKeys = Object.keys(rawProjects);
|
|
@@ -1984,7 +2047,7 @@ function loadUserConfig() {
|
|
|
1984
2047
|
}
|
|
1985
2048
|
projectGroups[groupName] = members;
|
|
1986
2049
|
}
|
|
1987
|
-
return { mode, projects, projectGroups };
|
|
2050
|
+
return { mode, advisor, projects, projectGroups };
|
|
1988
2051
|
}
|
|
1989
2052
|
var cachedRunMode;
|
|
1990
2053
|
function getRunMode() {
|
|
@@ -1993,6 +2056,27 @@ function getRunMode() {
|
|
|
1993
2056
|
}
|
|
1994
2057
|
return cachedRunMode;
|
|
1995
2058
|
}
|
|
2059
|
+
var cachedAdvisorEnabled;
|
|
2060
|
+
function isAdvisorEnabled() {
|
|
2061
|
+
if (cachedAdvisorEnabled === void 0) {
|
|
2062
|
+
cachedAdvisorEnabled = readAdvisorEnabled();
|
|
2063
|
+
}
|
|
2064
|
+
return cachedAdvisorEnabled;
|
|
2065
|
+
}
|
|
2066
|
+
function readAdvisorEnabled() {
|
|
2067
|
+
let raw;
|
|
2068
|
+
try {
|
|
2069
|
+
raw = readRawConfig();
|
|
2070
|
+
} catch (err) {
|
|
2071
|
+
console.warn(`[config] failed to read config file, advisor disabled: ${err}`);
|
|
2072
|
+
return DEFAULT_ADVISOR_ENABLED;
|
|
2073
|
+
}
|
|
2074
|
+
if (raw === void 0) return DEFAULT_ADVISOR_ENABLED;
|
|
2075
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2076
|
+
return DEFAULT_ADVISOR_ENABLED;
|
|
2077
|
+
}
|
|
2078
|
+
return parseAdvisor(raw, getUserConfigPath());
|
|
2079
|
+
}
|
|
1996
2080
|
function readRunMode() {
|
|
1997
2081
|
let raw;
|
|
1998
2082
|
try {
|
|
@@ -2059,6 +2143,38 @@ var childProcesses = /* @__PURE__ */ new Map();
|
|
|
2059
2143
|
var herdrTasks = /* @__PURE__ */ new Map();
|
|
2060
2144
|
var herdrAbortSignal = { aborted: false };
|
|
2061
2145
|
var tasks = /* @__PURE__ */ new Map();
|
|
2146
|
+
var logLines = [];
|
|
2147
|
+
function pushLogLine(id, stream, text) {
|
|
2148
|
+
const trimmed = text.replace(/\r$/, "");
|
|
2149
|
+
if (trimmed.trim().length === 0) return;
|
|
2150
|
+
logLines.push({ id, stream, text: trimmed, time: /* @__PURE__ */ new Date() });
|
|
2151
|
+
if (logLines.length > LOG_DISPLAY_LIMIT) {
|
|
2152
|
+
logLines.splice(0, logLines.length - LOG_DISPLAY_LIMIT);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
function makeLogFeeder(id, stream) {
|
|
2156
|
+
let partial = "";
|
|
2157
|
+
const decoder = new StringDecoder("utf-8");
|
|
2158
|
+
return {
|
|
2159
|
+
feed(chunk) {
|
|
2160
|
+
partial += decoder.write(chunk);
|
|
2161
|
+
const parts = partial.split("\n");
|
|
2162
|
+
partial = parts.pop() ?? "";
|
|
2163
|
+
for (const part of parts) pushLogLine(id, stream, part);
|
|
2164
|
+
},
|
|
2165
|
+
flush() {
|
|
2166
|
+
partial += decoder.end();
|
|
2167
|
+
if (partial.length > 0) {
|
|
2168
|
+
pushLogLine(id, stream, partial);
|
|
2169
|
+
partial = "";
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
function pruneTaskHistory() {
|
|
2175
|
+
const finished = [...tasks.values()].filter((t) => t.status !== "running").sort((a, b) => (b.finishedAt ?? b.startedAt).getTime() - (a.finishedAt ?? a.startedAt).getTime());
|
|
2176
|
+
for (const t of finished.slice(TASK_DISPLAY_LIMIT)) tasks.delete(t.id);
|
|
2177
|
+
}
|
|
2062
2178
|
var shuttingDown = false;
|
|
2063
2179
|
function setShuttingDown() {
|
|
2064
2180
|
shuttingDown = true;
|
|
@@ -2088,8 +2204,14 @@ function isWorkerAtCapacity(workerName) {
|
|
|
2088
2204
|
return count >= getWorkerConfig(workerName).maxConcurrentTasks;
|
|
2089
2205
|
}
|
|
2090
2206
|
function renderTable() {
|
|
2091
|
-
const
|
|
2092
|
-
|
|
2207
|
+
const taskLines = buildTaskTableLines(selectRecentTasks([...tasks.values()]));
|
|
2208
|
+
const logTableLines = buildLogTableLines(logLines);
|
|
2209
|
+
if (taskLines.length === 0 && logTableLines.length === 0) return;
|
|
2210
|
+
const lines = [...taskLines];
|
|
2211
|
+
if (logTableLines.length > 0) {
|
|
2212
|
+
if (lines.length > 0) lines.push("");
|
|
2213
|
+
lines.push("Logs", ...logTableLines);
|
|
2214
|
+
}
|
|
2093
2215
|
console.clear();
|
|
2094
2216
|
console.log(lines.join("\n"));
|
|
2095
2217
|
}
|
|
@@ -2116,6 +2238,7 @@ async function finishTask(id, result, onComplete) {
|
|
|
2116
2238
|
task.finishedAt = /* @__PURE__ */ new Date();
|
|
2117
2239
|
task.agentStatus = void 0;
|
|
2118
2240
|
}
|
|
2241
|
+
pruneTaskHistory();
|
|
2119
2242
|
renderTable();
|
|
2120
2243
|
}
|
|
2121
2244
|
function resolveProjectName(cwd = process.cwd()) {
|
|
@@ -2159,6 +2282,7 @@ async function runViaHerdr(args, id, onComplete, cwd, env) {
|
|
|
2159
2282
|
herdrTasks.delete(id);
|
|
2160
2283
|
}
|
|
2161
2284
|
function run(command, args, id, title, workerName, path2, onComplete, cwd, env) {
|
|
2285
|
+
tasks.delete(id);
|
|
2162
2286
|
tasks.set(id, {
|
|
2163
2287
|
id,
|
|
2164
2288
|
title,
|
|
@@ -2180,9 +2304,12 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env)
|
|
|
2180
2304
|
...env ? { env: { ...process.env, ...env } } : {}
|
|
2181
2305
|
});
|
|
2182
2306
|
childProcesses.set(id, child);
|
|
2307
|
+
const stdoutFeeder = makeLogFeeder(id, "stdout");
|
|
2308
|
+
const stderrFeeder = makeLogFeeder(id, "stderr");
|
|
2183
2309
|
const outputChunks = [];
|
|
2184
2310
|
child.stdout?.on("data", (chunk) => {
|
|
2185
2311
|
outputChunks.push(chunk);
|
|
2312
|
+
stdoutFeeder.feed(chunk);
|
|
2186
2313
|
});
|
|
2187
2314
|
const stderrChunks = [];
|
|
2188
2315
|
let stderrLen = 0;
|
|
@@ -2193,8 +2320,11 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env)
|
|
|
2193
2320
|
stderrLen -= stderrChunks[0].length;
|
|
2194
2321
|
stderrChunks.shift();
|
|
2195
2322
|
}
|
|
2323
|
+
stderrFeeder.feed(chunk);
|
|
2196
2324
|
});
|
|
2197
2325
|
child.on("close", async (code) => {
|
|
2326
|
+
stdoutFeeder.flush();
|
|
2327
|
+
stderrFeeder.flush();
|
|
2198
2328
|
const result = buildTaskResult(
|
|
2199
2329
|
code,
|
|
2200
2330
|
Buffer.concat(outputChunks).toString("utf-8"),
|
|
@@ -2903,7 +3033,7 @@ function createIssuePollingWorker(config) {
|
|
|
2903
3033
|
try {
|
|
2904
3034
|
const issueUrl = `https://github.com/${owner}/${name}/issues/${issue.number}`;
|
|
2905
3035
|
syncDefaultBranch(defaultBranch);
|
|
2906
|
-
const { model, effort, skill } = getWorkerConfig(config.name);
|
|
3036
|
+
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
2907
3037
|
const command = skill || config.command;
|
|
2908
3038
|
const parentNumber = issue.parent?.number;
|
|
2909
3039
|
const mode = getRunMode();
|
|
@@ -2911,7 +3041,9 @@ function createIssuePollingWorker(config) {
|
|
|
2911
3041
|
mode,
|
|
2912
3042
|
prompt: `${command} ${issue.number}`,
|
|
2913
3043
|
model,
|
|
2914
|
-
effort
|
|
3044
|
+
effort,
|
|
3045
|
+
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3046
|
+
advisorModel: isAdvisorEnabled() ? advisorModel : ""
|
|
2915
3047
|
});
|
|
2916
3048
|
let baseBranch = defaultBranch;
|
|
2917
3049
|
if (parentNumber !== void 0) {
|
|
@@ -3075,14 +3207,16 @@ function createPrPollingWorker(config) {
|
|
|
3075
3207
|
syncDefaultBranch(defaultBranch);
|
|
3076
3208
|
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3077
3209
|
const cwd = getWorktreePath(worktreeId);
|
|
3078
|
-
const { model, effort, skill } = getWorkerConfig(config.name);
|
|
3210
|
+
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
3079
3211
|
const command = skill || config.command;
|
|
3080
3212
|
const mode = getRunMode();
|
|
3081
3213
|
const execution = buildClaudeExecution({
|
|
3082
3214
|
mode,
|
|
3083
3215
|
prompt: `${command} ${pr.number}`,
|
|
3084
3216
|
model,
|
|
3085
|
-
effort
|
|
3217
|
+
effort,
|
|
3218
|
+
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3219
|
+
advisorModel: isAdvisorEnabled() ? advisorModel : ""
|
|
3086
3220
|
});
|
|
3087
3221
|
run(
|
|
3088
3222
|
execution.command,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-task-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "CLI tool that polls GitHub Issues/PRs and delegates work to Claude CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dev": "tsc --noEmit --watch",
|
|
13
13
|
"lint": "eslint .",
|
|
14
14
|
"lint:fix": "eslint . --fix",
|
|
15
|
-
"test": "node --experimental-strip-types --test \"src/**/*.test.ts\" \"plugin/scripts/**/*.test.mjs\"",
|
|
15
|
+
"test": "node --experimental-strip-types --import ./scripts/test-resolver.mjs --test \"src/**/*.test.ts\" \"plugin/scripts/**/*.test.mjs\"",
|
|
16
16
|
"format": "prettier --write .",
|
|
17
17
|
"format:check": "prettier --check .",
|
|
18
18
|
"version": "node scripts/sync-plugin-version.mjs && git add plugin/.claude-plugin/plugin.json",
|