claude-task-worker 0.106.0 → 0.108.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 +14 -1
- package/dist/index.js +109 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ claude-task-worker init --force # 強制上書き
|
|
|
122
122
|
## コマンド
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
|
-
claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--project <name>]... [--cloud]
|
|
125
|
+
claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--project <name>]... [--cloud] [--debug]
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
| コマンド | 内容 |
|
|
@@ -210,6 +210,19 @@ npx claude-task-worker cloud-setup
|
|
|
210
210
|
|
|
211
211
|
詳細は [`docs/prd-cloud-worker-execution.md`](./docs/prd-cloud-worker-execution.md) を参照。
|
|
212
212
|
|
|
213
|
+
### `--debug`
|
|
214
|
+
|
|
215
|
+
各タスクの最終報告を対象 Issue/PR へコメントとして残す。ローカル実行(`default` / `herdr`)でもクラウド実行(`--cloud`)でも使える。
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
claude-task-worker exec-issue --debug
|
|
219
|
+
claude-task-worker exec-issue --cloud --debug
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
既定(`--debug` なし)では最終報告は Slack 通知にのみ載り、Issue/PR にはコメントされない。毎タスク投稿すると Issue/PR がワーカーの実行ログで埋まるため。
|
|
223
|
+
|
|
224
|
+
投稿の担当は実行形態で違う。ローカルはワーカーが報告そのもの(`claude -p` の stdout / transcript)を持っているのでワーカーが投稿し、クラウドはワーカーに報告が届かないのでセッション自身に投稿させる(ワーカーはそれを回収して Slack へ載せる)。定期ワーカー(`update-*`)の投稿先は実行記録PR(`ctw-last-run-<ワーカー名>`)。クラウド実行の完了検知(`cc-cloud-done`)はフラグの有無に関わらず従来どおり動く。
|
|
225
|
+
|
|
213
226
|
### Pen CLI のログイン
|
|
214
227
|
|
|
215
228
|
`.pen` を扱うスキルは Pen CLI の認証を必要とする。UIデザイン先行ワークフローを使うなら一度ログインしておく。
|
package/dist/index.js
CHANGED
|
@@ -1732,6 +1732,13 @@ function hasCloudFlag() {
|
|
|
1732
1732
|
}
|
|
1733
1733
|
return cachedCloudFlag;
|
|
1734
1734
|
}
|
|
1735
|
+
var cachedDebugFlag;
|
|
1736
|
+
function hasDebugFlag() {
|
|
1737
|
+
if (cachedDebugFlag === void 0) {
|
|
1738
|
+
cachedDebugFlag = process.argv.includes("--debug");
|
|
1739
|
+
}
|
|
1740
|
+
return cachedDebugFlag;
|
|
1741
|
+
}
|
|
1735
1742
|
function assertCloudCompatibleCommand(command) {
|
|
1736
1743
|
if (FLAG_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
1737
1744
|
console.error(`[worker] --cloud cannot be used with the "${command}" command`);
|
|
@@ -1917,9 +1924,8 @@ var SCHEDULED_WORKER_NAMES = [
|
|
|
1917
1924
|
"update-design-md"
|
|
1918
1925
|
];
|
|
1919
1926
|
var CLOUD_DONE_LABEL = "cc-cloud-done";
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
return hasCloudFlag() && CLOUD_ALLOWED_WORKERS.includes(name);
|
|
1927
|
+
function isCloudWorker(_name) {
|
|
1928
|
+
return hasCloudFlag();
|
|
1923
1929
|
}
|
|
1924
1930
|
function checkCloudAuth(input) {
|
|
1925
1931
|
if (input.status.kind === "unknown") return [];
|
|
@@ -2586,22 +2592,22 @@ function buildClaudeArgs({
|
|
|
2586
2592
|
function buildCloudCreateArgs(commonArgs, prompt) {
|
|
2587
2593
|
return ["--cloud", prompt, ...commonArgs];
|
|
2588
2594
|
}
|
|
2589
|
-
function appendCloudDoneInstruction(prompt, target) {
|
|
2595
|
+
function appendCloudDoneInstruction(prompt, target, debug = false) {
|
|
2590
2596
|
const targetLabel = target.type === "issue" ? `Issue #${target.number}` : `PR #${target.number}`;
|
|
2591
2597
|
const checkoutInstruction = buildCloudCheckoutInstruction(target);
|
|
2592
2598
|
const worktreeInstruction = buildCloudWorktreeInstruction(prompt);
|
|
2593
|
-
const reportInstruction = `\`${CLOUD_DONE_LABEL}\` \u30E9\u30D9\u30EB\u3092\u4ED8\u3051\u308B\u76F4\u524D\u306B\u3001${targetLabel} \u3078 \`${CLOUD_REPORT_HEADING}\` \u3092\u898B\u51FA\u3057\u3068\u3059\u308B\u30B3\u30E1\u30F3\u30C8\u30921\u4EF6\u6295\u7A3F\u3057\u3001\u672C\u6587\u306B\u6700\u7D42\u5831\u544A\uFF08\u5B8C\u4E86\u30FB\u4E2D\u65AD\u306B\u304B\u304B\u308F\u3089\u305A\uFF09\u3092\u66F8\u304F\u3053\u3068\u3002GitHub MCP\uFF08\`add_issue_comment\`\uFF09\u3092\u512A\u5148\u3057\u3001\u5931\u6557\u3057\u305F\u5834\u5408\u306E\u307F \`gh ${target.type} comment ${target.number} --body-file -\` \u3078\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3059\u308B\u3053\u3068\uFF08\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u306F1\u56DE\u307E\u3067\uFF09\u3002\u30EF\u30FC\u30AB\u30FC\u306F\u3053\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u6700\u7D42\u30EC\u30DD\u30FC\u30C8\u3068\u3057\u3066\u56DE\u53CE\u3057 Slack \u901A\u77E5\u306B\u8F09\u305B\u308B\u3002
|
|
2594
|
-
const labelInstruction =
|
|
2599
|
+
const reportInstruction = debug ? `\`${CLOUD_DONE_LABEL}\` \u30E9\u30D9\u30EB\u3092\u4ED8\u3051\u308B\u76F4\u524D\u306B\u3001${targetLabel} \u3078 \`${CLOUD_REPORT_HEADING}\` \u3092\u898B\u51FA\u3057\u3068\u3059\u308B\u30B3\u30E1\u30F3\u30C8\u30921\u4EF6\u6295\u7A3F\u3057\u3001\u672C\u6587\u306B\u6700\u7D42\u5831\u544A\uFF08\u5B8C\u4E86\u30FB\u4E2D\u65AD\u306B\u304B\u304B\u308F\u3089\u305A\uFF09\u3092\u66F8\u304F\u3053\u3068\u3002GitHub MCP\uFF08\`add_issue_comment\`\uFF09\u3092\u512A\u5148\u3057\u3001\u5931\u6557\u3057\u305F\u5834\u5408\u306E\u307F \`gh ${target.type} comment ${target.number} --body-file -\` \u3078\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3059\u308B\u3053\u3068\uFF08\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u306F1\u56DE\u307E\u3067\uFF09\u3002\u30EF\u30FC\u30AB\u30FC\u306F\u3053\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u6700\u7D42\u30EC\u30DD\u30FC\u30C8\u3068\u3057\u3066\u56DE\u53CE\u3057 Slack \u901A\u77E5\u306B\u8F09\u305B\u308B\u3002` : "";
|
|
2600
|
+
const labelInstruction = `${debug ? "\u4E0A\u8A18\u30B3\u30E1\u30F3\u30C8\u306E\u6295\u7A3F\u5F8C\u3001" : ""}\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u5F8C\u306E\u64CD\u4F5C\u3068\u3057\u3066 ${targetLabel} \u306B \`${CLOUD_DONE_LABEL}\` \u30E9\u30D9\u30EB\u3092\u4ED8\u4E0E\u3059\u308B\u3053\u3068\u3002GitHub MCP\uFF08\`issue_write\` / method: \`update\`\uFF09\u3092\u512A\u5148\u3057\u3001\u5931\u6557\u3057\u305F\u5834\u5408\u306E\u307F \`gh ${target.type} edit ${target.number} --add-label ${CLOUD_DONE_LABEL}\` \u3078\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3059\u308B\u3053\u3068\uFF08\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u306F1\u56DE\u307E\u3067\uFF09\u3002\u30EF\u30FC\u30AB\u30FC\u306F\u3053\u306E\u30E9\u30D9\u30EB\u3067\u30BF\u30B9\u30AF\u306E\u7D42\u4E86\u3092\u691C\u77E5\u3057\u3066\u304A\u308A\u3001\u4ED8\u4E0E\u3055\u308C\u306A\u3044\u3068\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u307E\u3067\u5B8C\u4E86\u6271\u3044\u306B\u306A\u3089\u306A\u3044\u3002`;
|
|
2595
2601
|
return [prompt, checkoutInstruction, worktreeInstruction, reportInstruction, labelInstruction].filter((part) => part !== "").join("\n\n");
|
|
2596
2602
|
}
|
|
2597
|
-
var CLOUD_WORKTREE_EXEMPT_SKILLS = ["exec-issue", "fix-review-point"];
|
|
2603
|
+
var CLOUD_WORKTREE_EXEMPT_SKILLS = ["exec-issue", "fix-review-point", "create-ui-design"];
|
|
2598
2604
|
function buildCloudWorktreeInstruction(prompt) {
|
|
2599
2605
|
const skill = prompt.trim().split(/\s+/)[0] ?? "";
|
|
2600
2606
|
if (!CLOUD_WORKTREE_EXEMPT_SKILLS.some((name) => skill.endsWith(`:${name}`))) return "";
|
|
2601
2607
|
return "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u306E\u305F\u3081 worktree \u3092\u6301\u305F\u305A\u3001cwd \u306F\u30EA\u30DD\u30B8\u30C8\u30EA\u30EB\u30FC\u30C8\u3067\u3042\u308B\uFF08\u30EF\u30FC\u30AB\u30FC\u306F\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u6642\u306B worktree \u3092\u4F5C\u3089\u306A\u3044\uFF09\u3002\u30B9\u30AD\u30EB\u672C\u6587\u306E\u30D5\u30A7\u30FC\u30BA0\u306B\u3042\u308B\u300C`pwd` \u304C `.claude/worktrees/` \u914D\u4E0B\u3067\u306A\u3051\u308C\u3070\u4E2D\u65AD\u3059\u308B\u300D\u78BA\u8A8D\u306F\u3001\u3053\u306E\u6307\u793A\u304C\u3042\u308B\u5834\u5408\u306B\u9650\u308A\u9069\u7528\u3057\u306A\u3044\u3053\u3068\u3002\u305F\u3060\u3057\u30AC\u30FC\u30C9\u306E\u76EE\u7684\u3067\u3042\u308B\u300C\u30C7\u30D5\u30A9\u30EB\u30C8\u30D6\u30E9\u30F3\u30C1\u3067\u4F5C\u696D\u3057\u306A\u3044\u300D\u306F\u7DAD\u6301\u3059\u308B\u3053\u3068 \u2014 \u540C\u3058\u30D5\u30A7\u30FC\u30BA0\u306E\u300C\u30C7\u30D5\u30A9\u30EB\u30C8\u30D6\u30E9\u30F3\u30C1\u540D\u3092\u53D6\u5F97\u3057\u3001\u73FE\u5728\u30D6\u30E9\u30F3\u30C1\u3068\u4E00\u81F4\u3059\u308B\u5834\u5408\u306F\u4E2D\u65AD\u3059\u308B\uFF08\u53D6\u5F97\u5931\u6557\u3082\u4E2D\u65AD\uFF09\u300D\u306F\u5FC5\u305A\u5B9F\u884C\u3057\u3001\u4E00\u81F4\u3057\u305F\u5834\u5408\u306F\u5F93\u6765\u3069\u304A\u308A\u4E2D\u65AD\u3059\u308B\u3053\u3068\u3002";
|
|
2602
2608
|
}
|
|
2603
2609
|
function buildCloudCheckoutInstruction(target) {
|
|
2604
|
-
if (target.type !== "pr") return "";
|
|
2610
|
+
if (target.type !== "pr" || !target.onBranch) return "";
|
|
2605
2611
|
return `\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F PR #${target.number} \u306E head \u30D6\u30E9\u30F3\u30C1\u4E0A\u3067\u958B\u59CB\u3057\u3066\u3044\u308B\uFF08\u30EF\u30FC\u30AB\u30FC\u304C \`--on-branch\` \u3067\u6307\u5B9A\u6E08\u307F\uFF09\u3002\u30B9\u30AD\u30EB\u672C\u6587\u304C\u6307\u793A\u3059\u308B \`gh pr checkout\` \u306F\u5B9F\u884C\u3057\u306A\u3044\u3053\u3068\uFF08\u30AF\u30E9\u30A6\u30C9\u3067\u306F GraphQL \u30B2\u30FC\u30C8\u306B\u3088\u308A 403 \u3067\u5931\u6557\u3057\u3001\u304B\u3064\u65E2\u306B\u30D6\u30E9\u30F3\u30C1\u4E0A\u306B\u3044\u308B\u306E\u3067\u4E0D\u8981\uFF09\u3002\u30D6\u30E9\u30F3\u30C1\u306E\u78BA\u8A8D\u304C\u8981\u308B\u5834\u5408\u306F \`git rev-parse --abbrev-ref HEAD\` \u3092\u4F7F\u3046\u3053\u3068\u3002`;
|
|
2606
2612
|
}
|
|
2607
2613
|
function buildCloudGitHubAccessInstruction() {
|
|
@@ -2610,7 +2616,7 @@ function buildCloudGitHubAccessInstruction() {
|
|
|
2610
2616
|
function buildCloudToolRestriction() {
|
|
2611
2617
|
return `\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u3067\u306F \`--disallowedTools\` \u30D5\u30E9\u30B0\u306B\u3088\u308B\u30C4\u30FC\u30EB\u5236\u9650\u304C\u53CD\u6620\u3055\u308C\u306A\u3044\u305F\u3081\u3001\u4EE5\u4E0B\u306E\u30C4\u30FC\u30EB\u3092\u4F7F\u308F\u306A\u3044\u3053\u3068: ${DISALLOWED_TOOLS.join(", ")}`;
|
|
2612
2618
|
}
|
|
2613
|
-
function buildCloudPrompt(prompt, model, target) {
|
|
2619
|
+
function buildCloudPrompt(prompt, model, target, debug = false) {
|
|
2614
2620
|
const principles = `\u4EE5\u4E0B\u306F\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5B9F\u884C\u539F\u5247\u3067\u3042\u308B\u3002\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u3067\u306F\u30B7\u30B9\u30C6\u30E0\u30D7\u30ED\u30F3\u30D7\u30C8\u306B\u3088\u308B\u6CE8\u5165\u304C\u53CD\u6620\u3055\u308C\u306A\u3044\u305F\u3081\u3001\u30D7\u30ED\u30F3\u30D7\u30C8\u672C\u6587\u3068\u3057\u3066\u6E21\u3057\u3066\u3044\u308B\u3002
|
|
2615
2621
|
|
|
2616
2622
|
${systemPromptFor(model)}
|
|
@@ -2621,7 +2627,7 @@ ${buildCloudGitHubAccessInstruction()}`;
|
|
|
2621
2627
|
const withPrinciples = `${prompt}
|
|
2622
2628
|
|
|
2623
2629
|
${principles}`;
|
|
2624
|
-
return target ? appendCloudDoneInstruction(withPrinciples, target) : withPrinciples;
|
|
2630
|
+
return target ? appendCloudDoneInstruction(withPrinciples, target, debug) : withPrinciples;
|
|
2625
2631
|
}
|
|
2626
2632
|
function shellQuote2(value) {
|
|
2627
2633
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -2820,6 +2826,21 @@ async function finishTask(id, result, onComplete) {
|
|
|
2820
2826
|
pruneTaskHistory();
|
|
2821
2827
|
renderTable();
|
|
2822
2828
|
}
|
|
2829
|
+
function onCompleteWithDebugReport(onComplete, target) {
|
|
2830
|
+
if (!target || !hasDebugFlag()) return onComplete;
|
|
2831
|
+
return async (status, output, cloudSessionId) => {
|
|
2832
|
+
const body = output.trim();
|
|
2833
|
+
if (body !== "") {
|
|
2834
|
+
const comment = target.type === "issue" ? commentOnIssue : commentOnPR;
|
|
2835
|
+
await comment(target.number, `${CLOUD_REPORT_HEADING}
|
|
2836
|
+
|
|
2837
|
+
${body}`).catch(
|
|
2838
|
+
(err) => console.error(`[worker] failed to post the debug report to ${target.type} #${target.number}: ${err}`)
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
await onComplete?.(status, output, cloudSessionId);
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2823
2844
|
function resolveProjectName(cwd = process.cwd()) {
|
|
2824
2845
|
const injected = process.env.CTW_PROJECT_NAME;
|
|
2825
2846
|
if (injected && injected.length > 0) return injected;
|
|
@@ -2899,8 +2920,7 @@ async function pollCloudWaiters() {
|
|
|
2899
2920
|
const now = Date.now();
|
|
2900
2921
|
for (const [key, waiter] of [...cloudWaiters.entries()]) {
|
|
2901
2922
|
const done = doneKeys.get(waiter.type);
|
|
2902
|
-
|
|
2903
|
-
if (done?.has(number)) {
|
|
2923
|
+
if (done?.has(waiter.number)) {
|
|
2904
2924
|
waiter.settle("completed");
|
|
2905
2925
|
cloudWaiters.delete(key);
|
|
2906
2926
|
} else if (now >= waiter.deadline) {
|
|
@@ -2915,19 +2935,21 @@ async function pollCloudWaiters() {
|
|
|
2915
2935
|
}
|
|
2916
2936
|
}
|
|
2917
2937
|
}
|
|
2918
|
-
function waitForCloudTask(id,
|
|
2938
|
+
function waitForCloudTask(id, target) {
|
|
2919
2939
|
return new Promise((resolve3) => {
|
|
2920
|
-
cloudWaiters.set(`${type}:${id}`, {
|
|
2921
|
-
type,
|
|
2940
|
+
cloudWaiters.set(`${target.type}:${target.number}:${id}`, {
|
|
2941
|
+
type: target.type,
|
|
2942
|
+
number: target.number,
|
|
2922
2943
|
deadline: Date.now() + CLOUD_TASK_TIMEOUT_MS,
|
|
2923
2944
|
settle: resolve3
|
|
2924
2945
|
});
|
|
2925
2946
|
ensureCloudPollLoop();
|
|
2926
2947
|
});
|
|
2927
2948
|
}
|
|
2928
|
-
async function flagOrphanedCloudSession(target,
|
|
2929
|
-
|
|
2930
|
-
|
|
2949
|
+
async function flagOrphanedCloudSession(target, reason, cloudSessionId) {
|
|
2950
|
+
const { type, number: id } = target;
|
|
2951
|
+
await addLabel(type, id, "cc-need-human-check").catch((err) => {
|
|
2952
|
+
console.error(`[worker] failed to add cc-need-human-check to ${type} #${id}: ${err}`);
|
|
2931
2953
|
});
|
|
2932
2954
|
const causeLine = reason === "session-id" ? "\u30BB\u30C3\u30B7\u30E7\u30F3ID\u306E\u62BD\u51FA\u306B\u5931\u6557\u3057\u3066\u5F85\u6A5F\u3092\u6253\u3061\u5207\u308A\u307E\u3057\u305F\u3002\u30D7\u30ED\u30F3\u30D7\u30C8\u306F\u6295\u5165\u6E08\u307F\u3067\u3001\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u72EC\u7ACB\u3057\u3066\u7A3C\u50CD\u3057\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002" : "\u30EF\u30FC\u30AB\u30FC\u306E\u30B7\u30E3\u30C3\u30C8\u30C0\u30A6\u30F3\u3067\u5F85\u6A5F\u3092\u6253\u3061\u5207\u308A\u307E\u3057\u305F\u3002\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u7D99\u7D9A\u3057\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u9AD8\u3044\u3067\u3059\u3002";
|
|
2933
2955
|
const sessionLine = cloudSessionId ? `https://claude.ai/code/${cloudSessionId}` : "\u30BB\u30C3\u30B7\u30E7\u30F3URL\u4E0D\u660E\uFF08ID\u62BD\u51FA\u306B\u5931\u6557\uFF09";
|
|
@@ -2938,9 +2960,9 @@ async function flagOrphanedCloudSession(target, id, reason, cloudSessionId) {
|
|
|
2938
2960
|
**\u30BB\u30C3\u30B7\u30E7\u30F3URL**: ${sessionLine}
|
|
2939
2961
|
|
|
2940
2962
|
**\u518D\u958B\u65B9\u6CD5**: \u5B64\u7ACB\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6210\u679C\u7269\u304C\u5C4A\u3044\u3066\u3044\u306A\u3044\u304B\u78BA\u8A8D\u3057\u305F\u3046\u3048\u3067\u3001\`cc-need-human-check\` \u30E9\u30D9\u30EB\u3092\u5916\u3059\u3068\u30DD\u30FC\u30EA\u30F3\u30B0\u5BFE\u8C61\u306B\u623B\u308A\u307E\u3059\u3002`;
|
|
2941
|
-
const commentFn =
|
|
2963
|
+
const commentFn = type === "issue" ? commentOnIssue : commentOnPR;
|
|
2942
2964
|
await commentFn(id, body).catch((err) => {
|
|
2943
|
-
console.error(`[worker] failed to comment on ${
|
|
2965
|
+
console.error(`[worker] failed to comment on ${type} #${id} about the orphaned cloud session: ${err}`);
|
|
2944
2966
|
});
|
|
2945
2967
|
}
|
|
2946
2968
|
async function createCloudSession(args, initialPrompt, cwd, env) {
|
|
@@ -2992,11 +3014,8 @@ async function createCloudSession(args, initialPrompt, cwd, env) {
|
|
|
2992
3014
|
}
|
|
2993
3015
|
async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget, model) {
|
|
2994
3016
|
herdrTasks.set(id, { paneId: "", tabId: "" });
|
|
2995
|
-
const
|
|
2996
|
-
|
|
2997
|
-
model ?? "",
|
|
2998
|
-
cloudTarget ? { type: cloudTarget, number: id } : void 0
|
|
2999
|
-
);
|
|
3017
|
+
const debug = hasDebugFlag();
|
|
3018
|
+
const initialPrompt = buildCloudPrompt(prompt, model ?? "", cloudTarget, debug);
|
|
3000
3019
|
let result;
|
|
3001
3020
|
let cloudSessionId;
|
|
3002
3021
|
try {
|
|
@@ -3007,17 +3026,20 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
3007
3026
|
result = { status: "completed", output: createOutput };
|
|
3008
3027
|
} else {
|
|
3009
3028
|
const outcome = await waitForCloudTask(id, cloudTarget);
|
|
3029
|
+
const { type: targetType, number: targetNumber } = cloudTarget;
|
|
3010
3030
|
if (outcome === "completed") {
|
|
3011
|
-
await removeLabel(
|
|
3012
|
-
console.error(`[worker] failed to remove ${CLOUD_DONE_LABEL} from ${
|
|
3031
|
+
await removeLabel(targetType, targetNumber, CLOUD_DONE_LABEL).catch((err) => {
|
|
3032
|
+
console.error(`[worker] failed to remove ${CLOUD_DONE_LABEL} from ${targetType} #${targetNumber}: ${err}`);
|
|
3013
3033
|
});
|
|
3014
3034
|
let reportBody = null;
|
|
3015
3035
|
const startedAt = tasks.get(id)?.startedAt;
|
|
3016
|
-
if (startedAt) {
|
|
3036
|
+
if (debug && startedAt) {
|
|
3017
3037
|
try {
|
|
3018
|
-
reportBody = await findCommentSince(
|
|
3038
|
+
reportBody = await findCommentSince(targetNumber, startedAt, CLOUD_REPORT_HEADING);
|
|
3019
3039
|
} catch (err) {
|
|
3020
|
-
console.error(
|
|
3040
|
+
console.error(
|
|
3041
|
+
`[worker] failed to fetch the cloud report comment for ${targetType} #${targetNumber}: ${err}`
|
|
3042
|
+
);
|
|
3021
3043
|
}
|
|
3022
3044
|
}
|
|
3023
3045
|
result = {
|
|
@@ -3026,8 +3048,8 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
3026
3048
|
[worker] detected completion via the ${CLOUD_DONE_LABEL} label`
|
|
3027
3049
|
};
|
|
3028
3050
|
} else if (outcome === "timeout") {
|
|
3029
|
-
await addLabel(
|
|
3030
|
-
console.error(`[worker] failed to add cc-need-human-check to ${
|
|
3051
|
+
await addLabel(targetType, targetNumber, "cc-need-human-check").catch((err) => {
|
|
3052
|
+
console.error(`[worker] failed to add cc-need-human-check to ${targetType} #${targetNumber}: ${err}`);
|
|
3031
3053
|
});
|
|
3032
3054
|
result = {
|
|
3033
3055
|
status: "failed",
|
|
@@ -3035,7 +3057,7 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
3035
3057
|
[worker] timed out waiting for the ${CLOUD_DONE_LABEL} label after ${CLOUD_TASK_TIMEOUT_MS / 1e3 / 60} minutes. Possible causes: the session stopped on AskUserQuestion, the cloud VM crashed, the plugin was not installed, or label assignment itself failed.`
|
|
3036
3058
|
};
|
|
3037
3059
|
} else {
|
|
3038
|
-
await flagOrphanedCloudSession(cloudTarget,
|
|
3060
|
+
await flagOrphanedCloudSession(cloudTarget, "shutdown", cloudSessionId);
|
|
3039
3061
|
result = {
|
|
3040
3062
|
status: "failed",
|
|
3041
3063
|
output: `${createOutput}
|
|
@@ -3049,7 +3071,7 @@ async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget,
|
|
|
3049
3071
|
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).";
|
|
3050
3072
|
if (cloudTarget) {
|
|
3051
3073
|
const reason = herdrAbortSignal.aborted ? "shutdown" : "session-id";
|
|
3052
|
-
await flagOrphanedCloudSession(cloudTarget,
|
|
3074
|
+
await flagOrphanedCloudSession(cloudTarget, reason, cloudSessionId);
|
|
3053
3075
|
orphanNote += " added cc-need-human-check so a human can verify whether it completed on its own.";
|
|
3054
3076
|
}
|
|
3055
3077
|
result = {
|
|
@@ -3081,8 +3103,9 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
|
|
|
3081
3103
|
void runViaCloud(args, prompt ?? "", id, onComplete, cwd, env, cloudTarget, model);
|
|
3082
3104
|
return;
|
|
3083
3105
|
}
|
|
3106
|
+
const onCompleteWithReport = onCompleteWithDebugReport(onComplete, cloudTarget);
|
|
3084
3107
|
if (getRunMode() === "herdr") {
|
|
3085
|
-
void runViaHerdr(args, prompt ?? "", id,
|
|
3108
|
+
void runViaHerdr(args, prompt ?? "", id, onCompleteWithReport, cwd, env);
|
|
3086
3109
|
return;
|
|
3087
3110
|
}
|
|
3088
3111
|
const child = spawn(command, args, {
|
|
@@ -3118,12 +3141,12 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
|
|
|
3118
3141
|
Buffer.concat(outputChunks).toString("utf-8"),
|
|
3119
3142
|
Buffer.concat(stderrChunks).toString("utf-8").slice(-STDERR_TAIL_LIMIT)
|
|
3120
3143
|
);
|
|
3121
|
-
await finishTask(id, result,
|
|
3144
|
+
await finishTask(id, result, onCompleteWithReport);
|
|
3122
3145
|
childProcesses.delete(id);
|
|
3123
3146
|
});
|
|
3124
3147
|
child.on("error", async (err) => {
|
|
3125
3148
|
console.error(`[worker] failed to spawn process for #${id}: ${err.message}`);
|
|
3126
|
-
await finishTask(id, { status: "failed", output: err.message },
|
|
3149
|
+
await finishTask(id, { status: "failed", output: err.message }, onCompleteWithReport);
|
|
3127
3150
|
childProcesses.delete(id);
|
|
3128
3151
|
});
|
|
3129
3152
|
}
|
|
@@ -3985,7 +4008,9 @@ function createIssuePollingWorker(config) {
|
|
|
3985
4008
|
buildClaudeEnv(mode, cloud),
|
|
3986
4009
|
execution.prompt,
|
|
3987
4010
|
cloud,
|
|
3988
|
-
|
|
4011
|
+
// クラウドでは cc-cloud-done の探索先、ローカルでは --debug の報告コメントの
|
|
4012
|
+
// 投稿先。どちらも対象は同じ Issue なので実行形態で出し分けない。
|
|
4013
|
+
{ type: "issue", number: issue.number },
|
|
3989
4014
|
model
|
|
3990
4015
|
);
|
|
3991
4016
|
} catch (err) {
|
|
@@ -4242,7 +4267,9 @@ function createPrPollingWorker(config) {
|
|
|
4242
4267
|
buildClaudeEnv(mode, isCloud),
|
|
4243
4268
|
execution.prompt,
|
|
4244
4269
|
isCloud,
|
|
4245
|
-
|
|
4270
|
+
// クラウドでは cc-cloud-done の探索先、ローカルでは --debug の報告コメントの
|
|
4271
|
+
// 投稿先。onBranch は `--on-branch` を渡したか(=クラウドのみ)を表す。
|
|
4272
|
+
{ type: "pr", number: pr.number, onBranch: isCloud },
|
|
4246
4273
|
model
|
|
4247
4274
|
);
|
|
4248
4275
|
} catch (err) {
|
|
@@ -4715,7 +4742,7 @@ async function publishLastRunPr(workerName, defaultBranch, at) {
|
|
|
4715
4742
|
writeLastRun(cwd, workerName, at);
|
|
4716
4743
|
if (!hasLastRunChange(await git(cwd, ["status", "--porcelain", CONFIG_FILE]))) {
|
|
4717
4744
|
console.log(`[${workerName}] lastRun unchanged, skipping PR`);
|
|
4718
|
-
return;
|
|
4745
|
+
return null;
|
|
4719
4746
|
}
|
|
4720
4747
|
await git(cwd, ["add", CONFIG_FILE]);
|
|
4721
4748
|
await git(cwd, ["commit", "-m", lastRunPrTitle(workerName)]);
|
|
@@ -4723,7 +4750,7 @@ async function publishLastRunPr(workerName, defaultBranch, at) {
|
|
|
4723
4750
|
const existing = await findOpenPrNumberByHeadRef(branch);
|
|
4724
4751
|
if (existing !== null) {
|
|
4725
4752
|
console.log(`[${workerName}] updated lastRun PR #${existing}`);
|
|
4726
|
-
return;
|
|
4753
|
+
return existing;
|
|
4727
4754
|
}
|
|
4728
4755
|
const prNumber = await createPullRequest(
|
|
4729
4756
|
defaultBranch,
|
|
@@ -4733,6 +4760,7 @@ async function publishLastRunPr(workerName, defaultBranch, at) {
|
|
|
4733
4760
|
{ labels: [LABEL_TRIAGE_SCOPE2], assignee: await getCurrentUser() }
|
|
4734
4761
|
);
|
|
4735
4762
|
console.log(`[${workerName}] opened lastRun PR #${prNumber}`);
|
|
4763
|
+
return prNumber;
|
|
4736
4764
|
} finally {
|
|
4737
4765
|
await removeWorktree(branch).catch((err) => console.error(`[${workerName}] removeWorktree failed: ${err}`));
|
|
4738
4766
|
}
|
|
@@ -4742,6 +4770,7 @@ async function publishLastRunPr(workerName, defaultBranch, at) {
|
|
|
4742
4770
|
var SCHEDULE_INTERVAL_HOURS = 24;
|
|
4743
4771
|
var SCHEDULE_INTERVAL_MS = SCHEDULE_INTERVAL_HOURS * 60 * 60 * 1e3;
|
|
4744
4772
|
var SCOPE_DAYS = SCHEDULE_INTERVAL_HOURS / 24;
|
|
4773
|
+
var LABEL_IN_PROGRESS2 = "cc-in-progress";
|
|
4745
4774
|
function createScheduledWorker(config) {
|
|
4746
4775
|
return async () => {
|
|
4747
4776
|
const { owner, name: repoName, defaultBranch } = await getRepoInfo();
|
|
@@ -4775,9 +4804,23 @@ function createScheduledWorker(config) {
|
|
|
4775
4804
|
permissionMode: getPermissionMode(),
|
|
4776
4805
|
...cloud ? { cloud: true, baseRef: defaultBranch, remoteEnvId: getRemoteEnvId() ?? "" } : {}
|
|
4777
4806
|
});
|
|
4778
|
-
await publishLastRunPr(config.name, defaultBranch, new Date(now)).catch(
|
|
4779
|
-
|
|
4780
|
-
|
|
4807
|
+
const lastRunPr = await publishLastRunPr(config.name, defaultBranch, new Date(now)).catch((err) => {
|
|
4808
|
+
console.error(`[${config.name}] publishLastRunPr failed: ${err}`);
|
|
4809
|
+
return null;
|
|
4810
|
+
});
|
|
4811
|
+
if (cloud && lastRunPr === null) {
|
|
4812
|
+
console.warn(
|
|
4813
|
+
`[${config.name}] no lastRun PR to carry the cc-cloud-done label; the cloud session will be reported as completed as soon as it is created`
|
|
4814
|
+
);
|
|
4815
|
+
}
|
|
4816
|
+
if (cloud && lastRunPr !== null) {
|
|
4817
|
+
await removeLabel("pr", lastRunPr, CLOUD_DONE_LABEL).catch(
|
|
4818
|
+
(err) => console.error(`[${config.name}] removeLabel ${CLOUD_DONE_LABEL} failed for PR #${lastRunPr}: ${err}`)
|
|
4819
|
+
);
|
|
4820
|
+
await addLabel("pr", lastRunPr, LABEL_IN_PROGRESS2).catch(
|
|
4821
|
+
(err) => console.error(`[${config.name}] addLabel ${LABEL_IN_PROGRESS2} failed for PR #${lastRunPr}: ${err}`)
|
|
4822
|
+
);
|
|
4823
|
+
}
|
|
4781
4824
|
let cwd;
|
|
4782
4825
|
if (cloud) {
|
|
4783
4826
|
console.log(`[${config.name}] cloud execution, running without worktree on ${defaultBranch}`);
|
|
@@ -4804,6 +4847,13 @@ function createScheduledWorker(config) {
|
|
|
4804
4847
|
} catch (err) {
|
|
4805
4848
|
console.error(`[${config.name}] post-task error: ${err}`);
|
|
4806
4849
|
} finally {
|
|
4850
|
+
if (cloud && lastRunPr !== null) {
|
|
4851
|
+
await removeLabel("pr", lastRunPr, LABEL_IN_PROGRESS2).catch(
|
|
4852
|
+
(err) => console.error(
|
|
4853
|
+
`[${config.name}] removeLabel ${LABEL_IN_PROGRESS2} failed for PR #${lastRunPr}: ${err}`
|
|
4854
|
+
)
|
|
4855
|
+
);
|
|
4856
|
+
}
|
|
4807
4857
|
if (!cloud) {
|
|
4808
4858
|
await removeWorktree(worktreeId).catch(
|
|
4809
4859
|
(err) => console.error(`[${config.name}] removeWorktree failed: ${err}`)
|
|
@@ -4815,7 +4865,9 @@ function createScheduledWorker(config) {
|
|
|
4815
4865
|
buildClaudeEnv(mode, cloud),
|
|
4816
4866
|
execution.prompt,
|
|
4817
4867
|
cloud,
|
|
4818
|
-
|
|
4868
|
+
// 定期ワーカーは Issue/PR を起点に走らないため、クラウドの cc-cloud-done も
|
|
4869
|
+
// ローカル --debug の報告コメントも実行記録PRを置き先にする。
|
|
4870
|
+
lastRunPr !== null ? { type: "pr", number: lastRunPr } : void 0,
|
|
4819
4871
|
model
|
|
4820
4872
|
);
|
|
4821
4873
|
} catch (err) {
|
|
@@ -5297,6 +5349,9 @@ var CLOUD_SETTINGS_DEFAULTS = {
|
|
|
5297
5349
|
outputStyle: "Proactive",
|
|
5298
5350
|
language: "Japanese"
|
|
5299
5351
|
};
|
|
5352
|
+
var CLOUD_SETTINGS_ENV = {
|
|
5353
|
+
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
|
|
5354
|
+
};
|
|
5300
5355
|
function claudeSettingsPath() {
|
|
5301
5356
|
const dir = process.env.CLAUDE_CONFIG_DIR;
|
|
5302
5357
|
return join8(dir && dir.length > 0 ? dir : join8(homedir6(), ".claude"), "settings.json");
|
|
@@ -5312,6 +5367,11 @@ function withCloudDefaults(existing, force) {
|
|
|
5312
5367
|
throw new Error("`permissions` is not an object");
|
|
5313
5368
|
}
|
|
5314
5369
|
const permissions = { ...current };
|
|
5370
|
+
const currentEnv = settings["env"] ?? {};
|
|
5371
|
+
if (typeof currentEnv !== "object" || currentEnv === null || Array.isArray(currentEnv)) {
|
|
5372
|
+
throw new Error("`env` is not an object");
|
|
5373
|
+
}
|
|
5374
|
+
const env = { ...currentEnv };
|
|
5315
5375
|
let changed = false;
|
|
5316
5376
|
const set = (target, key, value) => {
|
|
5317
5377
|
if (key in target && !force) return;
|
|
@@ -5321,8 +5381,9 @@ function withCloudDefaults(existing, force) {
|
|
|
5321
5381
|
};
|
|
5322
5382
|
set(permissions, "defaultMode", CLOUD_DEFAULT_PERMISSION_MODE);
|
|
5323
5383
|
for (const [key, value] of Object.entries(CLOUD_SETTINGS_DEFAULTS)) set(settings, key, value);
|
|
5384
|
+
for (const [key, value] of Object.entries(CLOUD_SETTINGS_ENV)) set(env, key, value);
|
|
5324
5385
|
if (!changed) return null;
|
|
5325
|
-
return `${JSON.stringify({ ...settings, permissions }, null, 2)}
|
|
5386
|
+
return `${JSON.stringify({ ...settings, permissions, env }, null, 2)}
|
|
5326
5387
|
`;
|
|
5327
5388
|
}
|
|
5328
5389
|
async function writeClaudeSettings(force) {
|
|
@@ -5512,6 +5573,7 @@ Workers:
|
|
|
5512
5573
|
|
|
5513
5574
|
Options:
|
|
5514
5575
|
--project <name> Dispatch to project(s) via herdr instead of running the worker locally. Accepts a project name, a project group name, or "all". Repeatable.
|
|
5576
|
+
--debug Post each task's final report as a comment on the target Issue/PR (off by default; the report is only sent to Slack). Works in both local and --cloud runs.
|
|
5515
5577
|
--epic <number> Limit issue-based workers to sub-issues of the specified epic issue. Repeatable: any matching parent (OR).
|
|
5516
5578
|
--label <name> Limit issue-based workers to issues that also carry the specified label. Repeatable: all must be present (AND).
|
|
5517
5579
|
|
|
@@ -5650,9 +5712,7 @@ async function assertCloudAvailable() {
|
|
|
5650
5712
|
process.exit(1);
|
|
5651
5713
|
}
|
|
5652
5714
|
if (cloud) {
|
|
5653
|
-
console.log(
|
|
5654
|
-
`[worker] cloud execution enabled (--cloud); only these workers run in the cloud: ${CLOUD_ALLOWED_WORKERS.join(", ")} (all others stay local)`
|
|
5655
|
-
);
|
|
5715
|
+
console.log("[worker] cloud execution enabled (--cloud); every worker started by this process runs in the cloud");
|
|
5656
5716
|
}
|
|
5657
5717
|
}
|
|
5658
5718
|
async function assertRunPrerequisites() {
|