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.
Files changed (3) hide show
  1. package/README.md +14 -1
  2. package/dist/index.js +109 -49
  3. 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
- var CLOUD_ALLOWED_WORKERS = ["exec-issue", "fix-review-point"];
1921
- function isCloudWorker(name) {
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 = `\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`;
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
- const number = Number(key.split(":")[1]);
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, type) {
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, id, reason, cloudSessionId) {
2929
- await addLabel(target, id, "cc-need-human-check").catch((err) => {
2930
- console.error(`[worker] failed to add cc-need-human-check to ${target} #${id}: ${err}`);
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 = target === "issue" ? commentOnIssue : commentOnPR;
2963
+ const commentFn = type === "issue" ? commentOnIssue : commentOnPR;
2942
2964
  await commentFn(id, body).catch((err) => {
2943
- console.error(`[worker] failed to comment on ${target} #${id} about the orphaned cloud session: ${err}`);
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 initialPrompt = buildCloudPrompt(
2996
- prompt,
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(cloudTarget, id, CLOUD_DONE_LABEL).catch((err) => {
3012
- console.error(`[worker] failed to remove ${CLOUD_DONE_LABEL} from ${cloudTarget} #${id}: ${err}`);
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(id, startedAt, CLOUD_REPORT_HEADING);
3038
+ reportBody = await findCommentSince(targetNumber, startedAt, CLOUD_REPORT_HEADING);
3019
3039
  } catch (err) {
3020
- console.error(`[worker] failed to fetch the cloud report comment for ${cloudTarget} #${id}: ${err}`);
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(cloudTarget, id, "cc-need-human-check").catch((err) => {
3030
- console.error(`[worker] failed to add cc-need-human-check to ${cloudTarget} #${id}: ${err}`);
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, id, "shutdown", cloudSessionId);
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, id, reason, cloudSessionId);
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, onComplete, cwd, env);
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, onComplete);
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 }, onComplete);
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
- cloud ? "issue" : void 0,
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
- isCloud ? "pr" : void 0,
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
- (err) => console.error(`[${config.name}] publishLastRunPr failed: ${err}`)
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
- void 0,
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() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-worker",
3
- "version": "0.106.0",
3
+ "version": "0.108.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",