claude-task-worker 0.19.0 → 0.21.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 (2) hide show
  1. package/dist/index.js +172 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -572,11 +572,14 @@ async function listIssuesByNumbers(assignee, labels, excludeLabels, numbers) {
572
572
  return results;
573
573
  }
574
574
  async function findOpenPrNumberByHeadRef(headRefName) {
575
+ return findPrNumberByHeadRef(headRefName, "open");
576
+ }
577
+ async function findPrNumberByHeadRef(headRefName, state = "open") {
575
578
  const output = await execGh([
576
579
  "pr",
577
580
  "list",
578
581
  "--state",
579
- "open",
582
+ state,
580
583
  "--head",
581
584
  headRefName,
582
585
  "--json",
@@ -587,6 +590,37 @@ async function findOpenPrNumberByHeadRef(headRefName) {
587
590
  const prs = JSON.parse(output);
588
591
  return prs.length > 0 ? prs[0].number : null;
589
592
  }
593
+ async function getIssueState(issueNumber) {
594
+ const output = await execGh(["issue", "view", String(issueNumber), "--json", "state"]);
595
+ const parsed = JSON.parse(output);
596
+ return parsed.state;
597
+ }
598
+ async function findPrNumberClosingIssue(issueNumber) {
599
+ const { owner, name } = await getRepoInfo();
600
+ const query = `query($owner: String!, $name: String!, $number: Int!) {
601
+ repository(owner: $owner, name: $name) {
602
+ issue(number: $number) {
603
+ closedByPullRequestsReferences(first: 10, includeClosedPrs: true) { nodes { number state } }
604
+ }
605
+ }
606
+ }`;
607
+ const output = await execGh([
608
+ "api",
609
+ "graphql",
610
+ "-f",
611
+ `query=${query}`,
612
+ "-F",
613
+ `owner=${owner}`,
614
+ "-F",
615
+ `name=${name}`,
616
+ "-F",
617
+ `number=${issueNumber}`
618
+ ]);
619
+ const parsed = JSON.parse(output);
620
+ const nodes = parsed?.data?.repository?.issue?.closedByPullRequestsReferences?.nodes ?? [];
621
+ const validPr = nodes.find((node) => node.state === "MERGED" || node.state === "OPEN");
622
+ return validPr ? validPr.number : null;
623
+ }
590
624
  async function getIssueSubIssuesSummary(issueNumber) {
591
625
  const output = await execGh(["issue", "view", String(issueNumber), "--json", "subIssuesSummary"]);
592
626
  const parsed = JSON.parse(output);
@@ -694,6 +728,9 @@ async function removeLabel(type, number, label) {
694
728
  async function commentOnPR(prNumber, body) {
695
729
  await execGh(["pr", "comment", String(prNumber), "--body", body]);
696
730
  }
731
+ async function commentOnIssue(issueNumber, body) {
732
+ await execGh(["issue", "comment", String(issueNumber), "--body", body]);
733
+ }
697
734
  async function createLabel(name, color, force) {
698
735
  try {
699
736
  const args = ["label", "create", name];
@@ -712,6 +749,10 @@ var DISALLOWED_TOOLS = [
712
749
  // 呼ぶと処理未完のままプロセスが終了する。
713
750
  "Monitor",
714
751
  "ScheduleWakeup",
752
+ // SendMessage は過去に起動したサブエージェントをバックグラウンドでしか再開できず、
753
+ // 「完了通知を待ちます」でターンを終える誘因になる。未完のサブエージェントへの追加指示は
754
+ // フォアグラウンドの新規 Agent 起動で代替させる。
755
+ "SendMessage",
715
756
  // 対話 / 承認: 自律実行セッションには回答・承認するユーザーが存在しない。
716
757
  "AskUserQuestion",
717
758
  "EnterPlanMode",
@@ -727,6 +768,15 @@ var DISALLOWED_TOOLS = [
727
768
  "EnterWorktree"
728
769
  ];
729
770
  var DISALLOWED_TOOLS_ARG = DISALLOWED_TOOLS.join(",");
771
+ var SUBAGENT_SYSTEM_PROMPT = `\u3042\u306A\u305F\u306F claude-task-worker \u306E\u30EF\u30FC\u30AB\u30FC\u304C \`claude -p\`\uFF08\u975E\u5BFE\u8A71 print \u30E2\u30FC\u30C9\uFF09\u3067\u81EA\u52D5\u8D77\u52D5\u3057\u305F\u30BB\u30C3\u30B7\u30E7\u30F3\u5185\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3067\u3059\u3002print \u30E2\u30FC\u30C9\u306B\u306F\u518D\u8D77\u52D5\u30EB\u30FC\u30D7\u304C\u7121\u304F\u3001\u89AA\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u305F\u6642\u70B9\u3067\u30D7\u30ED\u30BB\u30B9\u304C\u6B63\u5E38\u7D42\u4E86\u3057\u307E\u3059\u3002\u3042\u306A\u305F\u304C\u51E6\u7406\u3092\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u5316\u3057\u3066\u672A\u5B8C\u306E\u307E\u307E\u5236\u5FA1\u3092\u8FD4\u3059\u3068\u3001\u5916\u5074\u306E\u30EF\u30FC\u30AB\u30FC\u304C\u300C\u6B63\u5E38\u5B8C\u4E86\u300D\u3068\u8AA4\u8A8D\u3057\u3066 GitHub Issue/PR \u306E\u30E9\u30D9\u30EB\u72B6\u614B\u304C\u58CA\u308C\u307E\u3059\u3002\u4EE5\u4E0B\u306E\u30EB\u30FC\u30EB\u3092\u5FC5\u305A\u9075\u5B88\u3057\u3066\u304F\u3060\u3055\u3044\u3002
772
+
773
+ - \`Bash\` \u3092 \`run_in_background: true\` \u3067\u547C\u3070\u306A\u3044\u3002\u30B3\u30DE\u30F3\u30C9\u672B\u5C3E\u306B \`&\` \u3092\u4ED8\u3051\u305A\u3001\`nohup\` / \`disown\` / \`setsid\` \u3067\u30D7\u30ED\u30BB\u30B9\u3092\u30C7\u30BF\u30C3\u30C1\u3057\u306A\u3044
774
+ - \`Agent\` \u30C4\u30FC\u30EB\u306F\u65E2\u5B9A\u304C\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u5B9F\u884C\u306E\u305F\u3081\u3001\u547C\u3073\u51FA\u3057\u3054\u3068\u306B\u5FC5\u305A \`run_in_background: false\` \u3092\u660E\u793A\u3057\u3001\u30D5\u30A9\u30A2\u30B0\u30E9\u30A6\u30F3\u30C9\u3067\u5B8C\u4E86\u3092\u5F85\u3063\u3066\u304B\u3089\u6B21\u3078\u9032\u3080
775
+ - \`Skill\` \u30C4\u30FC\u30EB\u306B \`run_in_background: true\` \u3092\u6307\u5B9A\u3057\u306A\u3044
776
+ - \`Monitor\` / \`ScheduleWakeup\` / \`SendMessage\` \u306A\u3069\u3067\u51E6\u7406\u3092\u5F8C\u56DE\u3057\u306B\u3057\u306A\u3044\u3002\u300C\u5B8C\u4E86\u901A\u77E5\u3092\u5F85\u3061\u307E\u3059\u300D\u306E\u3088\u3046\u306A\u901A\u77E5\u5F85\u3061\u72B6\u614B\u3067\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u306A\u3044
777
+ - E2E \u30C6\u30B9\u30C8\u306E\u3088\u3046\u306B\u6642\u9593\u306E\u304B\u304B\u308B\u51E6\u7406\u3082\u3001\u540C\u671F\u5B9F\u884C\u3067\u5B8C\u4E86\u3092\u5F85\u3064\u306E\u304C\u6B63\u3057\u3044\u6319\u52D5\u3067\u3042\u308A\u3001\u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u5316\u3057\u3066\u5F85\u305F\u305A\u306B\u9032\u3093\u3067\u306F\u306A\u3089\u306A\u3044
778
+ - \u30D0\u30C3\u30AF\u30B0\u30E9\u30A6\u30F3\u30C9\u30BF\u30B9\u30AF\u304C\u6B8B\u3063\u305F\u72B6\u614B\u3067\u5B8C\u4E86\u5831\u544A\u3057\u3066\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u306A\u3044\u3002\u672A\u5B8C\u306E\u51E6\u7406\u306F\u30D5\u30A9\u30A2\u30B0\u30E9\u30A6\u30F3\u30C9\u3067\u5B8C\u4E86\u307E\u3067\u5B9F\u884C\u3057\u5207\u3063\u3066\u304B\u3089\u5831\u544A\u3059\u308B
779
+ - \u30E6\u30FC\u30B6\u30FC\u3078\u306E\u78BA\u8A8D\u30FB\u8CEA\u554F\u306F\u884C\u308F\u306A\u3044\uFF08\u56DE\u7B54\u3059\u308B\u30E6\u30FC\u30B6\u30FC\u306F\u5B58\u5728\u3057\u306A\u3044\uFF09\u3002\u66D6\u6627\u306A\u5834\u5408\u306F\u7834\u58CA\u7684\u3067\u306A\u3044\u5074\u3092\u9078\u3073\u3001\u305D\u306E\u5224\u65AD\u3068\u6839\u62E0\u3092\u6700\u7D42\u5831\u544A\u306B\u660E\u8A18\u3059\u308B`;
730
780
 
731
781
  // src/config.ts
732
782
  import { readFileSync } from "node:fs";
@@ -972,8 +1022,32 @@ async function assertRemoteTrackingExists(epicBranch) {
972
1022
  // src/process-manager.ts
973
1023
  import { spawn } from "node:child_process";
974
1024
  init_table();
975
- var childProcesses = /* @__PURE__ */ new Map();
1025
+
1026
+ // src/task-result.ts
976
1027
  var TASK_TIMEOUT_MS = 90 * 60 * 1e3;
1028
+ var STDERR_TAIL_LIMIT = 8 * 1024;
1029
+ function buildTaskResult(code, timedOut, stdout, stderrTail) {
1030
+ const emptyOutput = stdout.trim() === "";
1031
+ const completed = !timedOut && code === 0 && !emptyOutput;
1032
+ let output = stdout;
1033
+ if (timedOut) {
1034
+ output += `
1035
+ [worker] task timed out after ${TASK_TIMEOUT_MS / 1e3}s`;
1036
+ } else if (code === 0 && emptyOutput) {
1037
+ output += "[worker] claude exited with code 0 but produced no output (session aborted before the model ran; e.g. a skill preamble command failed)";
1038
+ } else if (!completed) {
1039
+ output += `
1040
+ [worker] claude exited with code ${code}`;
1041
+ }
1042
+ if (!completed && stderrTail.trim() !== "") {
1043
+ output += `
1044
+ [stderr] ${stderrTail.trim()}`;
1045
+ }
1046
+ return { status: completed ? "completed" : "failed", output };
1047
+ }
1048
+
1049
+ // src/process-manager.ts
1050
+ var childProcesses = /* @__PURE__ */ new Map();
977
1051
  var tasks = /* @__PURE__ */ new Map();
978
1052
  var shuttingDown = false;
979
1053
  function setShuttingDown() {
@@ -1105,11 +1179,20 @@ function run(command, args, id, title, workerName, path, onComplete, cwd) {
1105
1179
  ...cwd ? { cwd } : {}
1106
1180
  });
1107
1181
  childProcesses.set(id, child);
1108
- child.stderr?.resume();
1109
1182
  const outputChunks = [];
1110
1183
  child.stdout?.on("data", (chunk) => {
1111
1184
  outputChunks.push(chunk);
1112
1185
  });
1186
+ const stderrChunks = [];
1187
+ let stderrLen = 0;
1188
+ child.stderr?.on("data", (chunk) => {
1189
+ stderrChunks.push(chunk);
1190
+ stderrLen += chunk.length;
1191
+ while (stderrChunks.length > 1 && stderrLen - stderrChunks[0].length >= STDERR_TAIL_LIMIT) {
1192
+ stderrLen -= stderrChunks[0].length;
1193
+ stderrChunks.shift();
1194
+ }
1195
+ });
1113
1196
  let timedOut = false;
1114
1197
  const timeoutHandle = setTimeout(() => {
1115
1198
  timedOut = true;
@@ -1128,9 +1211,12 @@ function run(command, args, id, title, workerName, path, onComplete, cwd) {
1128
1211
  timeoutHandle.unref();
1129
1212
  child.on("close", async (code) => {
1130
1213
  clearTimeout(timeoutHandle);
1131
- const output = Buffer.concat(outputChunks).toString("utf-8") + (timedOut ? `
1132
- [worker] task timed out after ${TASK_TIMEOUT_MS / 1e3}s` : "");
1133
- const finalStatus = !timedOut && code === 0 ? "completed" : "failed";
1214
+ const { status: finalStatus, output } = buildTaskResult(
1215
+ code,
1216
+ timedOut,
1217
+ Buffer.concat(outputChunks).toString("utf-8"),
1218
+ Buffer.concat(stderrChunks).toString("utf-8").slice(-STDERR_TAIL_LIMIT)
1219
+ );
1134
1220
  try {
1135
1221
  await Promise.race([
1136
1222
  onComplete?.(finalStatus, output) ?? Promise.resolve(),
@@ -1651,6 +1737,14 @@ async function deleteLocalBranch(branchName) {
1651
1737
  console.error(`[worktree] Failed to delete local branch ${branchName}: ${stderr.trim()}`);
1652
1738
  }
1653
1739
  }
1740
+ async function localBranchExists(branchName) {
1741
+ try {
1742
+ await execFileAsync2("git", ["rev-parse", "--verify", "--quiet", `refs/heads/${branchName}`]);
1743
+ return true;
1744
+ } catch {
1745
+ return false;
1746
+ }
1747
+ }
1654
1748
  function getWorktreePath(worktreeId) {
1655
1749
  return `${WORKTREES_DIR}/${worktreeId}`;
1656
1750
  }
@@ -1778,6 +1872,8 @@ function createIssuePollingWorker(config) {
1778
1872
  "--dangerously-skip-permissions",
1779
1873
  "--disallowedTools",
1780
1874
  DISALLOWED_TOOLS_ARG,
1875
+ "--append-subagent-system-prompt",
1876
+ SUBAGENT_SYSTEM_PROMPT,
1781
1877
  "--model",
1782
1878
  model,
1783
1879
  "--effort",
@@ -1814,13 +1910,20 @@ function createIssuePollingWorker(config) {
1814
1910
  }
1815
1911
  try {
1816
1912
  if (status === "completed") {
1817
- await config.onCompleted?.(issue.number);
1818
- await notifyTaskCompleted(config.name, name, issue.number, issue.title, issueUrl, output);
1913
+ const verified = await config.onCompleted?.(issue.number, worktreeId) ?? true;
1914
+ if (verified === false) {
1915
+ await notifyTaskFailed(config.name, name, issue.number, issue.title, issueUrl, output);
1916
+ } else {
1917
+ await notifyTaskCompleted(config.name, name, issue.number, issue.title, issueUrl, output);
1918
+ }
1819
1919
  } else {
1820
1920
  await notifyTaskFailed(config.name, name, issue.number, issue.title, issueUrl, output);
1821
1921
  }
1822
1922
  } catch (err) {
1823
1923
  console.error(`[${config.name}] post-task error for #${issue.number}: ${err}`);
1924
+ await notifyTaskFailed(config.name, name, issue.number, issue.title, issueUrl, output).catch(
1925
+ (notifyErr) => console.error(`[${config.name}] notifyTaskFailed failed for #${issue.number}: ${notifyErr}`)
1926
+ );
1824
1927
  } finally {
1825
1928
  await removeLabel("issue", issue.number, "cc-in-progress").catch(
1826
1929
  (err) => console.error(`[${config.name}] removeLabel cc-in-progress failed for #${issue.number}: ${err}`)
@@ -1852,18 +1955,47 @@ function createIssuePollingWorker(config) {
1852
1955
  }
1853
1956
 
1854
1957
  // src/workers/exec-issue.ts
1958
+ function prMissingComment(worktreeId) {
1959
+ return [
1960
+ "## PR\u672A\u4F5C\u6210\u306E\u307E\u307E\u81EA\u52D5\u5B9F\u884C\u304C\u7D42\u4E86\u3057\u307E\u3057\u305F\uFF08\u8981\u4EBA\u624B\u78BA\u8A8D\uFF09",
1961
+ `exec-issue \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u6B63\u5E38\u7D42\u4E86\uFF08exit 0\uFF09\u3057\u307E\u3057\u305F\u304C\u3001\u3053\u306E\u5B9F\u884C\u306E\u4F5C\u696D\u30D6\u30E9\u30F3\u30C1\uFF08\`${worktreeId}\`\uFF09\u3092 head \u3068\u3059\u308BPR\u3082\u3001\u672CIssue\u3092 closing \u53C2\u7167\u3059\u308BPR\u3082\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002PR\u4F5C\u6210\u524D\u306B\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u7D42\u4E86\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002`,
1962
+ "",
1963
+ "## \u72B6\u614B\u306E\u78BA\u8A8D",
1964
+ `- \u5909\u66F4\u304C push \u6E08\u307F\u306E\u5834\u5408\u306F\u30EA\u30E2\u30FC\u30C8\u30D6\u30E9\u30F3\u30C1 \`${worktreeId}\` \u304C\u6B8B\u3063\u3066\u3044\u307E\u3059\u3002\u5185\u5BB9\u3092\u78BA\u8A8D\u3057\u3001\u5FC5\u8981\u306A\u3089\u624B\u52D5\u3067PR\u3092\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044`,
1965
+ "",
1966
+ "## \u5BFE\u5FDC\u5F8C\u306E\u9032\u3081\u65B9",
1967
+ "- \u81EA\u52D5\u5B9F\u884C\u3092\u3084\u308A\u76F4\u3059\u5834\u5408: `cc-need-human-check` \u30E9\u30D9\u30EB\u3092\u5916\u3057\u3001`cc-exec-issue` \u30E9\u30D9\u30EB\u3092\u4ED8\u3051\u76F4\u3057\u3066\u304F\u3060\u3055\u3044",
1968
+ "- \u624B\u52D5\u3067PR\u3092\u4F5C\u6210\u3057\u305F\u5834\u5408\u306A\u3069\u5BFE\u5FDC\u6E08\u307F\u306E\u5834\u5408: `cc-need-human-check` \u30E9\u30D9\u30EB\u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044"
1969
+ ].join("\n");
1970
+ }
1855
1971
  var execIssueWorker = (opts = {}) => createIssuePollingWorker({
1856
1972
  name: "exec-issue",
1857
1973
  command: "/claude-task-worker:exec-issue",
1858
1974
  triggerLabels: ["cc-exec-issue"],
1859
1975
  epicFilters: opts.epicFilters,
1860
1976
  labelFilters: opts.labelFilters,
1861
- onCompleted: async (issueNumber) => {
1977
+ onCompleted: async (issueNumber, worktreeId) => {
1862
1978
  if (await hasLabel("issue", issueNumber, "cc-need-human-check")) {
1863
1979
  console.log(`[exec-issue] #${issueNumber}: cc-need-human-check present, skip cc-pr-created`);
1980
+ return false;
1981
+ }
1982
+ if (await getIssueState(issueNumber) === "CLOSED") {
1983
+ console.log(`[exec-issue] #${issueNumber}: issue closed by skill (no-change path), skip cc-pr-created`);
1864
1984
  return;
1865
1985
  }
1866
- await addLabel("issue", issueNumber, "cc-pr-created");
1986
+ const prNumber = await findPrNumberByHeadRef(worktreeId, "all") ?? await findPrNumberClosingIssue(issueNumber);
1987
+ if (prNumber !== null) {
1988
+ await addLabel("issue", issueNumber, "cc-pr-created");
1989
+ return;
1990
+ }
1991
+ console.error(
1992
+ `[exec-issue] #${issueNumber}: session exited without a PR (branch: ${worktreeId}); marking cc-need-human-check`
1993
+ );
1994
+ await addLabel("issue", issueNumber, "cc-need-human-check");
1995
+ await commentOnIssue(issueNumber, prMissingComment(worktreeId)).catch(
1996
+ (err) => console.error(`[exec-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
1997
+ );
1998
+ return false;
1867
1999
  }
1868
2000
  })();
1869
2001
 
@@ -1897,6 +2029,14 @@ function createPrPollingWorker(config) {
1897
2029
  try {
1898
2030
  await removeWorktreeByBranch(pr.headRefName);
1899
2031
  await deleteLocalBranch(pr.headRefName);
2032
+ if (await localBranchExists(pr.headRefName)) {
2033
+ console.error(
2034
+ `[${config.name}] PR #${pr.number}: branch ${pr.headRefName} is still checked out by another worktree; skipping this tick`
2035
+ );
2036
+ await removeLabel("pr", pr.number, LABEL_IN_PROGRESS).catch(() => {
2037
+ });
2038
+ continue;
2039
+ }
1900
2040
  syncDefaultBranch(defaultBranch);
1901
2041
  await createWorktreeFromBranch(worktreeId, defaultBranch);
1902
2042
  const cwd = getWorktreePath(worktreeId);
@@ -1910,6 +2050,8 @@ function createPrPollingWorker(config) {
1910
2050
  "--dangerously-skip-permissions",
1911
2051
  "--disallowedTools",
1912
2052
  DISALLOWED_TOOLS_ARG,
2053
+ "--append-subagent-system-prompt",
2054
+ SUBAGENT_SYSTEM_PROMPT,
1913
2055
  "--model",
1914
2056
  model,
1915
2057
  "--effort",
@@ -2074,6 +2216,19 @@ var checkDependabotWorker = createPrPollingWorker({
2074
2216
  });
2075
2217
 
2076
2218
  // src/workers/epic-issue.ts
2219
+ function epicPrMissingComment(issueNumber) {
2220
+ return [
2221
+ "## Epic PR\u672A\u4F5C\u6210\u306E\u307E\u307E\u81EA\u52D5\u5B9F\u884C\u304C\u7D42\u4E86\u3057\u307E\u3057\u305F\uFF08\u8981\u4EBA\u624B\u78BA\u8A8D\uFF09",
2222
+ `create-epic-pr \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u6B63\u5E38\u7D42\u4E86\uFF08exit 0\uFF09\u3057\u307E\u3057\u305F\u304C\u3001Epic \u30D6\u30E9\u30F3\u30C1 \`cc-epic-${issueNumber}\` \u3092 head \u3068\u3059\u308B\u30AA\u30FC\u30D7\u30F3\u306APR\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002PR\u4F5C\u6210\u524D\u306B\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u7D42\u4E86\u3057\u305F\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002`,
2223
+ "",
2224
+ "## \u72B6\u614B\u306E\u78BA\u8A8D",
2225
+ `- \u5909\u66F4\u304C push \u6E08\u307F\u306E\u5834\u5408\u306F\u30EA\u30E2\u30FC\u30C8\u30D6\u30E9\u30F3\u30C1 \`cc-epic-${issueNumber}\` \u304C\u6B8B\u3063\u3066\u3044\u307E\u3059\u3002\u5185\u5BB9\u3092\u78BA\u8A8D\u3057\u3001\u5FC5\u8981\u306A\u3089\u624B\u52D5\u3067PR\u3092\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044`,
2226
+ "",
2227
+ "## \u5BFE\u5FDC\u5F8C\u306E\u9032\u3081\u65B9",
2228
+ "- \u81EA\u52D5\u5B9F\u884C\u3092\u3084\u308A\u76F4\u3059\u5834\u5408: `cc-need-human-check` \u30E9\u30D9\u30EB\u3092\u5916\u3057\u3001`cc-epic-issue` \u30E9\u30D9\u30EB\u3092\u4ED8\u3051\u76F4\u3057\u3066\u304F\u3060\u3055\u3044",
2229
+ "- \u624B\u52D5\u3067PR\u3092\u4F5C\u6210\u3057\u305F\u5834\u5408\u306A\u3069\u5BFE\u5FDC\u6E08\u307F\u306E\u5834\u5408: `cc-need-human-check` \u30E9\u30D9\u30EB\u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044"
2230
+ ].join("\n");
2231
+ }
2077
2232
  var epicIssueWorker = (opts = {}) => createIssuePollingWorker({
2078
2233
  name: "epic-issue",
2079
2234
  command: "/claude-task-worker:create-epic-pr",
@@ -2088,12 +2243,16 @@ var epicIssueWorker = (opts = {}) => createIssuePollingWorker({
2088
2243
  return "proceed";
2089
2244
  },
2090
2245
  onCompleted: async (issueNumber) => {
2091
- await addLabel("issue", issueNumber, "cc-pr-created");
2092
2246
  const prNumber = await findOpenPrNumberByHeadRef(`cc-epic-${issueNumber}`);
2093
2247
  if (prNumber === null) {
2094
- console.error(`[epic-issue] Epic PR for branch cc-epic-${issueNumber} not found; skip labeling`);
2095
- return;
2248
+ console.error(`[epic-issue] Epic PR for branch cc-epic-${issueNumber} not found; skip cc-pr-created`);
2249
+ await addLabel("issue", issueNumber, "cc-need-human-check");
2250
+ await commentOnIssue(issueNumber, epicPrMissingComment(issueNumber)).catch(
2251
+ (err) => console.error(`[epic-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
2252
+ );
2253
+ return false;
2096
2254
  }
2255
+ await addLabel("issue", issueNumber, "cc-pr-created");
2097
2256
  await addLabel("pr", prNumber, "cc-epic-issue");
2098
2257
  await addLabel("pr", prNumber, "cc-triage-scope");
2099
2258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-worker",
3
- "version": "0.19.0",
3
+ "version": "0.21.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",