claude-task-worker 0.95.0 → 0.97.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 +68 -13
- package/dist/index.js +1125 -533
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -587,12 +587,14 @@ __export(herdr_runner_exports, {
|
|
|
587
587
|
PROMPT_ACCEPT_TIMEOUT_MS: () => PROMPT_ACCEPT_TIMEOUT_MS,
|
|
588
588
|
buildHerdrTaskResult: () => buildHerdrTaskResult,
|
|
589
589
|
createCompletionTracker: () => createCompletionTracker,
|
|
590
|
+
extractCloudSessionId: () => extractCloudSessionId,
|
|
590
591
|
observeAgentStatus: () => observeAgentStatus,
|
|
591
592
|
startHerdrTask: () => startHerdrTask,
|
|
592
593
|
stopHerdrTask: () => stopHerdrTask,
|
|
593
594
|
taskTabLabel: () => taskTabLabel,
|
|
594
595
|
toAgentName: () => toAgentName,
|
|
595
|
-
waitForHerdrTask: () => waitForHerdrTask
|
|
596
|
+
waitForHerdrTask: () => waitForHerdrTask,
|
|
597
|
+
waitForPaneReady: () => waitForPaneReady
|
|
596
598
|
});
|
|
597
599
|
async function loadHerdr() {
|
|
598
600
|
return await Promise.resolve().then(() => (init_herdr(), herdr_exports));
|
|
@@ -624,6 +626,9 @@ function observeAgentStatus(tracker, status) {
|
|
|
624
626
|
}
|
|
625
627
|
return { tracker, decision: "running" };
|
|
626
628
|
}
|
|
629
|
+
function extractCloudSessionId(text) {
|
|
630
|
+
return CLOUD_SESSION_URL_RE.exec(text)?.[1] ?? CLOUD_SESSION_CREATED_RE.exec(text)?.[1];
|
|
631
|
+
}
|
|
627
632
|
function buildHerdrTaskResult(paneOutput, options) {
|
|
628
633
|
const report = options?.report?.trim() ?? "";
|
|
629
634
|
if (report !== "") {
|
|
@@ -801,13 +806,15 @@ async function stopHerdrTask(task, herdr, options) {
|
|
|
801
806
|
function sleep(ms) {
|
|
802
807
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
803
808
|
}
|
|
804
|
-
var AGENT_POLL_INTERVAL_MS, PANE_OUTPUT_LINES, PANE_READY_TIMEOUT_MS, PANE_READY_POLL_INTERVAL_MS, PROMPT_ACCEPT_TIMEOUT_MS, PROMPT_ACCEPT_POLL_INTERVAL_MS, CLAUDE_EXIT_TIMEOUT_MS, CLAUDE_EXIT_POLL_INTERVAL_MS;
|
|
809
|
+
var AGENT_POLL_INTERVAL_MS, PANE_OUTPUT_LINES, CLOUD_SESSION_URL_RE, CLOUD_SESSION_CREATED_RE, PANE_READY_TIMEOUT_MS, PANE_READY_POLL_INTERVAL_MS, PROMPT_ACCEPT_TIMEOUT_MS, PROMPT_ACCEPT_POLL_INTERVAL_MS, CLAUDE_EXIT_TIMEOUT_MS, CLAUDE_EXIT_POLL_INTERVAL_MS;
|
|
805
810
|
var init_herdr_runner = __esm({
|
|
806
811
|
"src/herdr-runner.ts"() {
|
|
807
812
|
"use strict";
|
|
808
813
|
init_transcript();
|
|
809
814
|
AGENT_POLL_INTERVAL_MS = 3 * 1e3;
|
|
810
815
|
PANE_OUTPUT_LINES = 300;
|
|
816
|
+
CLOUD_SESSION_URL_RE = /https:\/\/claude\.ai\/code\/([A-Za-z0-9_-]+)/;
|
|
817
|
+
CLOUD_SESSION_CREATED_RE = /Created cloud session:\s*(session_[A-Za-z0-9_-]+)/;
|
|
811
818
|
PANE_READY_TIMEOUT_MS = 30 * 1e3;
|
|
812
819
|
PANE_READY_POLL_INTERVAL_MS = 200;
|
|
813
820
|
PROMPT_ACCEPT_TIMEOUT_MS = 20 * 1e3;
|
|
@@ -1423,12 +1430,12 @@ async function getIssueState(issueNumber) {
|
|
|
1423
1430
|
const parsed = JSON.parse(output);
|
|
1424
1431
|
return parsed.state;
|
|
1425
1432
|
}
|
|
1426
|
-
async function
|
|
1433
|
+
async function listPrsClosingIssue(issueNumber) {
|
|
1427
1434
|
const { owner, name } = await getRepoInfo();
|
|
1428
1435
|
const query = `query($owner: String!, $name: String!, $number: Int!) {
|
|
1429
1436
|
repository(owner: $owner, name: $name) {
|
|
1430
1437
|
issue(number: $number) {
|
|
1431
|
-
closedByPullRequestsReferences(first: 10, includeClosedPrs: true) { nodes { number state headRefName } }
|
|
1438
|
+
closedByPullRequestsReferences(first: 10, includeClosedPrs: true) { nodes { number state headRefName baseRefName createdAt } }
|
|
1432
1439
|
}
|
|
1433
1440
|
}
|
|
1434
1441
|
}`;
|
|
@@ -1446,10 +1453,13 @@ async function findPrNumberClosingIssue(issueNumber, expectedHeadRefName) {
|
|
|
1446
1453
|
]);
|
|
1447
1454
|
const parsed = JSON.parse(output);
|
|
1448
1455
|
const nodes = parsed?.data?.repository?.issue?.closedByPullRequestsReferences?.nodes ?? [];
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1456
|
+
return nodes.map((node) => ({
|
|
1457
|
+
number: node.number,
|
|
1458
|
+
state: node.state ?? "",
|
|
1459
|
+
headRefName: node.headRefName ?? "",
|
|
1460
|
+
baseRefName: node.baseRefName ?? "",
|
|
1461
|
+
createdAt: node.createdAt ?? ""
|
|
1462
|
+
}));
|
|
1453
1463
|
}
|
|
1454
1464
|
async function getIssueSubIssuesSummary(issueNumber) {
|
|
1455
1465
|
const output = await execGh(["issue", "view", String(issueNumber), "--json", "subIssuesSummary"]);
|
|
@@ -1543,6 +1553,31 @@ async function hasLabel(type, number, label) {
|
|
|
1543
1553
|
return labels.some((l) => l.name === label);
|
|
1544
1554
|
});
|
|
1545
1555
|
}
|
|
1556
|
+
async function findCommentSince(number, since, heading) {
|
|
1557
|
+
const output = await execGh(["api", `repos/{owner}/{repo}/issues/${number}/comments?since=${since.toISOString()}`]);
|
|
1558
|
+
const comments = JSON.parse(output);
|
|
1559
|
+
const matched = comments.filter((comment) => comment.body.split("\n").some((line) => line.trim() === heading));
|
|
1560
|
+
if (matched.length === 0) {
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
return matched[matched.length - 1].body;
|
|
1564
|
+
}
|
|
1565
|
+
async function listNumbersWithLabel(type, label, limit = 50) {
|
|
1566
|
+
const output = await execGh([
|
|
1567
|
+
type,
|
|
1568
|
+
"list",
|
|
1569
|
+
"--label",
|
|
1570
|
+
label,
|
|
1571
|
+
"--state",
|
|
1572
|
+
"all",
|
|
1573
|
+
"--json",
|
|
1574
|
+
"number",
|
|
1575
|
+
"--limit",
|
|
1576
|
+
String(limit)
|
|
1577
|
+
]);
|
|
1578
|
+
const parsed = JSON.parse(output);
|
|
1579
|
+
return parsed.map((entry) => entry.number);
|
|
1580
|
+
}
|
|
1546
1581
|
async function hasOpenBlockers(issueNumber) {
|
|
1547
1582
|
return withRetry(async () => {
|
|
1548
1583
|
const output = await execGh(["issue", "view", String(issueNumber), "--json", "blockedBy"]);
|
|
@@ -1591,369 +1626,71 @@ async function createLabel(name, color, force) {
|
|
|
1591
1626
|
}
|
|
1592
1627
|
|
|
1593
1628
|
// src/claude-args.ts
|
|
1594
|
-
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
1629
|
+
import { mkdirSync, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1595
1630
|
import os from "node:os";
|
|
1596
1631
|
import path from "node:path";
|
|
1597
1632
|
|
|
1598
|
-
// src/
|
|
1599
|
-
import { readFileSync,
|
|
1600
|
-
import {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
var
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
];
|
|
1613
|
-
var DEFAULT_PERMISSION_MODE = "bypassPermissions";
|
|
1614
|
-
var UserConfigError = class extends Error {
|
|
1615
|
-
constructor(message) {
|
|
1616
|
-
super(message);
|
|
1617
|
-
this.name = "UserConfigError";
|
|
1618
|
-
}
|
|
1619
|
-
};
|
|
1620
|
-
function getConfigDir() {
|
|
1621
|
-
const xdg = process.env.XDG_CONFIG_HOME;
|
|
1622
|
-
const configHome = xdg && xdg.length > 0 ? xdg : join(homedir(), ".config");
|
|
1623
|
-
return join(configHome, "claude-task-worker");
|
|
1624
|
-
}
|
|
1625
|
-
function getUserConfigPath() {
|
|
1626
|
-
return join(getConfigDir(), "config.json");
|
|
1627
|
-
}
|
|
1628
|
-
function isDirectory(path2) {
|
|
1629
|
-
try {
|
|
1630
|
-
return statSync(path2).isDirectory();
|
|
1631
|
-
} catch {
|
|
1632
|
-
return false;
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
function parseConfigFile(path2) {
|
|
1636
|
-
let content;
|
|
1637
|
-
try {
|
|
1638
|
-
content = readFileSync(path2, "utf-8");
|
|
1639
|
-
} catch (err) {
|
|
1640
|
-
if (err.code === "ENOENT") return void 0;
|
|
1641
|
-
throw err;
|
|
1642
|
-
}
|
|
1643
|
-
try {
|
|
1644
|
-
return JSON.parse(content);
|
|
1645
|
-
} catch (err) {
|
|
1646
|
-
if (err instanceof SyntaxError) {
|
|
1647
|
-
throw new UserConfigError(`config file contains invalid JSON: ${path2}: ${err.message}`);
|
|
1633
|
+
// src/config.ts
|
|
1634
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
1635
|
+
import { isAbsolute, join, normalize, sep as SEP } from "node:path";
|
|
1636
|
+
|
|
1637
|
+
// src/dispatch-args.ts
|
|
1638
|
+
var FLAG_INCOMPATIBLE_COMMANDS = ["init", "install", "update", "usage", "version"];
|
|
1639
|
+
function collectFlagValues(argv, flag) {
|
|
1640
|
+
const values = [];
|
|
1641
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1642
|
+
if (argv[i] !== flag) continue;
|
|
1643
|
+
const raw = argv[i + 1];
|
|
1644
|
+
if (!raw || raw.startsWith("--")) {
|
|
1645
|
+
console.error(`[dispatcher] ${flag} requires a value`);
|
|
1646
|
+
process.exit(1);
|
|
1648
1647
|
}
|
|
1649
|
-
|
|
1648
|
+
values.push(raw);
|
|
1650
1649
|
}
|
|
1650
|
+
return values;
|
|
1651
1651
|
}
|
|
1652
|
-
function
|
|
1653
|
-
return
|
|
1654
|
-
}
|
|
1655
|
-
function parseMode(raw, path2) {
|
|
1656
|
-
if (!("mode" in raw)) return DEFAULT_RUN_MODE;
|
|
1657
|
-
const value = raw["mode"];
|
|
1658
|
-
if (value === "default" || value === "herdr") return value;
|
|
1659
|
-
console.warn(`[config] invalid mode: ${JSON.stringify(value)} in ${path2}, using "${DEFAULT_RUN_MODE}"`);
|
|
1660
|
-
return DEFAULT_RUN_MODE;
|
|
1661
|
-
}
|
|
1662
|
-
function parseAdvisor(raw, path2) {
|
|
1663
|
-
if (!("advisor" in raw)) return DEFAULT_ADVISOR_ENABLED;
|
|
1664
|
-
const value = raw["advisor"];
|
|
1665
|
-
if (typeof value === "boolean") return value;
|
|
1666
|
-
console.warn(`[config] invalid advisor: ${JSON.stringify(value)} in ${path2}, using ${DEFAULT_ADVISOR_ENABLED}`);
|
|
1667
|
-
return DEFAULT_ADVISOR_ENABLED;
|
|
1668
|
-
}
|
|
1669
|
-
function parsePermission(raw, path2) {
|
|
1670
|
-
if (!("permission" in raw)) return DEFAULT_PERMISSION_MODE;
|
|
1671
|
-
const value = raw["permission"];
|
|
1672
|
-
if (typeof value === "string" && PERMISSION_MODES.includes(value)) return value;
|
|
1673
|
-
console.warn(`[config] invalid permission: ${JSON.stringify(value)} in ${path2}, using "${DEFAULT_PERMISSION_MODE}"`);
|
|
1674
|
-
return DEFAULT_PERMISSION_MODE;
|
|
1675
|
-
}
|
|
1676
|
-
function loadUserConfig() {
|
|
1677
|
-
const path2 = getUserConfigPath();
|
|
1678
|
-
const raw = readRawConfig();
|
|
1679
|
-
if (raw === void 0) {
|
|
1680
|
-
throw new UserConfigError(`config.json not found: ${path2}`);
|
|
1681
|
-
}
|
|
1682
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1683
|
-
throw new UserConfigError(`config.json must contain a JSON object: ${path2}`);
|
|
1684
|
-
}
|
|
1685
|
-
if (!("projects" in raw) || typeof raw["projects"] !== "object" || raw["projects"] === null || Array.isArray(raw["projects"])) {
|
|
1686
|
-
throw new UserConfigError(`config.json must contain a "projects" section as an object: ${path2}`);
|
|
1687
|
-
}
|
|
1688
|
-
if ("projectGroups" in raw && (typeof raw["projectGroups"] !== "object" || raw["projectGroups"] === null || Array.isArray(raw["projectGroups"]))) {
|
|
1689
|
-
throw new UserConfigError(`config.json "projectGroups" must be an object: ${path2}`);
|
|
1690
|
-
}
|
|
1691
|
-
const mode = parseMode(raw, path2);
|
|
1692
|
-
const advisor = parseAdvisor(raw, path2);
|
|
1693
|
-
const permission = parsePermission(raw, path2);
|
|
1694
|
-
const rawProjects = raw["projects"];
|
|
1695
|
-
const rawProjectGroups = "projectGroups" in raw ? raw["projectGroups"] : {};
|
|
1696
|
-
const projectKeys = Object.keys(rawProjects);
|
|
1697
|
-
const groupKeys = Object.keys(rawProjectGroups);
|
|
1698
|
-
if (projectKeys.includes(RESERVED_ALL) || groupKeys.includes(RESERVED_ALL)) {
|
|
1699
|
-
throw new UserConfigError(
|
|
1700
|
-
`"${RESERVED_ALL}" is a reserved word and cannot be used as a key in "projects" or "projectGroups"`
|
|
1701
|
-
);
|
|
1702
|
-
}
|
|
1703
|
-
const groupKeySet = new Set(groupKeys);
|
|
1704
|
-
const duplicateKeys = projectKeys.filter((key) => groupKeySet.has(key));
|
|
1705
|
-
if (duplicateKeys.length > 0) {
|
|
1706
|
-
throw new UserConfigError(
|
|
1707
|
-
`"projects" and "projectGroups" share the same key namespace; duplicate keys are not allowed: ${duplicateKeys.join(", ")}`
|
|
1708
|
-
);
|
|
1709
|
-
}
|
|
1710
|
-
const projects = {};
|
|
1711
|
-
for (const [name, value] of Object.entries(rawProjects)) {
|
|
1712
|
-
if (name === "__proto__") {
|
|
1713
|
-
throw new UserConfigError(`"__proto__" cannot be used as a key in "projects": ${path2}`);
|
|
1714
|
-
}
|
|
1715
|
-
if (typeof value !== "string" || !isAbsolute(value)) {
|
|
1716
|
-
console.warn(`[config] invalid projects.${name}: expected an absolute path, skipping`);
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
if (!isDirectory(value)) {
|
|
1720
|
-
console.warn(`[config] projects.${name} does not exist as a directory: ${value}, skipping`);
|
|
1721
|
-
continue;
|
|
1722
|
-
}
|
|
1723
|
-
projects[name] = value;
|
|
1724
|
-
}
|
|
1725
|
-
const projectGroups = {};
|
|
1726
|
-
for (const [groupName, value] of Object.entries(rawProjectGroups)) {
|
|
1727
|
-
if (groupName === "__proto__") {
|
|
1728
|
-
throw new UserConfigError(`"__proto__" cannot be used as a key in "projectGroups": ${path2}`);
|
|
1729
|
-
}
|
|
1730
|
-
if (!Array.isArray(value)) {
|
|
1731
|
-
console.warn(`[config] invalid projectGroups.${groupName}: expected an array, skipping`);
|
|
1732
|
-
continue;
|
|
1733
|
-
}
|
|
1734
|
-
const members = [];
|
|
1735
|
-
for (const member of value) {
|
|
1736
|
-
if (typeof member !== "string" || !Object.prototype.hasOwnProperty.call(projects, member)) {
|
|
1737
|
-
console.warn(`[config] projectGroups.${groupName} references unknown project "${String(member)}", skipping`);
|
|
1738
|
-
continue;
|
|
1739
|
-
}
|
|
1740
|
-
members.push(member);
|
|
1741
|
-
}
|
|
1742
|
-
projectGroups[groupName] = members;
|
|
1743
|
-
}
|
|
1744
|
-
return { mode, advisor, permission, projects, projectGroups };
|
|
1652
|
+
function parseProjectFilters() {
|
|
1653
|
+
return collectFlagValues(process.argv, "--project");
|
|
1745
1654
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
if (cachedRunMode === void 0) {
|
|
1749
|
-
cachedRunMode = readRunMode();
|
|
1750
|
-
}
|
|
1751
|
-
return cachedRunMode;
|
|
1655
|
+
function hasProjectFilter() {
|
|
1656
|
+
return process.argv.includes("--project");
|
|
1752
1657
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1658
|
+
function assertProjectCompatibleCommand(command) {
|
|
1659
|
+
if (FLAG_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
1660
|
+
console.error(`[dispatcher] --project cannot be used with the "${command}" command`);
|
|
1661
|
+
process.exit(1);
|
|
1757
1662
|
}
|
|
1758
|
-
return cachedAdvisorEnabled;
|
|
1759
1663
|
}
|
|
1760
|
-
var
|
|
1761
|
-
function
|
|
1762
|
-
if (
|
|
1763
|
-
|
|
1664
|
+
var cachedCloudFlag;
|
|
1665
|
+
function hasCloudFlag() {
|
|
1666
|
+
if (cachedCloudFlag === void 0) {
|
|
1667
|
+
cachedCloudFlag = process.argv.includes("--cloud");
|
|
1764
1668
|
}
|
|
1765
|
-
return
|
|
1669
|
+
return cachedCloudFlag;
|
|
1766
1670
|
}
|
|
1767
|
-
function
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
} catch (err) {
|
|
1772
|
-
console.warn(`[config] failed to read config file, using default ${label}: ${err}`);
|
|
1773
|
-
return fallback;
|
|
1774
|
-
}
|
|
1775
|
-
if (raw === void 0) return fallback;
|
|
1776
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1777
|
-
return fallback;
|
|
1671
|
+
function assertCloudCompatibleCommand(command) {
|
|
1672
|
+
if (FLAG_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
1673
|
+
console.error(`[worker] --cloud cannot be used with the "${command}" command`);
|
|
1674
|
+
process.exit(1);
|
|
1778
1675
|
}
|
|
1779
|
-
return parse(raw, getUserConfigPath());
|
|
1780
|
-
}
|
|
1781
|
-
function readAdvisorEnabled() {
|
|
1782
|
-
return readTopLevel(parseAdvisor, DEFAULT_ADVISOR_ENABLED, "advisor");
|
|
1783
1676
|
}
|
|
1784
|
-
function
|
|
1785
|
-
|
|
1786
|
-
}
|
|
1787
|
-
function findProjectNameByPath(path2) {
|
|
1788
|
-
let config;
|
|
1789
|
-
try {
|
|
1790
|
-
config = loadUserConfig();
|
|
1791
|
-
} catch {
|
|
1792
|
-
return void 0;
|
|
1793
|
-
}
|
|
1794
|
-
const target = resolve(path2);
|
|
1795
|
-
for (const [name, projectPath] of Object.entries(config.projects)) {
|
|
1796
|
-
if (resolve(projectPath) === target) return name;
|
|
1797
|
-
}
|
|
1798
|
-
return void 0;
|
|
1677
|
+
function shellQuote(value) {
|
|
1678
|
+
if (value === "") return "''";
|
|
1679
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
1799
1680
|
}
|
|
1800
|
-
function
|
|
1801
|
-
const
|
|
1802
|
-
for (
|
|
1803
|
-
if (
|
|
1804
|
-
|
|
1805
|
-
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
1806
|
-
}
|
|
1807
|
-
continue;
|
|
1808
|
-
}
|
|
1809
|
-
if (Object.prototype.hasOwnProperty.call(config.projects, name)) {
|
|
1810
|
-
resolved.set(name, { name, path: config.projects[name] });
|
|
1811
|
-
continue;
|
|
1812
|
-
}
|
|
1813
|
-
if (Object.prototype.hasOwnProperty.call(config.projectGroups, name)) {
|
|
1814
|
-
for (const projectName of config.projectGroups[name]) {
|
|
1815
|
-
const projectPath = config.projects[projectName];
|
|
1816
|
-
if (projectPath === void 0) continue;
|
|
1817
|
-
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
1818
|
-
}
|
|
1681
|
+
function buildForwardedCommand(argv) {
|
|
1682
|
+
const tokens = [];
|
|
1683
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1684
|
+
if (argv[i] === "--project") {
|
|
1685
|
+
i++;
|
|
1819
1686
|
continue;
|
|
1820
1687
|
}
|
|
1821
|
-
|
|
1822
|
-
const availableGroups = Object.keys(config.projectGroups).join(", ") || "(none)";
|
|
1823
|
-
throw new UserConfigError(
|
|
1824
|
-
`Unknown project or group: "${name}". Available projects: ${availableProjects}. Available groups: ${availableGroups}.`
|
|
1825
|
-
);
|
|
1688
|
+
tokens.push(argv[i]);
|
|
1826
1689
|
}
|
|
1827
|
-
|
|
1828
|
-
if (resolvedProjects.length === 0) {
|
|
1829
|
-
throw new UserConfigError(`No projects resolved from requested targets: ${requested.join(", ")}`);
|
|
1830
|
-
}
|
|
1831
|
-
return resolvedProjects;
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
// src/claude-args.ts
|
|
1835
|
-
var DISALLOWED_TOOLS = [
|
|
1836
|
-
// 遅延 / yield: 後続ウェイクアップ前提。print モードではウェイクアップが発火せず、
|
|
1837
|
-
// 呼ぶと処理未完のままプロセスが終了する。
|
|
1838
|
-
"Monitor",
|
|
1839
|
-
"ScheduleWakeup",
|
|
1840
|
-
// 対話 / 承認: 自律実行セッションには回答・承認するユーザーが存在しない。
|
|
1841
|
-
"AskUserQuestion",
|
|
1842
|
-
"EnterPlanMode",
|
|
1843
|
-
// スコープ外の副作用を伴う自動化: コード修正タスクに用途がなく、ユーザーの
|
|
1844
|
-
// クラウド routine / リモート環境へ副作用を及ぼしうる。
|
|
1845
|
-
"CronCreate",
|
|
1846
|
-
"CronDelete",
|
|
1847
|
-
"CronList",
|
|
1848
|
-
"RemoteTrigger",
|
|
1849
|
-
// 環境管理の競合: ワーカーは locked worktree の残骸問題のため claude 管理の worktree を
|
|
1850
|
-
// 意図的に避け、自前で worktree を生成して cwd として渡している。モデルが worktree を
|
|
1851
|
-
// 作成/切り替えると、この前提とクリーンアップが壊れる。
|
|
1852
|
-
"EnterWorktree"
|
|
1853
|
-
];
|
|
1854
|
-
var DISALLOWED_TOOLS_ARG = DISALLOWED_TOOLS.join(",");
|
|
1855
|
-
var CLAUDE_SPAWN_ENV = {
|
|
1856
|
-
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
|
|
1857
|
-
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: "0"
|
|
1858
|
-
};
|
|
1859
|
-
var SYSTEM_PROMPT_BASE = `\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F \`claude-task-worker\` \u306E\u30EF\u30FC\u30AB\u30FC\u304B\u3089\u81EA\u52D5\u8D77\u52D5\u3055\u308C\u3066\u3044\u308B\uFF08\u5FDC\u7B54\u3067\u304D\u308B\u30E6\u30FC\u30B6\u30FC\u306F\u5E38\u99D0\u3057\u3066\u3044\u306A\u3044\uFF09\u3002\u4EE5\u4E0B\u306E\u81EA\u5F8B\u5B9F\u884C\u539F\u5247\u3092\u5FC5\u305A\u9075\u5B88\u3059\u308B\u3053\u3068\u3002
|
|
1860
|
-
|
|
1861
|
-
- \u30E6\u30FC\u30B6\u30FC\u3078\u306E\u78BA\u8A8D\u30FB\u8CEA\u554F\u306F\u884C\u308F\u305A\u3001\u8D77\u52D5\u3055\u308C\u305F\u30B9\u30AD\u30EB\u306E\u30EB\u30FC\u30EB\u306B\u5F93\u3063\u3066\u81EA\u5F8B\u7684\u306B\u5224\u65AD\u3059\u308B
|
|
1862
|
-
- \u66D6\u6627\u306A\u5834\u5408\u306F\u300C\u3088\u308A\u5B89\u5168\u306A\u5074\uFF08\u7834\u58CA\u7684\u3067\u306A\u3044\u5074\uFF09\u300D\u3092\u9078\u629E\u3057\u3001\u305D\u306E\u5224\u65AD\u3068\u6839\u62E0\u3092\u6700\u7D42\u5831\u544A\u306B\u660E\u8A18\u3059\u308B
|
|
1863
|
-
- \u5168\u30B9\u30C6\u30C3\u30D7\u3092\u5B8C\u9042\u3057\u3066\u304B\u3089\u7D42\u4E86\u3059\u308B\uFF08\u30B9\u30AD\u30EB\u306B\u5B9A\u7FA9\u3055\u308C\u305F\u4E2D\u65AD\u6761\u4EF6\u306B\u8A72\u5F53\u3057\u305F\u5834\u5408\u306E\u307F\u3001\u7406\u7531\u3092\u51FA\u529B\u3057\u3066\u7D42\u4E86\u3059\u308B\uFF09
|
|
1864
|
-
- \u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u4F5C\u696D\u3092\u59D4\u8B72\u3059\u308B\u5834\u5408\u306F\u3001\u4E0A\u8A18\u306E\u539F\u5247\u3092\u59D4\u8B72\u30D7\u30ED\u30F3\u30D7\u30C8\u306B\u3082\u660E\u8A18\u3057\u3066\u4F1D\u3048\u308B
|
|
1865
|
-
- \u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u5B8C\u4E86\u5831\u544A\u306F\u9D5C\u5451\u307F\u306B\u3057\u306A\u3044\u3002\`git diff\` \u7B49\u3067\u5B9F\u969B\u306E\u6210\u679C\u7269\u3092\u691C\u8A3C\u3057\u3066\u304B\u3089\u5B8C\u4E86\u6271\u3044\u306B\u3059\u308B
|
|
1866
|
-
|
|
1867
|
-
\u30B3\u30FC\u30C9\u306E\u63A2\u7D22\u30FB\u8ABF\u67FB\u3067\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
1868
|
-
|
|
1869
|
-
- **CodeGraph \u304C\u4F7F\u3048\u308B\u5834\u5408\u306F \`Grep\`/\`Glob\` \u306B\u3088\u308B\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3088\u308A\u512A\u5148\u3059\u308B**\u3002\u30B7\u30F3\u30DC\u30EB\u306E\u5B9A\u7FA9\u5143\u30FB\u53C2\u7167\u5143\u30FB\u547C\u3073\u51FA\u3057\u95A2\u4FC2\u3092\u69CB\u9020\u3068\u3057\u3066\u8FBF\u308C\u308B\u305F\u3081\u3001\u547D\u540D\u3086\u308C\u306B\u3088\u308B\u53D6\u308A\u3053\u307C\u3057\u304C\u8D77\u304D\u306B\u304F\u304F\u3001\u5FC5\u8981\u306A\u60C5\u5831\u306B\u5C11\u306A\u3044\u8A66\u884C\u3067\u5230\u9054\u3067\u304D\u308B
|
|
1870
|
-
- **\u5229\u7528\u53EF\u5426\u306F codegraph \u7CFB\u306E MCP \u30C4\u30FC\u30EB\uFF08\`codegraph_explore\` \u7B49\uFF09\u304C\u81EA\u5206\u306B\u4E0E\u3048\u3089\u308C\u3066\u3044\u308B\u304B\u3060\u3051\u3067\u5224\u65AD\u3059\u308B**\u3002\u7121\u3051\u308C\u3070\u300C\u5229\u7528\u4E0D\u53EF\u300D\u3068\u5373\u65AD\u3057\u3066\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3078\u9032\u307F\u3001\u5224\u5B9A\u306B\u624B\u9593\u3092\u304B\u3051\u306A\u3044
|
|
1871
|
-
- \u672A\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3067\u306F MCP \u30C4\u30FC\u30EB\u304C\u3042\u3063\u3066\u3082\u30A8\u30E9\u30FC\u3084\u7A7A\u306E\u7D50\u679C\u304C\u8FD4\u308B\u3002\u305D\u306E\u5834\u5408\u3082\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3078\u5207\u308A\u66FF\u3048\u308B\u3060\u3051\u3067\u3088\u304F\u3001\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3092\u7528\u610F\u3057\u3088\u3046\u3068\u3057\u306A\u3044\uFF08\u30BF\u30B9\u30AF\u306E\u8CAC\u52D9\u5916\uFF09
|
|
1872
|
-
- CodeGraph \u304C\u8FD4\u3057\u305F\u30BD\u30FC\u30B9\u306F\u300C\u8AAD\u307F\u7D42\u3048\u305F\u3082\u306E\u300D\u3068\u3057\u3066\u6271\u3044\u3001\u540C\u3058\u7B87\u6240\u3092 \`Grep\`/\`Read\` \u3067\u88CF\u53D6\u308A\u3057\u76F4\u3055\u306A\u3044\u3002\u305F\u3060\u3057\u51FA\u529B\u306B staleness\uFF08\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u304C\u53E4\u3044\u65E8\uFF09\u306E\u8B66\u544A\u304C\u51FA\u3066\u3044\u308B\u5834\u5408\u306F\u8A72\u5F53\u30D5\u30A1\u30A4\u30EB\u3092 \`Read\` \u3057\u3066\u73FE\u7269\u3092\u78BA\u8A8D\u3059\u308B
|
|
1873
|
-
- \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u30FB\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u30FB\u30B3\u30E1\u30F3\u30C8/\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u30FB\u672A\u5BFE\u5FDC\u8A00\u8A9E\u306A\u3069 CodeGraph \u304C\u6271\u308F\u306A\u3044\u5BFE\u8C61\u306F\u3001\u5F93\u6765\u3069\u304A\u308A\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3067\u88DC\u3046
|
|
1874
|
-
- \u63A2\u7D22\u3092\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u59D4\u8B72\u3059\u308B\u5834\u5408\u306F\u3001\u3053\u306E\u65B9\u91DD\u3082\u59D4\u8B72\u30D7\u30ED\u30F3\u30D7\u30C8\u306B\u660E\u8A18\u3057\u3066\u4F1D\u3048\u308B`;
|
|
1875
|
-
var OPUS_SYSTEM_PROMPT_ADDENDUM = `\u6210\u679C\u7269\u306E\u5206\u91CF\u3068\u30B9\u30B3\u30FC\u30D7\u306B\u3064\u3044\u3066\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
1876
|
-
|
|
1877
|
-
- \u4F9D\u983C\u3055\u308C\u305F\u30B9\u30B3\u30FC\u30D7\u3060\u3051\u3092\u6210\u679C\u7269\u306B\u3059\u308B\u3002\u5468\u8FBA\u306E\u30EA\u30D5\u30A1\u30AF\u30BF\u30FB\u547D\u540D\u6574\u7406\u30FB\u6C17\u3065\u3044\u305F\u5225\u306E\u6539\u5584\u3092\u52DD\u624B\u306B\u8DB3\u3055\u306A\u3044\u3002\u6C17\u3065\u3044\u305F\u70B9\u306F\u5B9F\u88C5\u305B\u305A\u3001\u6700\u7D42\u5831\u544A\u306B1\u884C\u3067\u6319\u3052\u308B\u3060\u3051\u306B\u3059\u308B
|
|
1878
|
-
- \u4F9D\u983C\u306E\u524D\u63D0\u304C\u8AA4\u3063\u3066\u3044\u308B\u3068\u8003\u3048\u308B\u5834\u5408\u3082\u3001\u6307\u6458\u30921-2\u884C\u6DFB\u3048\u305F\u3046\u3048\u3067**\u4F9D\u983C\u3069\u304A\u308A\u306E\u30B9\u30B3\u30FC\u30D7\u3067**\u5B8C\u9042\u3059\u308B\uFF08\u9ED9\u3063\u3066\u7E2E\u5C0F\u30FB\u62E1\u5927\u30FB\u5225\u7269\u3078\u306E\u7F6E\u304D\u63DB\u3048\u3092\u3057\u306A\u3044\uFF09
|
|
1879
|
-
- Issue\u30B3\u30E1\u30F3\u30C8\u30FBPR\u306E\u672C\u6587\u30FBdescription\u30FB\u30EC\u30DD\u30FC\u30C8\u7B49\u306E\u66F8\u304D\u7269\u306F\u3001\u5FC5\u8981\u306A\u5B9F\u8CEA\u3060\u3051\u3092\u66F8\u304F\u3002\u540C\u3058\u5185\u5BB9\u306E\u8A00\u3044\u63DB\u3048\u30FB\u57CB\u3081\u8349\u30BB\u30AF\u30B7\u30E7\u30F3\u30FB\u300C\u8A72\u5F53\u306A\u3057\u300D\u3092\u4E26\u3079\u308B\u3060\u3051\u306E\u7BC0\u3092\u8DB3\u3055\u306A\u3044
|
|
1880
|
-
- \u6700\u7D42\u5831\u544A\u306F\u7D50\u8AD6\u304B\u3089\u66F8\u304F\u30021\u6587\u76EE\u3067\u300C\u4F55\u3092\u3057\u305F\u304B / \u3069\u3053\u3067\u6B62\u307E\u3063\u305F\u304B\u300D\u3092\u8FF0\u3079\u3001\u8A73\u7D30\u3092\u305D\u306E\u5F8C\u306B\u7F6E\u304F
|
|
1881
|
-
|
|
1882
|
-
\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u306E\u59D4\u8B72\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
1883
|
-
|
|
1884
|
-
- \u81EA\u5206\u3067\u6570\u56DE\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3067\u7D42\u308F\u308B\u4F5C\u696D\u306F\u59D4\u8B72\u3057\u306A\u3044\uFF08\u30D6\u30EA\u30FC\u30D5\u30A3\u30F3\u30B0\u4F5C\u6210\u3068\u59D4\u8B72\u5148\u306E\u518D\u63A2\u7D22\u3067\u30B3\u30B9\u30C8\u3068\u6642\u9593\u304C\u500D\u306B\u306A\u308B\uFF09
|
|
1885
|
-
- \u59D4\u8B72\u3059\u308B\u306E\u306F\u3001\u72EC\u7ACB\u3057\u3066\u4E26\u5217\u5B9F\u884C\u3067\u304D\u308B\u4F5C\u696D\u30FB\u63A2\u7D22\u7BC4\u56F2\u306E\u5E83\u3044\u8ABF\u67FB\u30FB\u5C02\u9580\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u524D\u63D0\u77E5\u8B58\u304C\u8981\u308B\u4F5C\u696D\u306B\u9650\u308B
|
|
1886
|
-
- 1\u30BF\u30B9\u30AF\u306B1\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u30021\u4F53\u3067\u5B8C\u7D50\u3059\u308B\u4F5C\u696D\u306B\u8907\u6570\u4F53\u3092\u91CD\u306D\u3066\u8D77\u52D5\u3057\u306A\u3044
|
|
1887
|
-
- **\u81EA\u5206\u306E\u4F5C\u696D\u306E\u78BA\u8A8D\u30FB\u518D\u30C1\u30A7\u30C3\u30AF\u3092\u76EE\u7684\u306B\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3092\u8D77\u52D5\u3057\u306A\u3044**\u3002\u6210\u679C\u7269\u306E\u691C\u8A3C\u306F \`git diff\` \u3084\u30C6\u30B9\u30C8\u30FBLint\u306E\u5B9F\u884C\u3067\u81EA\u5206\u3067\u884C\u3046`;
|
|
1888
|
-
function systemPromptFor(model) {
|
|
1889
|
-
return isOpusModel(model) ? `${SYSTEM_PROMPT_BASE}
|
|
1890
|
-
|
|
1891
|
-
${OPUS_SYSTEM_PROMPT_ADDENDUM}` : SYSTEM_PROMPT_BASE;
|
|
1892
|
-
}
|
|
1893
|
-
function isOpusModel(model) {
|
|
1894
|
-
return model.toLowerCase().includes("opus");
|
|
1895
|
-
}
|
|
1896
|
-
var CLAUDE_COMMAND = "claude";
|
|
1897
|
-
var cachedSystemPromptFilePaths = /* @__PURE__ */ new Map();
|
|
1898
|
-
function systemPromptFilePath(model) {
|
|
1899
|
-
const variant = isOpusModel(model) ? "opus" : "default";
|
|
1900
|
-
const cached = cachedSystemPromptFilePaths.get(variant);
|
|
1901
|
-
if (cached) return cached;
|
|
1902
|
-
const dir = path.join(os.tmpdir(), "claude-task-worker");
|
|
1903
|
-
mkdirSync(dir, { recursive: true });
|
|
1904
|
-
const target = path.join(dir, `append-system-prompt-${process.pid}-${variant}.txt`);
|
|
1905
|
-
const tmp = `${target}.tmp`;
|
|
1906
|
-
writeFileSync(tmp, systemPromptFor(model), "utf8");
|
|
1907
|
-
renameSync(tmp, target);
|
|
1908
|
-
cachedSystemPromptFilePaths.set(variant, target);
|
|
1909
|
-
return target;
|
|
1910
|
-
}
|
|
1911
|
-
function buildClaudeArgs({
|
|
1912
|
-
mode,
|
|
1913
|
-
prompt,
|
|
1914
|
-
model,
|
|
1915
|
-
effort,
|
|
1916
|
-
advisorModel,
|
|
1917
|
-
permissionMode
|
|
1918
|
-
}) {
|
|
1919
|
-
const advisor = advisorModel?.trim() ?? "";
|
|
1920
|
-
const permission = permissionMode ?? DEFAULT_PERMISSION_MODE;
|
|
1921
|
-
return [
|
|
1922
|
-
// default モードはプロンプトを引数で渡す(print モード)。herdr モードでは渡さない:
|
|
1923
|
-
// 引数で渡すと claude が起動と同時に作業を始めてしまい、`herdr agent start` が
|
|
1924
|
-
// 「入力待ちになるまで」ブロックする仕様と噛み合わない(タスクが終わるまで返らず、
|
|
1925
|
-
// 2分を超えると timeout で落ちる)。プロンプトは起動後に `herdr agent prompt` で
|
|
1926
|
-
// 投入し、herdr にターンを追跡させる(herdr-runner.ts の startHerdrTask 参照)。
|
|
1927
|
-
...mode === "herdr" ? [] : ["-p", prompt],
|
|
1928
|
-
"--permission-mode",
|
|
1929
|
-
permission,
|
|
1930
|
-
"--disallowedTools",
|
|
1931
|
-
DISALLOWED_TOOLS_ARG,
|
|
1932
|
-
"--append-system-prompt-file",
|
|
1933
|
-
systemPromptFilePath(model),
|
|
1934
|
-
"--model",
|
|
1935
|
-
model,
|
|
1936
|
-
"--effort",
|
|
1937
|
-
effort,
|
|
1938
|
-
// advisor 未指定(空文字)ならフラグごと省く。値なしの `--advisor` を渡すと
|
|
1939
|
-
// 後続フラグを値として食われるため、必ずモデル名とセットでのみ付ける。
|
|
1940
|
-
...advisor === "" ? [] : ["--advisor", advisor]
|
|
1941
|
-
];
|
|
1942
|
-
}
|
|
1943
|
-
function buildClaudeExecution(invocation) {
|
|
1944
|
-
return {
|
|
1945
|
-
command: CLAUDE_COMMAND,
|
|
1946
|
-
args: buildClaudeArgs(invocation),
|
|
1947
|
-
...invocation.mode === "herdr" ? { prompt: invocation.prompt } : {}
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
function buildClaudeEnv(mode) {
|
|
1951
|
-
return mode === "herdr" ? { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS } : { ...CLAUDE_SPAWN_ENV };
|
|
1690
|
+
return ["claude-task-worker", ...tokens.map(shellQuote)].join(" ");
|
|
1952
1691
|
}
|
|
1953
1692
|
|
|
1954
1693
|
// src/config.ts
|
|
1955
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1956
|
-
import { isAbsolute as isAbsolute2, join as join2, normalize, sep as SEP } from "node:path";
|
|
1957
1694
|
var DEFAULT_UI_DESIGN_CONFIG = {
|
|
1958
1695
|
enabled: false,
|
|
1959
1696
|
designDir: "designs",
|
|
@@ -2115,13 +1852,62 @@ var SCHEDULED_WORKER_NAMES = [
|
|
|
2115
1852
|
"update-requirement-rules",
|
|
2116
1853
|
"update-design-md"
|
|
2117
1854
|
];
|
|
1855
|
+
var CLOUD_DONE_LABEL = "cc-cloud-done";
|
|
1856
|
+
var CLOUD_DENIED_WORKERS = [
|
|
1857
|
+
"resolve-conflict",
|
|
1858
|
+
"create-ui-design",
|
|
1859
|
+
"apply-ui-design",
|
|
1860
|
+
...SCHEDULED_WORKER_NAMES
|
|
1861
|
+
];
|
|
1862
|
+
function isCloudWorker(name) {
|
|
1863
|
+
return hasCloudFlag() && !CLOUD_DENIED_WORKERS.includes(name);
|
|
1864
|
+
}
|
|
1865
|
+
function checkCloudAuth(input) {
|
|
1866
|
+
if (input.status.kind === "unknown") return [];
|
|
1867
|
+
const { loggedIn, authMethod, apiProvider, apiKeySource } = input.status;
|
|
1868
|
+
const baseUrlSet = !!input.baseUrl;
|
|
1869
|
+
if (loggedIn && apiProvider === "firstParty" && authMethod === "claude.ai" && !apiKeySource && !baseUrlSet) {
|
|
1870
|
+
return [];
|
|
1871
|
+
}
|
|
1872
|
+
const prefix = `\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\uFF08--cloud \u30D5\u30E9\u30B0\uFF09\u306B\u306F claude.ai \u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u306E\u30B5\u30A4\u30F3\u30A4\u30F3\u304C\u5FC5\u8981\u3067\u3059\u3002\u73FE\u5728\u306E\u8A8D\u8A3C\u69CB\u6210: ${authMethod} / ${apiProvider}\u3002`;
|
|
1873
|
+
if (apiProvider === "bedrock" || apiProvider === "vertex") {
|
|
1874
|
+
return [
|
|
1875
|
+
`${prefix} \u7B2C\u4E09\u8005\u30D7\u30ED\u30D0\u30A4\u30C0\uFF08Bedrock / Vertex\uFF09\u3092\u4F7F\u3063\u3066\u3044\u308B\u5834\u5408: \u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306F Anthropic \u306E\u30A4\u30F3\u30D5\u30E9\u4E0A\u3067\u52D5\u304F\u305F\u3081\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002CLAUDE_CODE_USE_BEDROCK / CLAUDE_CODE_USE_VERTEX \u3092\u89E3\u9664\u3059\u308B\u304B\u3001--cloud \u30D5\u30E9\u30B0\u3092\u5916\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
1876
|
+
];
|
|
1877
|
+
}
|
|
1878
|
+
if (apiKeySource || authMethod === "oauth_token") {
|
|
1879
|
+
return [
|
|
1880
|
+
`${prefix} API \u30AD\u30FC\u8A8D\u8A3C\uFF08ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN\uFF09\u306E\u5834\u5408: API \u30AD\u30FC\u3067\u306F\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002\u74B0\u5883\u5909\u6570\u3092\u89E3\u9664\u3057\u3066 claude auth login \u3067\u30B5\u30A4\u30F3\u30A4\u30F3\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
1881
|
+
];
|
|
1882
|
+
}
|
|
1883
|
+
if (!loggedIn) {
|
|
1884
|
+
return [`${prefix} \u672A\u30B5\u30A4\u30F3\u30A4\u30F3\u306E\u5834\u5408: claude auth login \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`];
|
|
1885
|
+
}
|
|
1886
|
+
if (baseUrlSet) {
|
|
1887
|
+
return [
|
|
1888
|
+
`${prefix} ANTHROPIC_BASE_URL \u3092\u8A2D\u5B9A\u3057\u3066\u3044\u308B\u5834\u5408: \u30AB\u30B9\u30BF\u30E0\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u69CB\u6210\u3067\u306F\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
1889
|
+
];
|
|
1890
|
+
}
|
|
1891
|
+
return [`${prefix} claude auth status --json \u306E\u51FA\u529B\u304B\u3089\u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u306E\u524D\u63D0\u6761\u4EF6\u3092\u5224\u5B9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002`];
|
|
1892
|
+
}
|
|
1893
|
+
function checkCloudConfig(input) {
|
|
1894
|
+
if (!input.cloud) return [];
|
|
1895
|
+
const errors = [];
|
|
1896
|
+
if (input.mode !== "herdr") {
|
|
1897
|
+
errors.push(
|
|
1898
|
+
`--cloud requires mode "herdr" but mode is "${input.mode}" (creating a new cloud session requires a TTY, which "default" mode's spawn does not have). Set mode to "herdr" in config.json, or drop the --cloud flag.`
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
if (input.auth !== void 0) errors.push(...checkCloudAuth(input.auth));
|
|
1902
|
+
return errors;
|
|
1903
|
+
}
|
|
2118
1904
|
var DEFAULT_CONFIG = {
|
|
2119
1905
|
fixReviewPointCallbackCommentMessage: "",
|
|
2120
1906
|
uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG },
|
|
2121
1907
|
lastRun: {},
|
|
2122
1908
|
workers: {}
|
|
2123
1909
|
};
|
|
2124
|
-
var CONFIG_PATH =
|
|
1910
|
+
var CONFIG_PATH = join(process.cwd(), "claude-task-worker.json");
|
|
2125
1911
|
function defaultsFor(name) {
|
|
2126
1912
|
return WORKER_DEFAULTS[name] ?? DEFAULT_WORKER_CONFIG;
|
|
2127
1913
|
}
|
|
@@ -2193,6 +1979,11 @@ function parseWorkerEntry(name, val) {
|
|
|
2193
1979
|
);
|
|
2194
1980
|
}
|
|
2195
1981
|
}
|
|
1982
|
+
if ("cloud" in entry) {
|
|
1983
|
+
console.warn(
|
|
1984
|
+
`[config] workers.${name}.cloud is removed; cloud execution now opts in via the --cloud flag at runtime. This setting is ignored.`
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
2196
1987
|
return result;
|
|
2197
1988
|
}
|
|
2198
1989
|
function parseUiDesignEntry(val) {
|
|
@@ -2222,7 +2013,7 @@ function parseUiDesignEntry(val) {
|
|
|
2222
2013
|
}
|
|
2223
2014
|
if ("designDir" in entry) {
|
|
2224
2015
|
const normalized = typeof entry.designDir === "string" && entry.designDir.length > 0 ? normalize(entry.designDir) : null;
|
|
2225
|
-
const isContained = normalized !== null && !
|
|
2016
|
+
const isContained = normalized !== null && !isAbsolute(normalized) && normalized !== ".." && !normalized.startsWith(`..${SEP}`);
|
|
2226
2017
|
if (isContained) {
|
|
2227
2018
|
result.designDir = normalized;
|
|
2228
2019
|
} else {
|
|
@@ -2252,7 +2043,7 @@ function loadConfig() {
|
|
|
2252
2043
|
const configPath = CONFIG_PATH;
|
|
2253
2044
|
let raw;
|
|
2254
2045
|
try {
|
|
2255
|
-
raw = JSON.parse(
|
|
2046
|
+
raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
2256
2047
|
} catch (err) {
|
|
2257
2048
|
if (err.code === "ENOENT") {
|
|
2258
2049
|
return { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, lastRun: {}, workers: {} };
|
|
@@ -2285,47 +2076,455 @@ function loadConfig() {
|
|
|
2285
2076
|
}
|
|
2286
2077
|
return result;
|
|
2287
2078
|
}
|
|
2288
|
-
function getWorkerConfig(workerName) {
|
|
2289
|
-
const config = loadConfig();
|
|
2290
|
-
return config.workers[workerName] ?? { ...defaultsFor(workerName) };
|
|
2079
|
+
function getWorkerConfig(workerName) {
|
|
2080
|
+
const config = loadConfig();
|
|
2081
|
+
return config.workers[workerName] ?? { ...defaultsFor(workerName) };
|
|
2082
|
+
}
|
|
2083
|
+
function getLastRunAt(workerName) {
|
|
2084
|
+
let at;
|
|
2085
|
+
try {
|
|
2086
|
+
at = loadConfig().lastRun[workerName];
|
|
2087
|
+
} catch (err) {
|
|
2088
|
+
console.warn(`[config] failed to load lastRun, treating ${workerName} as never run: ${err}`);
|
|
2089
|
+
return void 0;
|
|
2090
|
+
}
|
|
2091
|
+
if (at === void 0) return void 0;
|
|
2092
|
+
const parsed = Date.parse(at);
|
|
2093
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2094
|
+
}
|
|
2095
|
+
function writeLastRun(repoRoot, workerName, at = /* @__PURE__ */ new Date()) {
|
|
2096
|
+
const path2 = join(repoRoot, "claude-task-worker.json");
|
|
2097
|
+
let raw = {};
|
|
2098
|
+
try {
|
|
2099
|
+
const parsed = JSON.parse(readFileSync(path2, "utf-8"));
|
|
2100
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2101
|
+
raw = parsed;
|
|
2102
|
+
}
|
|
2103
|
+
} catch (err) {
|
|
2104
|
+
if (err.code !== "ENOENT") {
|
|
2105
|
+
console.warn(`[config] failed to read ${path2}, rewriting it with lastRun only: ${err}`);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
const current = typeof raw["lastRun"] === "object" && raw["lastRun"] !== null ? raw["lastRun"] : {};
|
|
2109
|
+
raw["lastRun"] = { ...current, [workerName]: at.toISOString() };
|
|
2110
|
+
writeFileSync(path2, `${JSON.stringify(raw, null, 2)}
|
|
2111
|
+
`, "utf-8");
|
|
2112
|
+
}
|
|
2113
|
+
function getUiDesignConfig() {
|
|
2114
|
+
try {
|
|
2115
|
+
return loadConfig().uiDesign;
|
|
2116
|
+
} catch (err) {
|
|
2117
|
+
console.warn(`[config] failed to load uiDesign config, using defaults: ${err}`);
|
|
2118
|
+
return { ...DEFAULT_UI_DESIGN_CONFIG };
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
// src/user-config.ts
|
|
2123
|
+
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
2124
|
+
import { homedir } from "node:os";
|
|
2125
|
+
import { isAbsolute as isAbsolute2, join as join2, resolve } from "node:path";
|
|
2126
|
+
var RESERVED_ALL = "all";
|
|
2127
|
+
var DEFAULT_RUN_MODE = "default";
|
|
2128
|
+
var DEFAULT_ADVISOR_ENABLED = false;
|
|
2129
|
+
var PERMISSION_MODES = [
|
|
2130
|
+
"manual",
|
|
2131
|
+
"auto",
|
|
2132
|
+
"acceptEdits",
|
|
2133
|
+
"dontAsk",
|
|
2134
|
+
"plan",
|
|
2135
|
+
"bypassPermissions"
|
|
2136
|
+
];
|
|
2137
|
+
var DEFAULT_PERMISSION_MODE = "bypassPermissions";
|
|
2138
|
+
var UserConfigError = class extends Error {
|
|
2139
|
+
constructor(message) {
|
|
2140
|
+
super(message);
|
|
2141
|
+
this.name = "UserConfigError";
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
function getConfigDir() {
|
|
2145
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
2146
|
+
const configHome = xdg && xdg.length > 0 ? xdg : join2(homedir(), ".config");
|
|
2147
|
+
return join2(configHome, "claude-task-worker");
|
|
2148
|
+
}
|
|
2149
|
+
function getUserConfigPath() {
|
|
2150
|
+
return join2(getConfigDir(), "config.json");
|
|
2151
|
+
}
|
|
2152
|
+
function isDirectory(path2) {
|
|
2153
|
+
try {
|
|
2154
|
+
return statSync(path2).isDirectory();
|
|
2155
|
+
} catch {
|
|
2156
|
+
return false;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
function parseConfigFile(path2) {
|
|
2160
|
+
let content;
|
|
2161
|
+
try {
|
|
2162
|
+
content = readFileSync2(path2, "utf-8");
|
|
2163
|
+
} catch (err) {
|
|
2164
|
+
if (err.code === "ENOENT") return void 0;
|
|
2165
|
+
throw err;
|
|
2166
|
+
}
|
|
2167
|
+
try {
|
|
2168
|
+
return JSON.parse(content);
|
|
2169
|
+
} catch (err) {
|
|
2170
|
+
if (err instanceof SyntaxError) {
|
|
2171
|
+
throw new UserConfigError(`config file contains invalid JSON: ${path2}: ${err.message}`);
|
|
2172
|
+
}
|
|
2173
|
+
throw err;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
function readRawConfig() {
|
|
2177
|
+
return parseConfigFile(getUserConfigPath());
|
|
2178
|
+
}
|
|
2179
|
+
function parseMode(raw, path2) {
|
|
2180
|
+
if (!("mode" in raw)) return DEFAULT_RUN_MODE;
|
|
2181
|
+
const value = raw["mode"];
|
|
2182
|
+
if (value === "default" || value === "herdr") return value;
|
|
2183
|
+
console.warn(`[config] invalid mode: ${JSON.stringify(value)} in ${path2}, using "${DEFAULT_RUN_MODE}"`);
|
|
2184
|
+
return DEFAULT_RUN_MODE;
|
|
2185
|
+
}
|
|
2186
|
+
function parseAdvisor(raw, path2) {
|
|
2187
|
+
if (!("advisor" in raw)) return DEFAULT_ADVISOR_ENABLED;
|
|
2188
|
+
const value = raw["advisor"];
|
|
2189
|
+
if (typeof value === "boolean") return value;
|
|
2190
|
+
console.warn(`[config] invalid advisor: ${JSON.stringify(value)} in ${path2}, using ${DEFAULT_ADVISOR_ENABLED}`);
|
|
2191
|
+
return DEFAULT_ADVISOR_ENABLED;
|
|
2192
|
+
}
|
|
2193
|
+
function parsePermission(raw, path2) {
|
|
2194
|
+
if (!("permission" in raw)) return DEFAULT_PERMISSION_MODE;
|
|
2195
|
+
const value = raw["permission"];
|
|
2196
|
+
if (typeof value === "string" && PERMISSION_MODES.includes(value)) return value;
|
|
2197
|
+
console.warn(`[config] invalid permission: ${JSON.stringify(value)} in ${path2}, using "${DEFAULT_PERMISSION_MODE}"`);
|
|
2198
|
+
return DEFAULT_PERMISSION_MODE;
|
|
2199
|
+
}
|
|
2200
|
+
function loadUserConfig() {
|
|
2201
|
+
const path2 = getUserConfigPath();
|
|
2202
|
+
const raw = readRawConfig();
|
|
2203
|
+
if (raw === void 0) {
|
|
2204
|
+
throw new UserConfigError(`config.json not found: ${path2}`);
|
|
2205
|
+
}
|
|
2206
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2207
|
+
throw new UserConfigError(`config.json must contain a JSON object: ${path2}`);
|
|
2208
|
+
}
|
|
2209
|
+
if (!("projects" in raw) || typeof raw["projects"] !== "object" || raw["projects"] === null || Array.isArray(raw["projects"])) {
|
|
2210
|
+
throw new UserConfigError(`config.json must contain a "projects" section as an object: ${path2}`);
|
|
2211
|
+
}
|
|
2212
|
+
if ("projectGroups" in raw && (typeof raw["projectGroups"] !== "object" || raw["projectGroups"] === null || Array.isArray(raw["projectGroups"]))) {
|
|
2213
|
+
throw new UserConfigError(`config.json "projectGroups" must be an object: ${path2}`);
|
|
2214
|
+
}
|
|
2215
|
+
const mode = parseMode(raw, path2);
|
|
2216
|
+
const advisor = parseAdvisor(raw, path2);
|
|
2217
|
+
const permission = parsePermission(raw, path2);
|
|
2218
|
+
const rawProjects = raw["projects"];
|
|
2219
|
+
const rawProjectGroups = "projectGroups" in raw ? raw["projectGroups"] : {};
|
|
2220
|
+
const projectKeys = Object.keys(rawProjects);
|
|
2221
|
+
const groupKeys = Object.keys(rawProjectGroups);
|
|
2222
|
+
if (projectKeys.includes(RESERVED_ALL) || groupKeys.includes(RESERVED_ALL)) {
|
|
2223
|
+
throw new UserConfigError(
|
|
2224
|
+
`"${RESERVED_ALL}" is a reserved word and cannot be used as a key in "projects" or "projectGroups"`
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
const groupKeySet = new Set(groupKeys);
|
|
2228
|
+
const duplicateKeys = projectKeys.filter((key) => groupKeySet.has(key));
|
|
2229
|
+
if (duplicateKeys.length > 0) {
|
|
2230
|
+
throw new UserConfigError(
|
|
2231
|
+
`"projects" and "projectGroups" share the same key namespace; duplicate keys are not allowed: ${duplicateKeys.join(", ")}`
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
const projects = {};
|
|
2235
|
+
for (const [name, value] of Object.entries(rawProjects)) {
|
|
2236
|
+
if (name === "__proto__") {
|
|
2237
|
+
throw new UserConfigError(`"__proto__" cannot be used as a key in "projects": ${path2}`);
|
|
2238
|
+
}
|
|
2239
|
+
if (typeof value !== "string" || !isAbsolute2(value)) {
|
|
2240
|
+
console.warn(`[config] invalid projects.${name}: expected an absolute path, skipping`);
|
|
2241
|
+
continue;
|
|
2242
|
+
}
|
|
2243
|
+
if (!isDirectory(value)) {
|
|
2244
|
+
console.warn(`[config] projects.${name} does not exist as a directory: ${value}, skipping`);
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
projects[name] = value;
|
|
2248
|
+
}
|
|
2249
|
+
const projectGroups = {};
|
|
2250
|
+
for (const [groupName, value] of Object.entries(rawProjectGroups)) {
|
|
2251
|
+
if (groupName === "__proto__") {
|
|
2252
|
+
throw new UserConfigError(`"__proto__" cannot be used as a key in "projectGroups": ${path2}`);
|
|
2253
|
+
}
|
|
2254
|
+
if (!Array.isArray(value)) {
|
|
2255
|
+
console.warn(`[config] invalid projectGroups.${groupName}: expected an array, skipping`);
|
|
2256
|
+
continue;
|
|
2257
|
+
}
|
|
2258
|
+
const members = [];
|
|
2259
|
+
for (const member of value) {
|
|
2260
|
+
if (typeof member !== "string" || !Object.prototype.hasOwnProperty.call(projects, member)) {
|
|
2261
|
+
console.warn(`[config] projectGroups.${groupName} references unknown project "${String(member)}", skipping`);
|
|
2262
|
+
continue;
|
|
2263
|
+
}
|
|
2264
|
+
members.push(member);
|
|
2265
|
+
}
|
|
2266
|
+
projectGroups[groupName] = members;
|
|
2267
|
+
}
|
|
2268
|
+
return { mode, advisor, permission, projects, projectGroups };
|
|
2269
|
+
}
|
|
2270
|
+
var cachedRunMode;
|
|
2271
|
+
function getRunMode() {
|
|
2272
|
+
if (cachedRunMode === void 0) {
|
|
2273
|
+
cachedRunMode = readRunMode();
|
|
2274
|
+
}
|
|
2275
|
+
return cachedRunMode;
|
|
2276
|
+
}
|
|
2277
|
+
var cachedAdvisorEnabled;
|
|
2278
|
+
function isAdvisorEnabled() {
|
|
2279
|
+
if (cachedAdvisorEnabled === void 0) {
|
|
2280
|
+
cachedAdvisorEnabled = readAdvisorEnabled();
|
|
2281
|
+
}
|
|
2282
|
+
return cachedAdvisorEnabled;
|
|
2283
|
+
}
|
|
2284
|
+
var cachedPermissionMode;
|
|
2285
|
+
function getPermissionMode() {
|
|
2286
|
+
if (cachedPermissionMode === void 0) {
|
|
2287
|
+
cachedPermissionMode = readTopLevel(parsePermission, DEFAULT_PERMISSION_MODE, "permission");
|
|
2288
|
+
}
|
|
2289
|
+
return cachedPermissionMode;
|
|
2290
|
+
}
|
|
2291
|
+
function readTopLevel(parse, fallback, label) {
|
|
2292
|
+
let raw;
|
|
2293
|
+
try {
|
|
2294
|
+
raw = readRawConfig();
|
|
2295
|
+
} catch (err) {
|
|
2296
|
+
console.warn(`[config] failed to read config file, using default ${label}: ${err}`);
|
|
2297
|
+
return fallback;
|
|
2298
|
+
}
|
|
2299
|
+
if (raw === void 0) return fallback;
|
|
2300
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2301
|
+
return fallback;
|
|
2302
|
+
}
|
|
2303
|
+
return parse(raw, getUserConfigPath());
|
|
2304
|
+
}
|
|
2305
|
+
function readAdvisorEnabled() {
|
|
2306
|
+
return readTopLevel(parseAdvisor, DEFAULT_ADVISOR_ENABLED, "advisor");
|
|
2307
|
+
}
|
|
2308
|
+
function readRunMode() {
|
|
2309
|
+
return readTopLevel(parseMode, DEFAULT_RUN_MODE, "mode");
|
|
2310
|
+
}
|
|
2311
|
+
function findProjectNameByPath(path2) {
|
|
2312
|
+
let config;
|
|
2313
|
+
try {
|
|
2314
|
+
config = loadUserConfig();
|
|
2315
|
+
} catch {
|
|
2316
|
+
return void 0;
|
|
2317
|
+
}
|
|
2318
|
+
const target = resolve(path2);
|
|
2319
|
+
for (const [name, projectPath] of Object.entries(config.projects)) {
|
|
2320
|
+
if (resolve(projectPath) === target) return name;
|
|
2321
|
+
}
|
|
2322
|
+
return void 0;
|
|
2323
|
+
}
|
|
2324
|
+
function resolveTargetProjects(requested, config) {
|
|
2325
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
2326
|
+
for (const name of requested) {
|
|
2327
|
+
if (name === RESERVED_ALL) {
|
|
2328
|
+
for (const [projectName, projectPath] of Object.entries(config.projects)) {
|
|
2329
|
+
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
2330
|
+
}
|
|
2331
|
+
continue;
|
|
2332
|
+
}
|
|
2333
|
+
if (Object.prototype.hasOwnProperty.call(config.projects, name)) {
|
|
2334
|
+
resolved.set(name, { name, path: config.projects[name] });
|
|
2335
|
+
continue;
|
|
2336
|
+
}
|
|
2337
|
+
if (Object.prototype.hasOwnProperty.call(config.projectGroups, name)) {
|
|
2338
|
+
for (const projectName of config.projectGroups[name]) {
|
|
2339
|
+
const projectPath = config.projects[projectName];
|
|
2340
|
+
if (projectPath === void 0) continue;
|
|
2341
|
+
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
2342
|
+
}
|
|
2343
|
+
continue;
|
|
2344
|
+
}
|
|
2345
|
+
const availableProjects = Object.keys(config.projects).join(", ") || "(none)";
|
|
2346
|
+
const availableGroups = Object.keys(config.projectGroups).join(", ") || "(none)";
|
|
2347
|
+
throw new UserConfigError(
|
|
2348
|
+
`Unknown project or group: "${name}". Available projects: ${availableProjects}. Available groups: ${availableGroups}.`
|
|
2349
|
+
);
|
|
2350
|
+
}
|
|
2351
|
+
const resolvedProjects = Array.from(resolved.values());
|
|
2352
|
+
if (resolvedProjects.length === 0) {
|
|
2353
|
+
throw new UserConfigError(`No projects resolved from requested targets: ${requested.join(", ")}`);
|
|
2354
|
+
}
|
|
2355
|
+
return resolvedProjects;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// src/claude-args.ts
|
|
2359
|
+
var CLOUD_REPORT_HEADING = "## claude-task-worker \u5B9F\u884C\u7D50\u679C";
|
|
2360
|
+
var DISALLOWED_TOOLS = [
|
|
2361
|
+
// 遅延 / yield: 後続ウェイクアップ前提。print モードではウェイクアップが発火せず、
|
|
2362
|
+
// 呼ぶと処理未完のままプロセスが終了する。
|
|
2363
|
+
"Monitor",
|
|
2364
|
+
"ScheduleWakeup",
|
|
2365
|
+
// 対話 / 承認: 自律実行セッションには回答・承認するユーザーが存在しない。
|
|
2366
|
+
"AskUserQuestion",
|
|
2367
|
+
"EnterPlanMode",
|
|
2368
|
+
// スコープ外の副作用を伴う自動化: コード修正タスクに用途がなく、ユーザーの
|
|
2369
|
+
// クラウド routine / リモート環境へ副作用を及ぼしうる。
|
|
2370
|
+
"CronCreate",
|
|
2371
|
+
"CronDelete",
|
|
2372
|
+
"CronList",
|
|
2373
|
+
"RemoteTrigger",
|
|
2374
|
+
// 環境管理の競合: ワーカーは locked worktree の残骸問題のため claude 管理の worktree を
|
|
2375
|
+
// 意図的に避け、自前で worktree を生成して cwd として渡している。モデルが worktree を
|
|
2376
|
+
// 作成/切り替えると、この前提とクリーンアップが壊れる。
|
|
2377
|
+
"EnterWorktree"
|
|
2378
|
+
];
|
|
2379
|
+
var DISALLOWED_TOOLS_ARG = DISALLOWED_TOOLS.join(",");
|
|
2380
|
+
var CLAUDE_SPAWN_ENV = {
|
|
2381
|
+
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1",
|
|
2382
|
+
CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: "0"
|
|
2383
|
+
};
|
|
2384
|
+
var SYSTEM_PROMPT_BASE = `\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F \`claude-task-worker\` \u306E\u30EF\u30FC\u30AB\u30FC\u304B\u3089\u81EA\u52D5\u8D77\u52D5\u3055\u308C\u3066\u3044\u308B\uFF08\u5FDC\u7B54\u3067\u304D\u308B\u30E6\u30FC\u30B6\u30FC\u306F\u5E38\u99D0\u3057\u3066\u3044\u306A\u3044\uFF09\u3002\u4EE5\u4E0B\u306E\u81EA\u5F8B\u5B9F\u884C\u539F\u5247\u3092\u5FC5\u305A\u9075\u5B88\u3059\u308B\u3053\u3068\u3002
|
|
2385
|
+
|
|
2386
|
+
- \u30E6\u30FC\u30B6\u30FC\u3078\u306E\u78BA\u8A8D\u30FB\u8CEA\u554F\u306F\u884C\u308F\u305A\u3001\u8D77\u52D5\u3055\u308C\u305F\u30B9\u30AD\u30EB\u306E\u30EB\u30FC\u30EB\u306B\u5F93\u3063\u3066\u81EA\u5F8B\u7684\u306B\u5224\u65AD\u3059\u308B
|
|
2387
|
+
- \u66D6\u6627\u306A\u5834\u5408\u306F\u300C\u3088\u308A\u5B89\u5168\u306A\u5074\uFF08\u7834\u58CA\u7684\u3067\u306A\u3044\u5074\uFF09\u300D\u3092\u9078\u629E\u3057\u3001\u305D\u306E\u5224\u65AD\u3068\u6839\u62E0\u3092\u6700\u7D42\u5831\u544A\u306B\u660E\u8A18\u3059\u308B
|
|
2388
|
+
- \u5168\u30B9\u30C6\u30C3\u30D7\u3092\u5B8C\u9042\u3057\u3066\u304B\u3089\u7D42\u4E86\u3059\u308B\uFF08\u30B9\u30AD\u30EB\u306B\u5B9A\u7FA9\u3055\u308C\u305F\u4E2D\u65AD\u6761\u4EF6\u306B\u8A72\u5F53\u3057\u305F\u5834\u5408\u306E\u307F\u3001\u7406\u7531\u3092\u51FA\u529B\u3057\u3066\u7D42\u4E86\u3059\u308B\uFF09
|
|
2389
|
+
- \u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u4F5C\u696D\u3092\u59D4\u8B72\u3059\u308B\u5834\u5408\u306F\u3001\u4E0A\u8A18\u306E\u539F\u5247\u3092\u59D4\u8B72\u30D7\u30ED\u30F3\u30D7\u30C8\u306B\u3082\u660E\u8A18\u3057\u3066\u4F1D\u3048\u308B
|
|
2390
|
+
- \u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u5B8C\u4E86\u5831\u544A\u306F\u9D5C\u5451\u307F\u306B\u3057\u306A\u3044\u3002\`git diff\` \u7B49\u3067\u5B9F\u969B\u306E\u6210\u679C\u7269\u3092\u691C\u8A3C\u3057\u3066\u304B\u3089\u5B8C\u4E86\u6271\u3044\u306B\u3059\u308B
|
|
2391
|
+
|
|
2392
|
+
\u30B3\u30FC\u30C9\u306E\u63A2\u7D22\u30FB\u8ABF\u67FB\u3067\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
2393
|
+
|
|
2394
|
+
- **CodeGraph \u304C\u4F7F\u3048\u308B\u5834\u5408\u306F \`Grep\`/\`Glob\` \u306B\u3088\u308B\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3088\u308A\u512A\u5148\u3059\u308B**\u3002\u30B7\u30F3\u30DC\u30EB\u306E\u5B9A\u7FA9\u5143\u30FB\u53C2\u7167\u5143\u30FB\u547C\u3073\u51FA\u3057\u95A2\u4FC2\u3092\u69CB\u9020\u3068\u3057\u3066\u8FBF\u308C\u308B\u305F\u3081\u3001\u547D\u540D\u3086\u308C\u306B\u3088\u308B\u53D6\u308A\u3053\u307C\u3057\u304C\u8D77\u304D\u306B\u304F\u304F\u3001\u5FC5\u8981\u306A\u60C5\u5831\u306B\u5C11\u306A\u3044\u8A66\u884C\u3067\u5230\u9054\u3067\u304D\u308B
|
|
2395
|
+
- **\u5229\u7528\u53EF\u5426\u306F codegraph \u7CFB\u306E MCP \u30C4\u30FC\u30EB\uFF08\`codegraph_explore\` \u7B49\uFF09\u304C\u81EA\u5206\u306B\u4E0E\u3048\u3089\u308C\u3066\u3044\u308B\u304B\u3060\u3051\u3067\u5224\u65AD\u3059\u308B**\u3002\u7121\u3051\u308C\u3070\u300C\u5229\u7528\u4E0D\u53EF\u300D\u3068\u5373\u65AD\u3057\u3066\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3078\u9032\u307F\u3001\u5224\u5B9A\u306B\u624B\u9593\u3092\u304B\u3051\u306A\u3044
|
|
2396
|
+
- \u672A\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3067\u306F MCP \u30C4\u30FC\u30EB\u304C\u3042\u3063\u3066\u3082\u30A8\u30E9\u30FC\u3084\u7A7A\u306E\u7D50\u679C\u304C\u8FD4\u308B\u3002\u305D\u306E\u5834\u5408\u3082\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3078\u5207\u308A\u66FF\u3048\u308B\u3060\u3051\u3067\u3088\u304F\u3001\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3092\u7528\u610F\u3057\u3088\u3046\u3068\u3057\u306A\u3044\uFF08\u30BF\u30B9\u30AF\u306E\u8CAC\u52D9\u5916\uFF09
|
|
2397
|
+
- CodeGraph \u304C\u8FD4\u3057\u305F\u30BD\u30FC\u30B9\u306F\u300C\u8AAD\u307F\u7D42\u3048\u305F\u3082\u306E\u300D\u3068\u3057\u3066\u6271\u3044\u3001\u540C\u3058\u7B87\u6240\u3092 \`Grep\`/\`Read\` \u3067\u88CF\u53D6\u308A\u3057\u76F4\u3055\u306A\u3044\u3002\u305F\u3060\u3057\u51FA\u529B\u306B staleness\uFF08\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u304C\u53E4\u3044\u65E8\uFF09\u306E\u8B66\u544A\u304C\u51FA\u3066\u3044\u308B\u5834\u5408\u306F\u8A72\u5F53\u30D5\u30A1\u30A4\u30EB\u3092 \`Read\` \u3057\u3066\u73FE\u7269\u3092\u78BA\u8A8D\u3059\u308B
|
|
2398
|
+
- \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u30FB\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u30FB\u30B3\u30E1\u30F3\u30C8/\u6587\u5B57\u5217\u30EA\u30C6\u30E9\u30EB\u30FB\u672A\u5BFE\u5FDC\u8A00\u8A9E\u306A\u3069 CodeGraph \u304C\u6271\u308F\u306A\u3044\u5BFE\u8C61\u306F\u3001\u5F93\u6765\u3069\u304A\u308A\u30C6\u30AD\u30B9\u30C8\u691C\u7D22\u3067\u88DC\u3046
|
|
2399
|
+
- \u63A2\u7D22\u3092\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u59D4\u8B72\u3059\u308B\u5834\u5408\u306F\u3001\u3053\u306E\u65B9\u91DD\u3082\u59D4\u8B72\u30D7\u30ED\u30F3\u30D7\u30C8\u306B\u660E\u8A18\u3057\u3066\u4F1D\u3048\u308B`;
|
|
2400
|
+
var OPUS_SYSTEM_PROMPT_ADDENDUM = `\u6210\u679C\u7269\u306E\u5206\u91CF\u3068\u30B9\u30B3\u30FC\u30D7\u306B\u3064\u3044\u3066\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
2401
|
+
|
|
2402
|
+
- \u4F9D\u983C\u3055\u308C\u305F\u30B9\u30B3\u30FC\u30D7\u3060\u3051\u3092\u6210\u679C\u7269\u306B\u3059\u308B\u3002\u5468\u8FBA\u306E\u30EA\u30D5\u30A1\u30AF\u30BF\u30FB\u547D\u540D\u6574\u7406\u30FB\u6C17\u3065\u3044\u305F\u5225\u306E\u6539\u5584\u3092\u52DD\u624B\u306B\u8DB3\u3055\u306A\u3044\u3002\u6C17\u3065\u3044\u305F\u70B9\u306F\u5B9F\u88C5\u305B\u305A\u3001\u6700\u7D42\u5831\u544A\u306B1\u884C\u3067\u6319\u3052\u308B\u3060\u3051\u306B\u3059\u308B
|
|
2403
|
+
- \u4F9D\u983C\u306E\u524D\u63D0\u304C\u8AA4\u3063\u3066\u3044\u308B\u3068\u8003\u3048\u308B\u5834\u5408\u3082\u3001\u6307\u6458\u30921-2\u884C\u6DFB\u3048\u305F\u3046\u3048\u3067**\u4F9D\u983C\u3069\u304A\u308A\u306E\u30B9\u30B3\u30FC\u30D7\u3067**\u5B8C\u9042\u3059\u308B\uFF08\u9ED9\u3063\u3066\u7E2E\u5C0F\u30FB\u62E1\u5927\u30FB\u5225\u7269\u3078\u306E\u7F6E\u304D\u63DB\u3048\u3092\u3057\u306A\u3044\uFF09
|
|
2404
|
+
- Issue\u30B3\u30E1\u30F3\u30C8\u30FBPR\u306E\u672C\u6587\u30FBdescription\u30FB\u30EC\u30DD\u30FC\u30C8\u7B49\u306E\u66F8\u304D\u7269\u306F\u3001\u5FC5\u8981\u306A\u5B9F\u8CEA\u3060\u3051\u3092\u66F8\u304F\u3002\u540C\u3058\u5185\u5BB9\u306E\u8A00\u3044\u63DB\u3048\u30FB\u57CB\u3081\u8349\u30BB\u30AF\u30B7\u30E7\u30F3\u30FB\u300C\u8A72\u5F53\u306A\u3057\u300D\u3092\u4E26\u3079\u308B\u3060\u3051\u306E\u7BC0\u3092\u8DB3\u3055\u306A\u3044
|
|
2405
|
+
- \u6700\u7D42\u5831\u544A\u306F\u7D50\u8AD6\u304B\u3089\u66F8\u304F\u30021\u6587\u76EE\u3067\u300C\u4F55\u3092\u3057\u305F\u304B / \u3069\u3053\u3067\u6B62\u307E\u3063\u305F\u304B\u300D\u3092\u8FF0\u3079\u3001\u8A73\u7D30\u3092\u305D\u306E\u5F8C\u306B\u7F6E\u304F
|
|
2406
|
+
|
|
2407
|
+
\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3078\u306E\u59D4\u8B72\u306F\u4EE5\u4E0B\u306B\u5F93\u3046\u3053\u3068\u3002
|
|
2408
|
+
|
|
2409
|
+
- \u81EA\u5206\u3067\u6570\u56DE\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3067\u7D42\u308F\u308B\u4F5C\u696D\u306F\u59D4\u8B72\u3057\u306A\u3044\uFF08\u30D6\u30EA\u30FC\u30D5\u30A3\u30F3\u30B0\u4F5C\u6210\u3068\u59D4\u8B72\u5148\u306E\u518D\u63A2\u7D22\u3067\u30B3\u30B9\u30C8\u3068\u6642\u9593\u304C\u500D\u306B\u306A\u308B\uFF09
|
|
2410
|
+
- \u59D4\u8B72\u3059\u308B\u306E\u306F\u3001\u72EC\u7ACB\u3057\u3066\u4E26\u5217\u5B9F\u884C\u3067\u304D\u308B\u4F5C\u696D\u30FB\u63A2\u7D22\u7BC4\u56F2\u306E\u5E83\u3044\u8ABF\u67FB\u30FB\u5C02\u9580\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u524D\u63D0\u77E5\u8B58\u304C\u8981\u308B\u4F5C\u696D\u306B\u9650\u308B
|
|
2411
|
+
- 1\u30BF\u30B9\u30AF\u306B1\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u30021\u4F53\u3067\u5B8C\u7D50\u3059\u308B\u4F5C\u696D\u306B\u8907\u6570\u4F53\u3092\u91CD\u306D\u3066\u8D77\u52D5\u3057\u306A\u3044
|
|
2412
|
+
- **\u81EA\u5206\u306E\u4F5C\u696D\u306E\u78BA\u8A8D\u30FB\u518D\u30C1\u30A7\u30C3\u30AF\u3092\u76EE\u7684\u306B\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3092\u8D77\u52D5\u3057\u306A\u3044**\u3002\u6210\u679C\u7269\u306E\u691C\u8A3C\u306F \`git diff\` \u3084\u30C6\u30B9\u30C8\u30FBLint\u306E\u5B9F\u884C\u3067\u81EA\u5206\u3067\u884C\u3046`;
|
|
2413
|
+
function systemPromptFor(model) {
|
|
2414
|
+
return isOpusModel(model) ? `${SYSTEM_PROMPT_BASE}
|
|
2415
|
+
|
|
2416
|
+
${OPUS_SYSTEM_PROMPT_ADDENDUM}` : SYSTEM_PROMPT_BASE;
|
|
2417
|
+
}
|
|
2418
|
+
function isOpusModel(model) {
|
|
2419
|
+
return model.toLowerCase().includes("opus");
|
|
2420
|
+
}
|
|
2421
|
+
var CLAUDE_COMMAND = "claude";
|
|
2422
|
+
var cachedSystemPromptFilePaths = /* @__PURE__ */ new Map();
|
|
2423
|
+
function systemPromptFilePath(model) {
|
|
2424
|
+
const variant = isOpusModel(model) ? "opus" : "default";
|
|
2425
|
+
const cached = cachedSystemPromptFilePaths.get(variant);
|
|
2426
|
+
if (cached) return cached;
|
|
2427
|
+
const dir = path.join(os.tmpdir(), "claude-task-worker");
|
|
2428
|
+
mkdirSync(dir, { recursive: true });
|
|
2429
|
+
const target = path.join(dir, `append-system-prompt-${process.pid}-${variant}.txt`);
|
|
2430
|
+
const tmp = `${target}.tmp`;
|
|
2431
|
+
writeFileSync2(tmp, systemPromptFor(model), "utf8");
|
|
2432
|
+
renameSync(tmp, target);
|
|
2433
|
+
cachedSystemPromptFilePaths.set(variant, target);
|
|
2434
|
+
return target;
|
|
2435
|
+
}
|
|
2436
|
+
function buildClaudeArgs({
|
|
2437
|
+
mode,
|
|
2438
|
+
prompt,
|
|
2439
|
+
model,
|
|
2440
|
+
effort,
|
|
2441
|
+
advisorModel,
|
|
2442
|
+
permissionMode,
|
|
2443
|
+
cloud,
|
|
2444
|
+
baseRef,
|
|
2445
|
+
onBranch
|
|
2446
|
+
}) {
|
|
2447
|
+
const ref = baseRef?.trim() ?? "";
|
|
2448
|
+
const onBranchValue = onBranch?.trim() ?? "";
|
|
2449
|
+
if (cloud === true && ref !== "" && onBranchValue !== "") {
|
|
2450
|
+
throw new Error("--on-branch and --ref both set the cloud session's base branch; pass one or the other");
|
|
2451
|
+
}
|
|
2452
|
+
const advisor = advisorModel?.trim() ?? "";
|
|
2453
|
+
const permission = permissionMode ?? DEFAULT_PERMISSION_MODE;
|
|
2454
|
+
return [
|
|
2455
|
+
// default モードはプロンプトを引数で渡す(print モード)。herdr モードでは渡さない:
|
|
2456
|
+
// 引数で渡すと claude が起動と同時に作業を始めてしまい、`herdr agent start` が
|
|
2457
|
+
// 「入力待ちになるまで」ブロックする仕様と噛み合わない(タスクが終わるまで返らず、
|
|
2458
|
+
// 2分を超えると timeout で落ちる)。プロンプトは起動後に `herdr agent prompt` で
|
|
2459
|
+
// 投入し、herdr にターンを追跡させる(herdr-runner.ts の startHerdrTask 参照)。
|
|
2460
|
+
// クラウド実行(`cloud: true`)も print モード非対応のため同様に省く
|
|
2461
|
+
// (実測 T2: `Error: --cloud cannot be combined with --print.`)。
|
|
2462
|
+
...mode === "herdr" || cloud === true ? [] : ["-p", prompt],
|
|
2463
|
+
"--permission-mode",
|
|
2464
|
+
permission,
|
|
2465
|
+
"--disallowedTools",
|
|
2466
|
+
DISALLOWED_TOOLS_ARG,
|
|
2467
|
+
"--append-system-prompt-file",
|
|
2468
|
+
systemPromptFilePath(model),
|
|
2469
|
+
"--model",
|
|
2470
|
+
model,
|
|
2471
|
+
"--effort",
|
|
2472
|
+
effort,
|
|
2473
|
+
// advisor 未指定(空文字)ならフラグごと省く。値なしの `--advisor` を渡すと
|
|
2474
|
+
// 後続フラグを値として食われるため、必ずモデル名とセットでのみ付ける。
|
|
2475
|
+
...advisor === "" ? [] : ["--advisor", advisor],
|
|
2476
|
+
// クラウド実行時は「作成コマンドの共通フラグ」だけをここで返す。`--cloud` 自体は
|
|
2477
|
+
// 付けない(値として渡す description(=クラウドセッションの初期プロンプト。
|
|
2478
|
+
// appendCloudDoneInstruction() 適用後のタスクプロンプトそのもの)は
|
|
2479
|
+
// `src/process-manager.ts` 側でしか決まらないため。`buildCloudCreateArgs()` が
|
|
2480
|
+
// このフラグ列の先頭へ `--cloud <description>` を足して作成コマンドを完成させる)。
|
|
2481
|
+
// ベースブランチ指定は `--ref` / `--on-branch` のどちらか一方のみ(両方指定は上で例外)。
|
|
2482
|
+
...cloud === true && ref !== "" ? ["--ref", ref] : [],
|
|
2483
|
+
...cloud === true && onBranchValue !== "" ? ["--on-branch", onBranchValue] : []
|
|
2484
|
+
];
|
|
2485
|
+
}
|
|
2486
|
+
function buildCloudCreateArgs(commonArgs, prompt) {
|
|
2487
|
+
return ["--cloud", prompt, ...commonArgs];
|
|
2488
|
+
}
|
|
2489
|
+
function appendCloudDoneInstruction(prompt, target) {
|
|
2490
|
+
const targetLabel = target.type === "issue" ? `Issue #${target.number}` : `PR #${target.number}`;
|
|
2491
|
+
const checkoutInstruction = buildCloudCheckoutInstruction(target);
|
|
2492
|
+
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`;
|
|
2493
|
+
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`;
|
|
2494
|
+
return [prompt, checkoutInstruction, reportInstruction, labelInstruction].filter((part) => part !== "").join("\n\n");
|
|
2495
|
+
}
|
|
2496
|
+
function buildCloudCheckoutInstruction(target) {
|
|
2497
|
+
if (target.type !== "pr") return "";
|
|
2498
|
+
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`;
|
|
2291
2499
|
}
|
|
2292
|
-
function
|
|
2293
|
-
|
|
2294
|
-
try {
|
|
2295
|
-
at = loadConfig().lastRun[workerName];
|
|
2296
|
-
} catch (err) {
|
|
2297
|
-
console.warn(`[config] failed to load lastRun, treating ${workerName} as never run: ${err}`);
|
|
2298
|
-
return void 0;
|
|
2299
|
-
}
|
|
2300
|
-
if (at === void 0) return void 0;
|
|
2301
|
-
const parsed = Date.parse(at);
|
|
2302
|
-
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2500
|
+
function buildCloudToolRestriction() {
|
|
2501
|
+
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(", ")}`;
|
|
2303
2502
|
}
|
|
2304
|
-
function
|
|
2305
|
-
const
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
console.warn(`[config] failed to read ${path2}, rewriting it with lastRun only: ${err}`);
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
const current = typeof raw["lastRun"] === "object" && raw["lastRun"] !== null ? raw["lastRun"] : {};
|
|
2318
|
-
raw["lastRun"] = { ...current, [workerName]: at.toISOString() };
|
|
2319
|
-
writeFileSync2(path2, `${JSON.stringify(raw, null, 2)}
|
|
2320
|
-
`, "utf-8");
|
|
2503
|
+
function buildCloudPrompt(prompt, model, target) {
|
|
2504
|
+
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
|
|
2505
|
+
|
|
2506
|
+
${systemPromptFor(model)}
|
|
2507
|
+
|
|
2508
|
+
${buildCloudToolRestriction()}`;
|
|
2509
|
+
const withPrinciples = `${prompt}
|
|
2510
|
+
|
|
2511
|
+
${principles}`;
|
|
2512
|
+
return target ? appendCloudDoneInstruction(withPrinciples, target) : withPrinciples;
|
|
2321
2513
|
}
|
|
2322
|
-
function
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2514
|
+
function shellQuote2(value) {
|
|
2515
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2516
|
+
}
|
|
2517
|
+
function buildClaudeExecution(invocation) {
|
|
2518
|
+
return {
|
|
2519
|
+
command: CLAUDE_COMMAND,
|
|
2520
|
+
args: buildClaudeArgs(invocation),
|
|
2521
|
+
...invocation.mode === "herdr" ? { prompt: invocation.prompt } : {}
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
function buildClaudeEnv(mode, cloud) {
|
|
2525
|
+
const base = mode === "herdr" ? { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS } : { ...CLAUDE_SPAWN_ENV };
|
|
2526
|
+
if (!cloud) return base;
|
|
2527
|
+
return { ...base, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
2329
2528
|
}
|
|
2330
2529
|
|
|
2331
2530
|
// src/git.ts
|
|
@@ -2379,6 +2578,12 @@ init_table();
|
|
|
2379
2578
|
|
|
2380
2579
|
// src/task-result.ts
|
|
2381
2580
|
var STDERR_TAIL_LIMIT = 8 * 1024;
|
|
2581
|
+
var CLOUD_FAILURE_GUIDANCE = "[worker] \u30AF\u30E9\u30A6\u30C9\u5B9F\u884C\u306E\u524D\u63D0\u6761\u4EF6\uFF08GitHub \u9023\u643A / allow_remote_sessions \u7D44\u7E54\u30DD\u30EA\u30B7\u30FC\uFF09\u304C\u6E80\u305F\u3055\u308C\u3066\u3044\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u8A73\u7D30\u306F docs/cloud-prerequisite-checks.md \u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
|
|
2582
|
+
function appendCloudFailureGuidance(result, cloud) {
|
|
2583
|
+
if (!cloud || result.status === "completed") return result;
|
|
2584
|
+
return { ...result, output: `${CLOUD_FAILURE_GUIDANCE}
|
|
2585
|
+
${result.output}` };
|
|
2586
|
+
}
|
|
2382
2587
|
function buildTaskResult(code, stdout, stderrTail) {
|
|
2383
2588
|
const emptyOutput = stdout.trim() === "";
|
|
2384
2589
|
const completed = code === 0 && !emptyOutput;
|
|
@@ -2475,7 +2680,7 @@ function ensureRenderInterval() {
|
|
|
2475
2680
|
async function finishTask(id, result, onComplete) {
|
|
2476
2681
|
try {
|
|
2477
2682
|
await Promise.race([
|
|
2478
|
-
onComplete?.(result.status, result.output) ?? Promise.resolve(),
|
|
2683
|
+
onComplete?.(result.status, result.output, result.cloudSessionId) ?? Promise.resolve(),
|
|
2479
2684
|
new Promise(
|
|
2480
2685
|
(_, reject) => setTimeout(() => reject(new Error("onComplete timed out after 120s")), 12e4).unref()
|
|
2481
2686
|
)
|
|
@@ -2533,7 +2738,198 @@ async function runViaHerdr(args, prompt, id, onComplete, cwd, env) {
|
|
|
2533
2738
|
await finishTask(id, result, onComplete);
|
|
2534
2739
|
herdrTasks.delete(id);
|
|
2535
2740
|
}
|
|
2536
|
-
|
|
2741
|
+
var CLOUD_SESSION_TIMEOUT_MS = Number(process.env.CTW_CLOUD_SESSION_TIMEOUT_MS) || 120 * 1e3;
|
|
2742
|
+
var CLOUD_SESSION_POLL_INTERVAL_MS = 1e3;
|
|
2743
|
+
var CLOUD_POLL_INTERVAL_MS = 30 * 1e3;
|
|
2744
|
+
var CLOUD_TASK_TIMEOUT_MS = Number(process.env.CTW_CLOUD_TASK_TIMEOUT_MS) || 4 * 60 * 60 * 1e3;
|
|
2745
|
+
var cloudWaiters = /* @__PURE__ */ new Map();
|
|
2746
|
+
var cloudPollLoopRunning = false;
|
|
2747
|
+
function ensureCloudPollLoop() {
|
|
2748
|
+
if (cloudPollLoopRunning) return;
|
|
2749
|
+
cloudPollLoopRunning = true;
|
|
2750
|
+
void (async () => {
|
|
2751
|
+
try {
|
|
2752
|
+
await pollCloudWaiters();
|
|
2753
|
+
} finally {
|
|
2754
|
+
cloudPollLoopRunning = false;
|
|
2755
|
+
}
|
|
2756
|
+
})();
|
|
2757
|
+
}
|
|
2758
|
+
async function pollCloudWaiters() {
|
|
2759
|
+
while (cloudWaiters.size > 0) {
|
|
2760
|
+
if (herdrAbortSignal.aborted) {
|
|
2761
|
+
for (const waiter of cloudWaiters.values()) waiter.settle("aborted");
|
|
2762
|
+
cloudWaiters.clear();
|
|
2763
|
+
break;
|
|
2764
|
+
}
|
|
2765
|
+
const pendingTypes = /* @__PURE__ */ new Set();
|
|
2766
|
+
for (const waiter of cloudWaiters.values()) pendingTypes.add(waiter.type);
|
|
2767
|
+
const doneKeys = /* @__PURE__ */ new Map();
|
|
2768
|
+
for (const type of pendingTypes) {
|
|
2769
|
+
try {
|
|
2770
|
+
const numbers = await listNumbersWithLabel(type, CLOUD_DONE_LABEL);
|
|
2771
|
+
doneKeys.set(type, new Set(numbers));
|
|
2772
|
+
} catch (err) {
|
|
2773
|
+
console.error(`[worker] failed to poll ${CLOUD_DONE_LABEL} for ${type}: ${err}`);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
const now = Date.now();
|
|
2777
|
+
for (const [key, waiter] of [...cloudWaiters.entries()]) {
|
|
2778
|
+
const done = doneKeys.get(waiter.type);
|
|
2779
|
+
const number = Number(key.split(":")[1]);
|
|
2780
|
+
if (done?.has(number)) {
|
|
2781
|
+
waiter.settle("completed");
|
|
2782
|
+
cloudWaiters.delete(key);
|
|
2783
|
+
} else if (now >= waiter.deadline) {
|
|
2784
|
+
waiter.settle("timeout");
|
|
2785
|
+
cloudWaiters.delete(key);
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (cloudWaiters.size === 0) break;
|
|
2789
|
+
for (let waited = 0; waited < CLOUD_POLL_INTERVAL_MS; waited += 1e3) {
|
|
2790
|
+
if (herdrAbortSignal.aborted) break;
|
|
2791
|
+
await new Promise((resolve3) => setTimeout(resolve3, 1e3));
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
function waitForCloudTask(id, type) {
|
|
2796
|
+
return new Promise((resolve3) => {
|
|
2797
|
+
cloudWaiters.set(`${type}:${id}`, {
|
|
2798
|
+
type,
|
|
2799
|
+
deadline: Date.now() + CLOUD_TASK_TIMEOUT_MS,
|
|
2800
|
+
settle: resolve3
|
|
2801
|
+
});
|
|
2802
|
+
ensureCloudPollLoop();
|
|
2803
|
+
});
|
|
2804
|
+
}
|
|
2805
|
+
async function flagOrphanedCloudSession(target, id, reason, cloudSessionId) {
|
|
2806
|
+
await addLabel(target, id, "cc-need-human-check").catch((err) => {
|
|
2807
|
+
console.error(`[worker] failed to add cc-need-human-check to ${target} #${id}: ${err}`);
|
|
2808
|
+
});
|
|
2809
|
+
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";
|
|
2810
|
+
const sessionLine = cloudSessionId ? `https://claude.ai/code/${cloudSessionId}` : "\u30BB\u30C3\u30B7\u30E7\u30F3URL\u4E0D\u660E\uFF08ID\u62BD\u51FA\u306B\u5931\u6557\uFF09";
|
|
2811
|
+
const body = `## \u5B64\u7ACB\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u53EF\u80FD\u6027
|
|
2812
|
+
|
|
2813
|
+
**\u539F\u56E0**: ${causeLine}
|
|
2814
|
+
|
|
2815
|
+
**\u30BB\u30C3\u30B7\u30E7\u30F3URL**: ${sessionLine}
|
|
2816
|
+
|
|
2817
|
+
**\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`;
|
|
2818
|
+
const commentFn = target === "issue" ? commentOnIssue : commentOnPR;
|
|
2819
|
+
await commentFn(id, body).catch((err) => {
|
|
2820
|
+
console.error(`[worker] failed to comment on ${target} #${id} about the orphaned cloud session: ${err}`);
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget, model) {
|
|
2824
|
+
const herdrRunnerMod = await Promise.resolve().then(() => (init_herdr_runner(), herdr_runner_exports));
|
|
2825
|
+
const { taskTabLabel: taskTabLabel2, waitForPaneReady: waitForPaneReady3, extractCloudSessionId: extractCloudSessionId2 } = herdrRunnerMod;
|
|
2826
|
+
const herdrMod = await Promise.resolve().then(() => (init_herdr(), herdr_exports));
|
|
2827
|
+
const { tabCreate: tabCreate2, tabClose: tabClose2, paneSendText: paneSendText2, paneSendKeys: paneSendKeys2, paneRead: paneRead2, getCurrentWorkspaceId: getCurrentWorkspaceId2 } = herdrMod;
|
|
2828
|
+
herdrTasks.set(id, { paneId: "", tabId: "" });
|
|
2829
|
+
const label = taskTabLabel2(resolveProjectName(), id);
|
|
2830
|
+
const initialPrompt = buildCloudPrompt(
|
|
2831
|
+
prompt,
|
|
2832
|
+
model ?? "",
|
|
2833
|
+
cloudTarget ? { type: cloudTarget, number: id } : void 0
|
|
2834
|
+
);
|
|
2835
|
+
let result;
|
|
2836
|
+
let cloudSessionId;
|
|
2837
|
+
try {
|
|
2838
|
+
const created = await tabCreate2({ label, cwd: cwd ?? process.cwd(), workspaceId: getCurrentWorkspaceId2(), env });
|
|
2839
|
+
herdrTasks.set(id, created);
|
|
2840
|
+
try {
|
|
2841
|
+
const ready = await waitForPaneReady3(created.paneId, herdrMod);
|
|
2842
|
+
if (!ready) {
|
|
2843
|
+
console.warn(`[worker] pane ${created.paneId} produced no prompt before the timeout, launching anyway`);
|
|
2844
|
+
}
|
|
2845
|
+
const command = ["claude", ...buildCloudCreateArgs(args, initialPrompt)].map(shellQuote2).join(" ");
|
|
2846
|
+
await paneSendText2(created.paneId, command);
|
|
2847
|
+
await paneSendKeys2(created.paneId, "enter");
|
|
2848
|
+
const deadline = Date.now() + CLOUD_SESSION_TIMEOUT_MS;
|
|
2849
|
+
for (; ; ) {
|
|
2850
|
+
if (herdrAbortSignal.aborted) {
|
|
2851
|
+
throw new Error("the worker is shutting down before the cloud session could be created");
|
|
2852
|
+
}
|
|
2853
|
+
let content = "";
|
|
2854
|
+
try {
|
|
2855
|
+
content = await paneRead2(created.paneId);
|
|
2856
|
+
} catch (err) {
|
|
2857
|
+
console.error(`[worker] failed to read pane ${created.paneId} while waiting for the cloud session: ${err}`);
|
|
2858
|
+
}
|
|
2859
|
+
cloudSessionId = extractCloudSessionId2(content);
|
|
2860
|
+
if (cloudSessionId) break;
|
|
2861
|
+
if (Date.now() >= deadline) {
|
|
2862
|
+
throw new Error(`timed out waiting for the cloud session id (pane tail: ${content.slice(-1e3)})`);
|
|
2863
|
+
}
|
|
2864
|
+
await new Promise((resolve3) => setTimeout(resolve3, CLOUD_SESSION_POLL_INTERVAL_MS));
|
|
2865
|
+
}
|
|
2866
|
+
} finally {
|
|
2867
|
+
await tabClose2(created.tabId).catch((err) => {
|
|
2868
|
+
console.error(`[worker] failed to close cloud task tab ${created.tabId}: ${err}`);
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
const createOutput = `[worker] created cloud session ${cloudSessionId} with the task's initial prompt`;
|
|
2872
|
+
if (!cloudTarget) {
|
|
2873
|
+
console.warn(`[worker] #${id} has no completion-detection target, treating session creation as completion`);
|
|
2874
|
+
result = { status: "completed", output: createOutput };
|
|
2875
|
+
} else {
|
|
2876
|
+
const outcome = await waitForCloudTask(id, cloudTarget);
|
|
2877
|
+
if (outcome === "completed") {
|
|
2878
|
+
await removeLabel(cloudTarget, id, CLOUD_DONE_LABEL).catch((err) => {
|
|
2879
|
+
console.error(`[worker] failed to remove ${CLOUD_DONE_LABEL} from ${cloudTarget} #${id}: ${err}`);
|
|
2880
|
+
});
|
|
2881
|
+
let reportBody = null;
|
|
2882
|
+
const startedAt = tasks.get(id)?.startedAt;
|
|
2883
|
+
if (startedAt) {
|
|
2884
|
+
try {
|
|
2885
|
+
reportBody = await findCommentSince(id, startedAt, CLOUD_REPORT_HEADING);
|
|
2886
|
+
} catch (err) {
|
|
2887
|
+
console.error(`[worker] failed to fetch the cloud report comment for ${cloudTarget} #${id}: ${err}`);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
result = {
|
|
2891
|
+
status: "completed",
|
|
2892
|
+
output: reportBody ?? `${createOutput}
|
|
2893
|
+
[worker] detected completion via the ${CLOUD_DONE_LABEL} label`
|
|
2894
|
+
};
|
|
2895
|
+
} else if (outcome === "timeout") {
|
|
2896
|
+
await addLabel(cloudTarget, id, "cc-need-human-check").catch((err) => {
|
|
2897
|
+
console.error(`[worker] failed to add cc-need-human-check to ${cloudTarget} #${id}: ${err}`);
|
|
2898
|
+
});
|
|
2899
|
+
result = {
|
|
2900
|
+
status: "failed",
|
|
2901
|
+
output: `${createOutput}
|
|
2902
|
+
[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.`
|
|
2903
|
+
};
|
|
2904
|
+
} else {
|
|
2905
|
+
await flagOrphanedCloudSession(cloudTarget, id, "shutdown", cloudSessionId);
|
|
2906
|
+
result = {
|
|
2907
|
+
status: "failed",
|
|
2908
|
+
output: `${createOutput}
|
|
2909
|
+
[worker] shutdown aborted the wait for the ${CLOUD_DONE_LABEL} label. The session may still be running; added cc-need-human-check.`
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
result.cloudSessionId = cloudSessionId;
|
|
2914
|
+
} catch (err) {
|
|
2915
|
+
console.error(`[worker] failed to run #${id} via cloud: ${err}`);
|
|
2916
|
+
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).";
|
|
2917
|
+
if (cloudTarget) {
|
|
2918
|
+
await flagOrphanedCloudSession(cloudTarget, id, "session-id", cloudSessionId);
|
|
2919
|
+
orphanNote += " added cc-need-human-check so a human can verify whether it completed on its own.";
|
|
2920
|
+
}
|
|
2921
|
+
result = {
|
|
2922
|
+
status: "failed",
|
|
2923
|
+
output: `[worker] failed to run the task via cloud: ${err}
|
|
2924
|
+
${orphanNote}`,
|
|
2925
|
+
...cloudSessionId ? { cloudSessionId } : {}
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
result = appendCloudFailureGuidance(result, true);
|
|
2929
|
+
await finishTask(id, result, onComplete);
|
|
2930
|
+
herdrTasks.delete(id);
|
|
2931
|
+
}
|
|
2932
|
+
function run(command, args, id, title, workerName, path2, onComplete, cwd, env, prompt, cloud, cloudTarget, model) {
|
|
2537
2933
|
tasks.delete(id);
|
|
2538
2934
|
tasks.set(id, {
|
|
2539
2935
|
id,
|
|
@@ -2546,6 +2942,10 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
|
|
|
2546
2942
|
ensureRenderInterval();
|
|
2547
2943
|
renderTable();
|
|
2548
2944
|
if (getRunMode() === "herdr") {
|
|
2945
|
+
if (cloud) {
|
|
2946
|
+
void runViaCloud(args, prompt ?? "", id, onComplete, cwd, env, cloudTarget, model);
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2549
2949
|
void runViaHerdr(args, prompt ?? "", id, onComplete, cwd, env);
|
|
2550
2950
|
return;
|
|
2551
2951
|
}
|
|
@@ -3058,22 +3458,59 @@ async function buildTokenLimitText() {
|
|
|
3058
3458
|
writeRuncatUsage(usage);
|
|
3059
3459
|
return formatTokenLimitText(usage);
|
|
3060
3460
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3461
|
+
function truncateOutputForNotification(output) {
|
|
3462
|
+
if (output.length <= 1e3) return output;
|
|
3463
|
+
if (output.startsWith(CLOUD_FAILURE_GUIDANCE)) {
|
|
3464
|
+
const rest = output.slice(CLOUD_FAILURE_GUIDANCE.length);
|
|
3465
|
+
return `${CLOUD_FAILURE_GUIDANCE}\u2026${rest.slice(-1e3)}`;
|
|
3466
|
+
}
|
|
3467
|
+
return `\u2026${output.slice(-1e3)}`;
|
|
3468
|
+
}
|
|
3469
|
+
function buildTaskNotificationText(params) {
|
|
3470
|
+
const { status, workerName, repoName, id, title, url, tokenText, output, cloud } = params;
|
|
3471
|
+
const emoji = status === "completed" ? "\u2705" : "\u274C";
|
|
3472
|
+
const label = status === "completed" ? "completed" : "failed";
|
|
3473
|
+
const truncatedOutput = output ? truncateOutputForNotification(output) : output;
|
|
3064
3474
|
const outputBlock = truncatedOutput ? `
|
|
3065
3475
|
\`\`\`${truncatedOutput}\`\`\`` : "";
|
|
3476
|
+
const body = `${emoji} [${workerName}] ${repoName} | Task ${label}: <${url}|#${id} ${title}>${tokenText}${outputBlock}`;
|
|
3477
|
+
let sessionUrlPrefix = "";
|
|
3478
|
+
if (cloud) {
|
|
3479
|
+
sessionUrlPrefix = cloud.sessionId?.trim() ? `https://claude.ai/code/${cloud.sessionId.trim()}
|
|
3480
|
+
` : "\u30BB\u30C3\u30B7\u30E7\u30F3URL\u4E0D\u660E\uFF08ID\u62BD\u51FA\u306B\u5931\u6557\uFF09\n";
|
|
3481
|
+
}
|
|
3482
|
+
return `${sessionUrlPrefix}${body}`;
|
|
3483
|
+
}
|
|
3484
|
+
async function notifyTaskCompleted(workerName, repoName, id, title, url, output, cloud) {
|
|
3485
|
+
const tokenText = await buildTokenLimitText();
|
|
3066
3486
|
await send({
|
|
3067
|
-
text:
|
|
3487
|
+
text: buildTaskNotificationText({
|
|
3488
|
+
status: "completed",
|
|
3489
|
+
workerName,
|
|
3490
|
+
repoName,
|
|
3491
|
+
id,
|
|
3492
|
+
title,
|
|
3493
|
+
url,
|
|
3494
|
+
tokenText,
|
|
3495
|
+
output,
|
|
3496
|
+
cloud
|
|
3497
|
+
})
|
|
3068
3498
|
});
|
|
3069
3499
|
}
|
|
3070
|
-
async function notifyTaskFailed(workerName, repoName, id, title, url, output) {
|
|
3500
|
+
async function notifyTaskFailed(workerName, repoName, id, title, url, output, cloud) {
|
|
3071
3501
|
const tokenText = await buildTokenLimitText();
|
|
3072
|
-
const truncatedOutput = output && output.length > 1e3 ? `\u2026${output.slice(-1e3)}` : output;
|
|
3073
|
-
const outputBlock = truncatedOutput ? `
|
|
3074
|
-
\`\`\`${truncatedOutput}\`\`\`` : "";
|
|
3075
3502
|
await send({
|
|
3076
|
-
text:
|
|
3503
|
+
text: buildTaskNotificationText({
|
|
3504
|
+
status: "failed",
|
|
3505
|
+
workerName,
|
|
3506
|
+
repoName,
|
|
3507
|
+
id,
|
|
3508
|
+
title,
|
|
3509
|
+
url,
|
|
3510
|
+
tokenText,
|
|
3511
|
+
output,
|
|
3512
|
+
cloud
|
|
3513
|
+
})
|
|
3077
3514
|
});
|
|
3078
3515
|
}
|
|
3079
3516
|
async function notifyError(workerName, repoName, error) {
|
|
@@ -3292,6 +3729,7 @@ function createIssuePollingWorker(config) {
|
|
|
3292
3729
|
}
|
|
3293
3730
|
await addLabel("issue", issue.number, "cc-in-progress");
|
|
3294
3731
|
const worktreeId = generateWorktreeName();
|
|
3732
|
+
const cloud = isCloudWorker(config.name);
|
|
3295
3733
|
try {
|
|
3296
3734
|
const issueUrl = `https://github.com/${owner}/${name}/issues/${issue.number}`;
|
|
3297
3735
|
syncDefaultBranch(defaultBranch);
|
|
@@ -3299,6 +3737,11 @@ function createIssuePollingWorker(config) {
|
|
|
3299
3737
|
const command = skill || config.command;
|
|
3300
3738
|
const parentNumber = issue.parent?.number;
|
|
3301
3739
|
const mode = getRunMode();
|
|
3740
|
+
let baseBranch = defaultBranch;
|
|
3741
|
+
if (parentNumber !== void 0) {
|
|
3742
|
+
baseBranch = `cc-epic-${parentNumber}`;
|
|
3743
|
+
await ensureEpicBranch(baseBranch, defaultBranch);
|
|
3744
|
+
}
|
|
3302
3745
|
const execution = buildClaudeExecution({
|
|
3303
3746
|
mode,
|
|
3304
3747
|
prompt: `${command} ${issue.number}`,
|
|
@@ -3306,24 +3749,33 @@ function createIssuePollingWorker(config) {
|
|
|
3306
3749
|
effort,
|
|
3307
3750
|
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3308
3751
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
3309
|
-
permissionMode: getPermissionMode()
|
|
3752
|
+
permissionMode: getPermissionMode(),
|
|
3753
|
+
...cloud ? { cloud: true, baseRef: baseBranch } : {}
|
|
3310
3754
|
});
|
|
3311
|
-
let
|
|
3312
|
-
if (
|
|
3313
|
-
|
|
3314
|
-
|
|
3755
|
+
let cwd;
|
|
3756
|
+
if (cloud) {
|
|
3757
|
+
console.log(
|
|
3758
|
+
`[${config.name}] #${issue.number}: cloud execution, running without worktree on ${baseBranch}`
|
|
3759
|
+
);
|
|
3760
|
+
} else {
|
|
3761
|
+
await createWorktreeFromBranch(worktreeId, baseBranch);
|
|
3762
|
+
cwd = getWorktreePath(worktreeId);
|
|
3763
|
+
console.log(`[${config.name}] #${issue.number}: created worktree ${worktreeId} from ${baseBranch}`);
|
|
3315
3764
|
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3765
|
+
if (cloud) {
|
|
3766
|
+
await removeLabel("issue", issue.number, CLOUD_DONE_LABEL).catch(
|
|
3767
|
+
(err) => console.error(`[${config.name}] removeLabel ${CLOUD_DONE_LABEL} failed for #${issue.number}: ${err}`)
|
|
3768
|
+
);
|
|
3769
|
+
}
|
|
3770
|
+
const startedAt = Date.now();
|
|
3319
3771
|
run(
|
|
3320
3772
|
execution.command,
|
|
3321
3773
|
execution.args,
|
|
3322
3774
|
issue.number,
|
|
3323
3775
|
issue.title,
|
|
3324
3776
|
config.name,
|
|
3325
|
-
worktreeId,
|
|
3326
|
-
async (status, output) => {
|
|
3777
|
+
cloud ? void 0 : worktreeId,
|
|
3778
|
+
async (status, output, cloudSessionId) => {
|
|
3327
3779
|
lastCompletionAt = Date.now();
|
|
3328
3780
|
for (const label of consumableTriggerLabels(config.triggerLabels)) {
|
|
3329
3781
|
await removeLabel("issue", issue.number, label).catch(
|
|
@@ -3332,39 +3784,82 @@ function createIssuePollingWorker(config) {
|
|
|
3332
3784
|
}
|
|
3333
3785
|
try {
|
|
3334
3786
|
if (status === "completed") {
|
|
3335
|
-
const verified = await config.onCompleted?.(issue.number, worktreeId, output
|
|
3787
|
+
const verified = await config.onCompleted?.(issue.number, worktreeId, output, {
|
|
3788
|
+
cloud,
|
|
3789
|
+
baseBranch,
|
|
3790
|
+
startedAt
|
|
3791
|
+
}) ?? true;
|
|
3336
3792
|
if (verified === false) {
|
|
3337
|
-
await notifyTaskFailed(
|
|
3793
|
+
await notifyTaskFailed(
|
|
3794
|
+
config.name,
|
|
3795
|
+
name,
|
|
3796
|
+
issue.number,
|
|
3797
|
+
issue.title,
|
|
3798
|
+
issueUrl,
|
|
3799
|
+
output,
|
|
3800
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3801
|
+
);
|
|
3338
3802
|
} else {
|
|
3339
|
-
await notifyTaskCompleted(
|
|
3803
|
+
await notifyTaskCompleted(
|
|
3804
|
+
config.name,
|
|
3805
|
+
name,
|
|
3806
|
+
issue.number,
|
|
3807
|
+
issue.title,
|
|
3808
|
+
issueUrl,
|
|
3809
|
+
output,
|
|
3810
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3811
|
+
);
|
|
3340
3812
|
}
|
|
3341
3813
|
} else {
|
|
3342
|
-
await notifyTaskFailed(
|
|
3814
|
+
await notifyTaskFailed(
|
|
3815
|
+
config.name,
|
|
3816
|
+
name,
|
|
3817
|
+
issue.number,
|
|
3818
|
+
issue.title,
|
|
3819
|
+
issueUrl,
|
|
3820
|
+
output,
|
|
3821
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3822
|
+
);
|
|
3343
3823
|
}
|
|
3344
3824
|
} catch (err) {
|
|
3345
3825
|
console.error(`[${config.name}] post-task error for #${issue.number}: ${err}`);
|
|
3346
|
-
await notifyTaskFailed(
|
|
3826
|
+
await notifyTaskFailed(
|
|
3827
|
+
config.name,
|
|
3828
|
+
name,
|
|
3829
|
+
issue.number,
|
|
3830
|
+
issue.title,
|
|
3831
|
+
issueUrl,
|
|
3832
|
+
output,
|
|
3833
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3834
|
+
).catch(
|
|
3347
3835
|
(notifyErr) => console.error(`[${config.name}] notifyTaskFailed failed for #${issue.number}: ${notifyErr}`)
|
|
3348
3836
|
);
|
|
3349
3837
|
} finally {
|
|
3350
3838
|
await removeLabel("issue", issue.number, "cc-in-progress").catch(
|
|
3351
3839
|
(err) => console.error(`[${config.name}] removeLabel cc-in-progress failed for #${issue.number}: ${err}`)
|
|
3352
3840
|
);
|
|
3353
|
-
|
|
3354
|
-
(
|
|
3355
|
-
|
|
3841
|
+
if (!cloud) {
|
|
3842
|
+
await removeWorktree(worktreeId).catch(
|
|
3843
|
+
(err) => console.error(`[${config.name}] removeWorktree failed for #${issue.number}: ${err}`)
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
3356
3846
|
}
|
|
3357
3847
|
},
|
|
3358
3848
|
cwd,
|
|
3359
|
-
buildClaudeEnv(mode),
|
|
3360
|
-
execution.prompt
|
|
3849
|
+
buildClaudeEnv(mode, cloud),
|
|
3850
|
+
execution.prompt,
|
|
3851
|
+
cloud,
|
|
3852
|
+
cloud ? "issue" : void 0,
|
|
3853
|
+
model
|
|
3361
3854
|
);
|
|
3362
3855
|
} catch (err) {
|
|
3363
3856
|
console.error(`[${config.name}] setup error for #${issue.number}: ${err}`);
|
|
3364
3857
|
await removeLabel("issue", issue.number, "cc-in-progress").catch(() => {
|
|
3365
3858
|
});
|
|
3366
|
-
|
|
3367
|
-
|
|
3859
|
+
if (!cloud) {
|
|
3860
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3368
3863
|
await notifyError(config.name, name, err);
|
|
3369
3864
|
}
|
|
3370
3865
|
}
|
|
@@ -3387,51 +3882,84 @@ function formatSessionReport(output) {
|
|
|
3387
3882
|
${trimmed.slice(-REPORT_TAIL_LIMIT)}` : trimmed;
|
|
3388
3883
|
return ["```", tail, "```"].join("\n");
|
|
3389
3884
|
}
|
|
3390
|
-
function
|
|
3885
|
+
function selectOwnedClosingPr(candidates, ctx) {
|
|
3886
|
+
const open = candidates.filter((c) => c.state === "MERGED" || c.state === "OPEN");
|
|
3887
|
+
if (!ctx.cloud) {
|
|
3888
|
+
const found2 = open.find((c) => c.headRefName === ctx.expectedHeadRefName);
|
|
3889
|
+
return found2 ? found2.number : null;
|
|
3890
|
+
}
|
|
3891
|
+
const found = open.find((c) => {
|
|
3892
|
+
if (c.baseRefName !== ctx.baseBranch) return false;
|
|
3893
|
+
const createdAt = Date.parse(c.createdAt);
|
|
3894
|
+
return !Number.isNaN(createdAt) && createdAt >= ctx.startedAt && createdAt <= ctx.now;
|
|
3895
|
+
});
|
|
3896
|
+
return found ? found.number : null;
|
|
3897
|
+
}
|
|
3898
|
+
function prMissingComment(worktreeId, output, cloud) {
|
|
3899
|
+
const stateSection = cloud ? [
|
|
3900
|
+
"## \u72B6\u614B\u306E\u78BA\u8A8D",
|
|
3901
|
+
"- \u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u4F5C\u696D\u30D6\u30E9\u30F3\u30C1\u540D\u3092\u81EA\u8EAB\u3067\u6C7A\u3081\u308B\u305F\u3081\u3001\u30ED\u30FC\u30AB\u30EB\u304B\u3089\u306F\u540D\u524D\u304C\u5206\u304B\u308A\u307E\u305B\u3093\u3002claude.ai \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u753B\u9762\u3067\u4F5C\u696D\u30D6\u30E9\u30F3\u30C1\u3068 push \u72B6\u6CC1\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
3902
|
+
] : [
|
|
3903
|
+
"## \u72B6\u614B\u306E\u78BA\u8A8D",
|
|
3904
|
+
`- \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`
|
|
3905
|
+
];
|
|
3391
3906
|
return [
|
|
3392
3907
|
"## 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",
|
|
3393
|
-
`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`,
|
|
3908
|
+
cloud ? "exec-issue \u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u6B63\u5E38\u7D42\u4E86\uFF08exit 0\uFF09\u3057\u307E\u3057\u305F\u304C\u3001\u672CIssue\u3092 closing \u53C2\u7167\u3059\u308BPR\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" : `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`,
|
|
3394
3909
|
"",
|
|
3395
3910
|
"## PR\u3092\u4F5C\u6210\u3057\u306A\u304B\u3063\u305F\u7406\u7531\uFF08\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u7D42\u5831\u544A\uFF09",
|
|
3396
3911
|
formatSessionReport(output),
|
|
3397
3912
|
"",
|
|
3398
|
-
|
|
3399
|
-
`- \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`,
|
|
3913
|
+
...stateSection,
|
|
3400
3914
|
"",
|
|
3401
3915
|
"## \u5BFE\u5FDC\u5F8C\u306E\u9032\u3081\u65B9",
|
|
3402
3916
|
"- \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",
|
|
3403
3917
|
"- \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"
|
|
3404
3918
|
].join("\n");
|
|
3405
3919
|
}
|
|
3920
|
+
async function verifyPrCreated(issueNumber, worktreeId, output, ctx) {
|
|
3921
|
+
if (await hasLabel("issue", issueNumber, "cc-need-human-check")) {
|
|
3922
|
+
console.log(`[exec-issue] #${issueNumber}: cc-need-human-check present, skip cc-pr-created`);
|
|
3923
|
+
return false;
|
|
3924
|
+
}
|
|
3925
|
+
if (await getIssueState(issueNumber) === "CLOSED") {
|
|
3926
|
+
console.log(`[exec-issue] #${issueNumber}: issue closed by skill (no-change path), skip cc-pr-created`);
|
|
3927
|
+
return;
|
|
3928
|
+
}
|
|
3929
|
+
let prNumber = null;
|
|
3930
|
+
if (!ctx.cloud) {
|
|
3931
|
+
prNumber = await findPrNumberByHeadRef(worktreeId, "all");
|
|
3932
|
+
}
|
|
3933
|
+
if (prNumber === null) {
|
|
3934
|
+
const candidates = await listPrsClosingIssue(issueNumber);
|
|
3935
|
+
prNumber = selectOwnedClosingPr(candidates, {
|
|
3936
|
+
cloud: ctx.cloud,
|
|
3937
|
+
expectedHeadRefName: worktreeId,
|
|
3938
|
+
baseBranch: ctx.baseBranch,
|
|
3939
|
+
startedAt: ctx.startedAt,
|
|
3940
|
+
now: Date.now()
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
if (prNumber !== null) {
|
|
3944
|
+
await addLabel("issue", issueNumber, "cc-pr-created");
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
console.error(
|
|
3948
|
+
`[exec-issue] #${issueNumber}: session exited without a PR (branch: ${worktreeId}); marking cc-need-human-check`
|
|
3949
|
+
);
|
|
3950
|
+
await addLabel("issue", issueNumber, "cc-need-human-check");
|
|
3951
|
+
await commentOnIssue(issueNumber, prMissingComment(worktreeId, output, ctx.cloud)).catch(
|
|
3952
|
+
(err) => console.error(`[exec-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
|
|
3953
|
+
);
|
|
3954
|
+
return false;
|
|
3955
|
+
}
|
|
3406
3956
|
var execIssueWorker = (opts = {}) => createIssuePollingWorker({
|
|
3407
3957
|
name: "exec-issue",
|
|
3408
3958
|
command: "/claude-task-worker:exec-issue",
|
|
3409
3959
|
triggerLabels: ["cc-exec-issue"],
|
|
3410
3960
|
epicFilters: opts.epicFilters,
|
|
3411
3961
|
labelFilters: opts.labelFilters,
|
|
3412
|
-
onCompleted:
|
|
3413
|
-
if (await hasLabel("issue", issueNumber, "cc-need-human-check")) {
|
|
3414
|
-
console.log(`[exec-issue] #${issueNumber}: cc-need-human-check present, skip cc-pr-created`);
|
|
3415
|
-
return false;
|
|
3416
|
-
}
|
|
3417
|
-
if (await getIssueState(issueNumber) === "CLOSED") {
|
|
3418
|
-
console.log(`[exec-issue] #${issueNumber}: issue closed by skill (no-change path), skip cc-pr-created`);
|
|
3419
|
-
return;
|
|
3420
|
-
}
|
|
3421
|
-
const prNumber = await findPrNumberByHeadRef(worktreeId, "all") ?? await findPrNumberClosingIssue(issueNumber, worktreeId);
|
|
3422
|
-
if (prNumber !== null) {
|
|
3423
|
-
await addLabel("issue", issueNumber, "cc-pr-created");
|
|
3424
|
-
return;
|
|
3425
|
-
}
|
|
3426
|
-
console.error(
|
|
3427
|
-
`[exec-issue] #${issueNumber}: session exited without a PR (branch: ${worktreeId}); marking cc-need-human-check`
|
|
3428
|
-
);
|
|
3429
|
-
await addLabel("issue", issueNumber, "cc-need-human-check");
|
|
3430
|
-
await commentOnIssue(issueNumber, prMissingComment(worktreeId, output)).catch(
|
|
3431
|
-
(err) => console.error(`[exec-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
|
|
3432
|
-
);
|
|
3433
|
-
return false;
|
|
3434
|
-
}
|
|
3962
|
+
onCompleted: verifyPrCreated
|
|
3435
3963
|
})();
|
|
3436
3964
|
|
|
3437
3965
|
// src/workers/pr-worker.ts
|
|
@@ -3460,21 +3988,24 @@ function createPrPollingWorker(config) {
|
|
|
3460
3988
|
const prUrl = `https://github.com/${owner}/${name}/pull/${pr.number}`;
|
|
3461
3989
|
const hadTriageScope = pr.labels.some((l) => l.name === LABEL_TRIAGE_SCOPE);
|
|
3462
3990
|
await addLabel("pr", pr.number, LABEL_IN_PROGRESS);
|
|
3463
|
-
const
|
|
3991
|
+
const isCloud = isCloudWorker(config.name);
|
|
3992
|
+
const worktreeId = isCloud ? void 0 : generateWorktreeName();
|
|
3464
3993
|
try {
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3994
|
+
if (worktreeId) {
|
|
3995
|
+
await removeWorktreeByBranch(pr.headRefName);
|
|
3996
|
+
await deleteLocalBranch(pr.headRefName);
|
|
3997
|
+
if (await localBranchExists(pr.headRefName)) {
|
|
3998
|
+
console.error(
|
|
3999
|
+
`[${config.name}] PR #${pr.number}: branch ${pr.headRefName} is still checked out by another worktree; skipping this tick`
|
|
4000
|
+
);
|
|
4001
|
+
await removeLabel("pr", pr.number, LABEL_IN_PROGRESS).catch(() => {
|
|
4002
|
+
});
|
|
4003
|
+
continue;
|
|
4004
|
+
}
|
|
4005
|
+
syncDefaultBranch(defaultBranch);
|
|
4006
|
+
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3474
4007
|
}
|
|
3475
|
-
|
|
3476
|
-
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3477
|
-
const cwd = getWorktreePath(worktreeId);
|
|
4008
|
+
const cwd = worktreeId ? getWorktreePath(worktreeId) : void 0;
|
|
3478
4009
|
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
3479
4010
|
const command = skill || config.command;
|
|
3480
4011
|
const mode = getRunMode();
|
|
@@ -3485,8 +4016,15 @@ function createPrPollingWorker(config) {
|
|
|
3485
4016
|
effort,
|
|
3486
4017
|
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3487
4018
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
3488
|
-
permissionMode: getPermissionMode()
|
|
4019
|
+
permissionMode: getPermissionMode(),
|
|
4020
|
+
cloud: isCloud,
|
|
4021
|
+
onBranch: isCloud ? pr.headRefName : void 0
|
|
3489
4022
|
});
|
|
4023
|
+
if (isCloud) {
|
|
4024
|
+
await removeLabel("pr", pr.number, CLOUD_DONE_LABEL).catch(
|
|
4025
|
+
(err) => console.error(`[${config.name}] removeLabel ${CLOUD_DONE_LABEL} failed for PR #${pr.number}: ${err}`)
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
3490
4028
|
run(
|
|
3491
4029
|
execution.command,
|
|
3492
4030
|
execution.args,
|
|
@@ -3494,14 +4032,30 @@ function createPrPollingWorker(config) {
|
|
|
3494
4032
|
`PR #${pr.number} (${pr.headRefName})`,
|
|
3495
4033
|
config.name,
|
|
3496
4034
|
worktreeId,
|
|
3497
|
-
async (status, output) => {
|
|
4035
|
+
async (status, output, cloudSessionId) => {
|
|
3498
4036
|
lastCompletionAt = Date.now();
|
|
3499
4037
|
try {
|
|
3500
4038
|
if (status === "completed") {
|
|
3501
4039
|
await config.onCompleted?.(pr, output);
|
|
3502
|
-
await notifyTaskCompleted(
|
|
4040
|
+
await notifyTaskCompleted(
|
|
4041
|
+
config.name,
|
|
4042
|
+
name,
|
|
4043
|
+
pr.number,
|
|
4044
|
+
pr.title,
|
|
4045
|
+
prUrl,
|
|
4046
|
+
output,
|
|
4047
|
+
isCloud ? { sessionId: cloudSessionId } : void 0
|
|
4048
|
+
);
|
|
3503
4049
|
} else {
|
|
3504
|
-
await notifyTaskFailed(
|
|
4050
|
+
await notifyTaskFailed(
|
|
4051
|
+
config.name,
|
|
4052
|
+
name,
|
|
4053
|
+
pr.number,
|
|
4054
|
+
pr.title,
|
|
4055
|
+
prUrl,
|
|
4056
|
+
output,
|
|
4057
|
+
isCloud ? { sessionId: cloudSessionId } : void 0
|
|
4058
|
+
);
|
|
3505
4059
|
}
|
|
3506
4060
|
} catch (err) {
|
|
3507
4061
|
console.error(`[${config.name}] post-task error for PR #${pr.number}: ${err}`);
|
|
@@ -3528,21 +4082,28 @@ function createPrPollingWorker(config) {
|
|
|
3528
4082
|
`[${config.name}] removeLabel ${LABEL_IN_PROGRESS} failed for PR #${pr.number}: ${err}`
|
|
3529
4083
|
)
|
|
3530
4084
|
);
|
|
3531
|
-
|
|
3532
|
-
(
|
|
3533
|
-
|
|
4085
|
+
if (worktreeId) {
|
|
4086
|
+
await removeWorktree(worktreeId).catch(
|
|
4087
|
+
(err) => console.error(`[${config.name}] removeWorktree failed for PR #${pr.number}: ${err}`)
|
|
4088
|
+
);
|
|
4089
|
+
}
|
|
3534
4090
|
}
|
|
3535
4091
|
},
|
|
3536
4092
|
cwd,
|
|
3537
|
-
buildClaudeEnv(mode),
|
|
3538
|
-
execution.prompt
|
|
4093
|
+
buildClaudeEnv(mode, isCloud),
|
|
4094
|
+
execution.prompt,
|
|
4095
|
+
isCloud,
|
|
4096
|
+
isCloud ? "pr" : void 0,
|
|
4097
|
+
model
|
|
3539
4098
|
);
|
|
3540
4099
|
} catch (err) {
|
|
3541
4100
|
console.error(`[${config.name}] setup error for PR #${pr.number}: ${err}`);
|
|
3542
4101
|
await removeLabel("pr", pr.number, LABEL_IN_PROGRESS).catch(() => {
|
|
3543
4102
|
});
|
|
3544
|
-
|
|
3545
|
-
|
|
4103
|
+
if (worktreeId) {
|
|
4104
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
4105
|
+
});
|
|
4106
|
+
}
|
|
3546
4107
|
await notifyError(config.name, name, err);
|
|
3547
4108
|
}
|
|
3548
4109
|
}
|
|
@@ -4049,9 +4610,11 @@ function createScheduledWorker(config) {
|
|
|
4049
4610
|
if (lastRunAt > 0 && now - lastRunAt < SCHEDULE_INTERVAL_MS) return;
|
|
4050
4611
|
const worktreeId = generateWorktreeName();
|
|
4051
4612
|
startedAt = now;
|
|
4613
|
+
let cloud = false;
|
|
4052
4614
|
try {
|
|
4053
4615
|
syncDefaultBranch(defaultBranch);
|
|
4054
4616
|
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
4617
|
+
cloud = isCloudWorker(config.name);
|
|
4055
4618
|
const command = skill || config.command;
|
|
4056
4619
|
const mode = getRunMode();
|
|
4057
4620
|
const execution = buildClaudeExecution({
|
|
@@ -4060,14 +4623,20 @@ function createScheduledWorker(config) {
|
|
|
4060
4623
|
model,
|
|
4061
4624
|
effort,
|
|
4062
4625
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
4063
|
-
permissionMode: getPermissionMode()
|
|
4626
|
+
permissionMode: getPermissionMode(),
|
|
4627
|
+
...cloud ? { cloud: true, baseRef: defaultBranch } : {}
|
|
4064
4628
|
});
|
|
4065
4629
|
await publishLastRunPr(config.name, defaultBranch, new Date(now)).catch(
|
|
4066
4630
|
(err) => console.error(`[${config.name}] publishLastRunPr failed: ${err}`)
|
|
4067
4631
|
);
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4632
|
+
let cwd;
|
|
4633
|
+
if (cloud) {
|
|
4634
|
+
console.log(`[${config.name}] cloud execution, running without worktree on ${defaultBranch}`);
|
|
4635
|
+
} else {
|
|
4636
|
+
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
4637
|
+
cwd = getWorktreePath(worktreeId);
|
|
4638
|
+
console.log(`[${config.name}] created worktree ${worktreeId} from ${defaultBranch}`);
|
|
4639
|
+
}
|
|
4071
4640
|
const repoUrl = `https://github.com/${owner}/${repoName}`;
|
|
4072
4641
|
run(
|
|
4073
4642
|
execution.command,
|
|
@@ -4075,7 +4644,7 @@ function createScheduledWorker(config) {
|
|
|
4075
4644
|
config.taskId,
|
|
4076
4645
|
config.name,
|
|
4077
4646
|
config.name,
|
|
4078
|
-
worktreeId,
|
|
4647
|
+
cloud ? void 0 : worktreeId,
|
|
4079
4648
|
async (status, output) => {
|
|
4080
4649
|
try {
|
|
4081
4650
|
if (status === "completed") {
|
|
@@ -4086,19 +4655,26 @@ function createScheduledWorker(config) {
|
|
|
4086
4655
|
} catch (err) {
|
|
4087
4656
|
console.error(`[${config.name}] post-task error: ${err}`);
|
|
4088
4657
|
} finally {
|
|
4089
|
-
|
|
4090
|
-
(
|
|
4091
|
-
|
|
4658
|
+
if (!cloud) {
|
|
4659
|
+
await removeWorktree(worktreeId).catch(
|
|
4660
|
+
(err) => console.error(`[${config.name}] removeWorktree failed: ${err}`)
|
|
4661
|
+
);
|
|
4662
|
+
}
|
|
4092
4663
|
}
|
|
4093
4664
|
},
|
|
4094
4665
|
cwd,
|
|
4095
|
-
buildClaudeEnv(mode),
|
|
4096
|
-
execution.prompt
|
|
4666
|
+
buildClaudeEnv(mode, cloud),
|
|
4667
|
+
execution.prompt,
|
|
4668
|
+
cloud,
|
|
4669
|
+
void 0,
|
|
4670
|
+
model
|
|
4097
4671
|
);
|
|
4098
4672
|
} catch (err) {
|
|
4099
4673
|
console.error(`[${config.name}] setup error: ${err}`);
|
|
4100
|
-
|
|
4101
|
-
|
|
4674
|
+
if (!cloud) {
|
|
4675
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4102
4678
|
await notifyError(config.name, repoName, err);
|
|
4103
4679
|
}
|
|
4104
4680
|
};
|
|
@@ -4256,8 +4832,10 @@ var LABELS = [
|
|
|
4256
4832
|
// vivid amber (H44 S100 L57 / L*83 C*79)
|
|
4257
4833
|
{ name: "cc-ui-design-pr-created", color: "947100" },
|
|
4258
4834
|
// vivid bronze (H46 S100 L29 / L*50 C*56)
|
|
4259
|
-
{ name: "cc-ui-design-ready", color: "0c73e9" }
|
|
4835
|
+
{ name: "cc-ui-design-ready", color: "0c73e9" },
|
|
4260
4836
|
// vivid azure (H212 S90 L48 / L*50 C*69)
|
|
4837
|
+
{ name: CLOUD_DONE_LABEL, color: "33cfff" }
|
|
4838
|
+
// vivid sky blue (H194 S100 L60 / L*78 C*42)
|
|
4261
4839
|
];
|
|
4262
4840
|
var ISSUE_TEMPLATE = `name: "[claude-task-worker] Issue\u4F5C\u6210\u4F9D\u983C"
|
|
4263
4841
|
description: claude-task-worker\u3067GitHub Issue\u3092\u4F5C\u6210\u3059\u308B
|
|
@@ -4604,50 +5182,10 @@ function version() {
|
|
|
4604
5182
|
}
|
|
4605
5183
|
}
|
|
4606
5184
|
|
|
4607
|
-
// src/dispatch-args.ts
|
|
4608
|
-
var PROJECT_INCOMPATIBLE_COMMANDS = ["init", "install", "update", "usage", "version"];
|
|
4609
|
-
function collectFlagValues(argv, flag) {
|
|
4610
|
-
const values = [];
|
|
4611
|
-
for (let i = 0; i < argv.length; i++) {
|
|
4612
|
-
if (argv[i] !== flag) continue;
|
|
4613
|
-
const raw = argv[i + 1];
|
|
4614
|
-
if (!raw || raw.startsWith("--")) {
|
|
4615
|
-
console.error(`[dispatcher] ${flag} requires a value`);
|
|
4616
|
-
process.exit(1);
|
|
4617
|
-
}
|
|
4618
|
-
values.push(raw);
|
|
4619
|
-
}
|
|
4620
|
-
return values;
|
|
4621
|
-
}
|
|
4622
|
-
function parseProjectFilters() {
|
|
4623
|
-
return collectFlagValues(process.argv, "--project");
|
|
4624
|
-
}
|
|
4625
|
-
function hasProjectFilter() {
|
|
4626
|
-
return process.argv.includes("--project");
|
|
4627
|
-
}
|
|
4628
|
-
function assertProjectCompatibleCommand(command) {
|
|
4629
|
-
if (PROJECT_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
4630
|
-
console.error(`[dispatcher] --project cannot be used with the "${command}" command`);
|
|
4631
|
-
process.exit(1);
|
|
4632
|
-
}
|
|
4633
|
-
}
|
|
4634
|
-
function shellQuote(value) {
|
|
4635
|
-
if (value === "") return "''";
|
|
4636
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4637
|
-
}
|
|
4638
|
-
function buildForwardedCommand(argv) {
|
|
4639
|
-
const tokens = [];
|
|
4640
|
-
for (let i = 0; i < argv.length; i++) {
|
|
4641
|
-
if (argv[i] === "--project") {
|
|
4642
|
-
i++;
|
|
4643
|
-
continue;
|
|
4644
|
-
}
|
|
4645
|
-
tokens.push(argv[i]);
|
|
4646
|
-
}
|
|
4647
|
-
return ["claude-task-worker", ...tokens.map(shellQuote)].join(" ");
|
|
4648
|
-
}
|
|
4649
|
-
|
|
4650
5185
|
// src/index.ts
|
|
5186
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
5187
|
+
import { promisify as promisify5 } from "node:util";
|
|
5188
|
+
var execFileAsync4 = promisify5(execFile4);
|
|
4651
5189
|
var WORKERS = {
|
|
4652
5190
|
"exec-issue": execIssueWorker,
|
|
4653
5191
|
"fix-review-point": fixReviewPointWorker,
|
|
@@ -4669,7 +5207,7 @@ function printUsage() {
|
|
|
4669
5207
|
console.log(`Usage: claude-task-worker <command> [--project <name>] [--epic <issue-number>] [--label <label-name>]
|
|
4670
5208
|
|
|
4671
5209
|
Commands:
|
|
4672
|
-
init [--force]
|
|
5210
|
+
init [--force] Create required GitHub labels and config file (use --force to overwrite existing files)
|
|
4673
5211
|
install Add the claude-task-worker marketplace, install the plugin, and install/update the CLI
|
|
4674
5212
|
update Update the claude-task-worker plugin/marketplace and the CLI itself
|
|
4675
5213
|
usage Notify current usage to Slack
|
|
@@ -4730,6 +5268,9 @@ if (workerType !== "all" && workerType !== "yolo" && workerType !== "init" && wo
|
|
|
4730
5268
|
if (hasProjectFilter()) {
|
|
4731
5269
|
assertProjectCompatibleCommand(workerType);
|
|
4732
5270
|
}
|
|
5271
|
+
if (hasCloudFlag()) {
|
|
5272
|
+
assertCloudCompatibleCommand(workerType);
|
|
5273
|
+
}
|
|
4733
5274
|
function collectFlagValues2(flag) {
|
|
4734
5275
|
const values = [];
|
|
4735
5276
|
for (let i = 0; i < process.argv.length; i++) {
|
|
@@ -4773,10 +5314,60 @@ async function assertRunModeAvailable() {
|
|
|
4773
5314
|
}
|
|
4774
5315
|
console.log("[worker] run mode: herdr (each task runs as a TUI session in its own herdr tab)");
|
|
4775
5316
|
}
|
|
5317
|
+
async function readCloudAuthStatus() {
|
|
5318
|
+
let stdout;
|
|
5319
|
+
try {
|
|
5320
|
+
const result = await execFileAsync4("claude", ["auth", "status", "--json"]);
|
|
5321
|
+
stdout = result.stdout;
|
|
5322
|
+
} catch (err) {
|
|
5323
|
+
const maybeStdout = err.stdout;
|
|
5324
|
+
if (!maybeStdout) return { kind: "unknown" };
|
|
5325
|
+
stdout = maybeStdout;
|
|
5326
|
+
}
|
|
5327
|
+
try {
|
|
5328
|
+
const parsed = JSON.parse(stdout);
|
|
5329
|
+
return {
|
|
5330
|
+
kind: "ok",
|
|
5331
|
+
loggedIn: parsed.loggedIn === true,
|
|
5332
|
+
authMethod: String(parsed.authMethod ?? ""),
|
|
5333
|
+
apiProvider: String(parsed.apiProvider ?? ""),
|
|
5334
|
+
apiKeySource: typeof parsed.apiKeySource === "string" ? parsed.apiKeySource : void 0
|
|
5335
|
+
};
|
|
5336
|
+
} catch {
|
|
5337
|
+
return { kind: "unknown" };
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
async function assertCloudAvailable() {
|
|
5341
|
+
const cloud = hasCloudFlag();
|
|
5342
|
+
const labelReady = cloud ? await createLabel(CLOUD_DONE_LABEL, "33cfff", true) : true;
|
|
5343
|
+
const status = cloud ? await readCloudAuthStatus() : void 0;
|
|
5344
|
+
const errors = checkCloudConfig({
|
|
5345
|
+
cloud,
|
|
5346
|
+
mode: getRunMode(),
|
|
5347
|
+
auth: status ? { status, baseUrl: process.env.ANTHROPIC_BASE_URL } : void 0
|
|
5348
|
+
});
|
|
5349
|
+
if (!labelReady) {
|
|
5350
|
+
errors.push(
|
|
5351
|
+
`${CLOUD_DONE_LABEL} \u30E9\u30D9\u30EB\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002gh \u306E\u8A8D\u8A3C\u30FB\u6A29\u9650\u3092\u78BA\u8A8D\u3059\u308B\u304B\u3001claude-task-worker init \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
5352
|
+
);
|
|
5353
|
+
}
|
|
5354
|
+
if (errors.length > 0) {
|
|
5355
|
+
for (const message of errors) {
|
|
5356
|
+
console.error(`[worker] ${message}`);
|
|
5357
|
+
}
|
|
5358
|
+
process.exit(1);
|
|
5359
|
+
}
|
|
5360
|
+
if (cloud) {
|
|
5361
|
+
console.log(
|
|
5362
|
+
`[worker] cloud execution enabled (--cloud); these workers stay local: ${CLOUD_DENIED_WORKERS.join(", ")}`
|
|
5363
|
+
);
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
4776
5366
|
async function assertRunPrerequisites() {
|
|
4777
5367
|
captureConsole();
|
|
4778
5368
|
ensureRenderInterval();
|
|
4779
5369
|
await assertRunModeAvailable();
|
|
5370
|
+
await assertCloudAvailable();
|
|
4780
5371
|
}
|
|
4781
5372
|
if (!hasProjectFilter()) {
|
|
4782
5373
|
process.on("SIGTERM", async () => {
|
|
@@ -4843,7 +5434,8 @@ if (hasProjectFilter()) {
|
|
|
4843
5434
|
}
|
|
4844
5435
|
})();
|
|
4845
5436
|
} else if (workerType === "init") {
|
|
4846
|
-
const
|
|
5437
|
+
const initArgs = process.argv.slice(3);
|
|
5438
|
+
const force = initArgs.includes("--force");
|
|
4847
5439
|
init({ force });
|
|
4848
5440
|
} else if (workerType === "install") {
|
|
4849
5441
|
(async () => {
|