claude-task-worker 0.94.0 → 0.96.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 +1122 -539
- 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: {} };
|
|
@@ -2301,31 +2092,438 @@ function getLastRunAt(workerName) {
|
|
|
2301
2092
|
const parsed = Date.parse(at);
|
|
2302
2093
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2303
2094
|
}
|
|
2304
|
-
function writeLastRun(repoRoot, workerName, at = /* @__PURE__ */ new Date()) {
|
|
2305
|
-
const path2 =
|
|
2306
|
-
let raw = {};
|
|
2307
|
-
try {
|
|
2308
|
-
const parsed = JSON.parse(
|
|
2309
|
-
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2310
|
-
raw = parsed;
|
|
2311
|
-
}
|
|
2312
|
-
} catch (err) {
|
|
2313
|
-
if (err.code !== "ENOENT") {
|
|
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
|
-
|
|
2320
|
-
`, "utf-8");
|
|
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 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`;
|
|
2492
|
+
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`;
|
|
2493
|
+
return `${prompt}
|
|
2494
|
+
|
|
2495
|
+
${reportInstruction}
|
|
2496
|
+
|
|
2497
|
+
${labelInstruction}`;
|
|
2498
|
+
}
|
|
2499
|
+
function buildCloudToolRestriction() {
|
|
2500
|
+
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(", ")}`;
|
|
2321
2501
|
}
|
|
2322
|
-
function
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
}
|
|
2502
|
+
function buildCloudPrompt(prompt, model, target) {
|
|
2503
|
+
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
|
|
2504
|
+
|
|
2505
|
+
${systemPromptFor(model)}
|
|
2506
|
+
|
|
2507
|
+
${buildCloudToolRestriction()}`;
|
|
2508
|
+
const withPrinciples = `${prompt}
|
|
2509
|
+
|
|
2510
|
+
${principles}`;
|
|
2511
|
+
return target ? appendCloudDoneInstruction(withPrinciples, target) : withPrinciples;
|
|
2512
|
+
}
|
|
2513
|
+
function shellQuote2(value) {
|
|
2514
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
2515
|
+
}
|
|
2516
|
+
function buildClaudeExecution(invocation) {
|
|
2517
|
+
return {
|
|
2518
|
+
command: CLAUDE_COMMAND,
|
|
2519
|
+
args: buildClaudeArgs(invocation),
|
|
2520
|
+
...invocation.mode === "herdr" ? { prompt: invocation.prompt } : {}
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
function buildClaudeEnv(mode, cloud) {
|
|
2524
|
+
const base = mode === "herdr" ? { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS } : { ...CLAUDE_SPAWN_ENV };
|
|
2525
|
+
if (!cloud) return base;
|
|
2526
|
+
return { ...base, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
2329
2527
|
}
|
|
2330
2528
|
|
|
2331
2529
|
// src/git.ts
|
|
@@ -2379,6 +2577,12 @@ init_table();
|
|
|
2379
2577
|
|
|
2380
2578
|
// src/task-result.ts
|
|
2381
2579
|
var STDERR_TAIL_LIMIT = 8 * 1024;
|
|
2580
|
+
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";
|
|
2581
|
+
function appendCloudFailureGuidance(result, cloud) {
|
|
2582
|
+
if (!cloud || result.status === "completed") return result;
|
|
2583
|
+
return { ...result, output: `${CLOUD_FAILURE_GUIDANCE}
|
|
2584
|
+
${result.output}` };
|
|
2585
|
+
}
|
|
2382
2586
|
function buildTaskResult(code, stdout, stderrTail) {
|
|
2383
2587
|
const emptyOutput = stdout.trim() === "";
|
|
2384
2588
|
const completed = code === 0 && !emptyOutput;
|
|
@@ -2475,7 +2679,7 @@ function ensureRenderInterval() {
|
|
|
2475
2679
|
async function finishTask(id, result, onComplete) {
|
|
2476
2680
|
try {
|
|
2477
2681
|
await Promise.race([
|
|
2478
|
-
onComplete?.(result.status, result.output) ?? Promise.resolve(),
|
|
2682
|
+
onComplete?.(result.status, result.output, result.cloudSessionId) ?? Promise.resolve(),
|
|
2479
2683
|
new Promise(
|
|
2480
2684
|
(_, reject) => setTimeout(() => reject(new Error("onComplete timed out after 120s")), 12e4).unref()
|
|
2481
2685
|
)
|
|
@@ -2533,7 +2737,198 @@ async function runViaHerdr(args, prompt, id, onComplete, cwd, env) {
|
|
|
2533
2737
|
await finishTask(id, result, onComplete);
|
|
2534
2738
|
herdrTasks.delete(id);
|
|
2535
2739
|
}
|
|
2536
|
-
|
|
2740
|
+
var CLOUD_SESSION_TIMEOUT_MS = Number(process.env.CTW_CLOUD_SESSION_TIMEOUT_MS) || 120 * 1e3;
|
|
2741
|
+
var CLOUD_SESSION_POLL_INTERVAL_MS = 1e3;
|
|
2742
|
+
var CLOUD_POLL_INTERVAL_MS = 30 * 1e3;
|
|
2743
|
+
var CLOUD_TASK_TIMEOUT_MS = Number(process.env.CTW_CLOUD_TASK_TIMEOUT_MS) || 4 * 60 * 60 * 1e3;
|
|
2744
|
+
var cloudWaiters = /* @__PURE__ */ new Map();
|
|
2745
|
+
var cloudPollLoopRunning = false;
|
|
2746
|
+
function ensureCloudPollLoop() {
|
|
2747
|
+
if (cloudPollLoopRunning) return;
|
|
2748
|
+
cloudPollLoopRunning = true;
|
|
2749
|
+
void (async () => {
|
|
2750
|
+
try {
|
|
2751
|
+
await pollCloudWaiters();
|
|
2752
|
+
} finally {
|
|
2753
|
+
cloudPollLoopRunning = false;
|
|
2754
|
+
}
|
|
2755
|
+
})();
|
|
2756
|
+
}
|
|
2757
|
+
async function pollCloudWaiters() {
|
|
2758
|
+
while (cloudWaiters.size > 0) {
|
|
2759
|
+
if (herdrAbortSignal.aborted) {
|
|
2760
|
+
for (const waiter of cloudWaiters.values()) waiter.settle("aborted");
|
|
2761
|
+
cloudWaiters.clear();
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
const pendingTypes = /* @__PURE__ */ new Set();
|
|
2765
|
+
for (const waiter of cloudWaiters.values()) pendingTypes.add(waiter.type);
|
|
2766
|
+
const doneKeys = /* @__PURE__ */ new Map();
|
|
2767
|
+
for (const type of pendingTypes) {
|
|
2768
|
+
try {
|
|
2769
|
+
const numbers = await listNumbersWithLabel(type, CLOUD_DONE_LABEL);
|
|
2770
|
+
doneKeys.set(type, new Set(numbers));
|
|
2771
|
+
} catch (err) {
|
|
2772
|
+
console.error(`[worker] failed to poll ${CLOUD_DONE_LABEL} for ${type}: ${err}`);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
const now = Date.now();
|
|
2776
|
+
for (const [key, waiter] of [...cloudWaiters.entries()]) {
|
|
2777
|
+
const done = doneKeys.get(waiter.type);
|
|
2778
|
+
const number = Number(key.split(":")[1]);
|
|
2779
|
+
if (done?.has(number)) {
|
|
2780
|
+
waiter.settle("completed");
|
|
2781
|
+
cloudWaiters.delete(key);
|
|
2782
|
+
} else if (now >= waiter.deadline) {
|
|
2783
|
+
waiter.settle("timeout");
|
|
2784
|
+
cloudWaiters.delete(key);
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
if (cloudWaiters.size === 0) break;
|
|
2788
|
+
for (let waited = 0; waited < CLOUD_POLL_INTERVAL_MS; waited += 1e3) {
|
|
2789
|
+
if (herdrAbortSignal.aborted) break;
|
|
2790
|
+
await new Promise((resolve3) => setTimeout(resolve3, 1e3));
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
function waitForCloudTask(id, type) {
|
|
2795
|
+
return new Promise((resolve3) => {
|
|
2796
|
+
cloudWaiters.set(`${type}:${id}`, {
|
|
2797
|
+
type,
|
|
2798
|
+
deadline: Date.now() + CLOUD_TASK_TIMEOUT_MS,
|
|
2799
|
+
settle: resolve3
|
|
2800
|
+
});
|
|
2801
|
+
ensureCloudPollLoop();
|
|
2802
|
+
});
|
|
2803
|
+
}
|
|
2804
|
+
async function flagOrphanedCloudSession(target, id, reason, cloudSessionId) {
|
|
2805
|
+
await addLabel(target, id, "cc-need-human-check").catch((err) => {
|
|
2806
|
+
console.error(`[worker] failed to add cc-need-human-check to ${target} #${id}: ${err}`);
|
|
2807
|
+
});
|
|
2808
|
+
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";
|
|
2809
|
+
const sessionLine = cloudSessionId ? `https://claude.ai/code/${cloudSessionId}` : "\u30BB\u30C3\u30B7\u30E7\u30F3URL\u4E0D\u660E\uFF08ID\u62BD\u51FA\u306B\u5931\u6557\uFF09";
|
|
2810
|
+
const body = `## \u5B64\u7ACB\u30AF\u30E9\u30A6\u30C9\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u53EF\u80FD\u6027
|
|
2811
|
+
|
|
2812
|
+
**\u539F\u56E0**: ${causeLine}
|
|
2813
|
+
|
|
2814
|
+
**\u30BB\u30C3\u30B7\u30E7\u30F3URL**: ${sessionLine}
|
|
2815
|
+
|
|
2816
|
+
**\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`;
|
|
2817
|
+
const commentFn = target === "issue" ? commentOnIssue : commentOnPR;
|
|
2818
|
+
await commentFn(id, body).catch((err) => {
|
|
2819
|
+
console.error(`[worker] failed to comment on ${target} #${id} about the orphaned cloud session: ${err}`);
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
async function runViaCloud(args, prompt, id, onComplete, cwd, env, cloudTarget, model) {
|
|
2823
|
+
const herdrRunnerMod = await Promise.resolve().then(() => (init_herdr_runner(), herdr_runner_exports));
|
|
2824
|
+
const { taskTabLabel: taskTabLabel2, waitForPaneReady: waitForPaneReady3, extractCloudSessionId: extractCloudSessionId2 } = herdrRunnerMod;
|
|
2825
|
+
const herdrMod = await Promise.resolve().then(() => (init_herdr(), herdr_exports));
|
|
2826
|
+
const { tabCreate: tabCreate2, tabClose: tabClose2, paneSendText: paneSendText2, paneSendKeys: paneSendKeys2, paneRead: paneRead2, getCurrentWorkspaceId: getCurrentWorkspaceId2 } = herdrMod;
|
|
2827
|
+
herdrTasks.set(id, { paneId: "", tabId: "" });
|
|
2828
|
+
const label = taskTabLabel2(resolveProjectName(), id);
|
|
2829
|
+
const initialPrompt = buildCloudPrompt(
|
|
2830
|
+
prompt,
|
|
2831
|
+
model ?? "",
|
|
2832
|
+
cloudTarget ? { type: cloudTarget, number: id } : void 0
|
|
2833
|
+
);
|
|
2834
|
+
let result;
|
|
2835
|
+
let cloudSessionId;
|
|
2836
|
+
try {
|
|
2837
|
+
const created = await tabCreate2({ label, cwd: cwd ?? process.cwd(), workspaceId: getCurrentWorkspaceId2(), env });
|
|
2838
|
+
herdrTasks.set(id, created);
|
|
2839
|
+
try {
|
|
2840
|
+
const ready = await waitForPaneReady3(created.paneId, herdrMod);
|
|
2841
|
+
if (!ready) {
|
|
2842
|
+
console.warn(`[worker] pane ${created.paneId} produced no prompt before the timeout, launching anyway`);
|
|
2843
|
+
}
|
|
2844
|
+
const command = ["claude", ...buildCloudCreateArgs(args, initialPrompt)].map(shellQuote2).join(" ");
|
|
2845
|
+
await paneSendText2(created.paneId, command);
|
|
2846
|
+
await paneSendKeys2(created.paneId, "enter");
|
|
2847
|
+
const deadline = Date.now() + CLOUD_SESSION_TIMEOUT_MS;
|
|
2848
|
+
for (; ; ) {
|
|
2849
|
+
if (herdrAbortSignal.aborted) {
|
|
2850
|
+
throw new Error("the worker is shutting down before the cloud session could be created");
|
|
2851
|
+
}
|
|
2852
|
+
let content = "";
|
|
2853
|
+
try {
|
|
2854
|
+
content = await paneRead2(created.paneId);
|
|
2855
|
+
} catch (err) {
|
|
2856
|
+
console.error(`[worker] failed to read pane ${created.paneId} while waiting for the cloud session: ${err}`);
|
|
2857
|
+
}
|
|
2858
|
+
cloudSessionId = extractCloudSessionId2(content);
|
|
2859
|
+
if (cloudSessionId) break;
|
|
2860
|
+
if (Date.now() >= deadline) {
|
|
2861
|
+
throw new Error(`timed out waiting for the cloud session id (pane tail: ${content.slice(-1e3)})`);
|
|
2862
|
+
}
|
|
2863
|
+
await new Promise((resolve3) => setTimeout(resolve3, CLOUD_SESSION_POLL_INTERVAL_MS));
|
|
2864
|
+
}
|
|
2865
|
+
} finally {
|
|
2866
|
+
await tabClose2(created.tabId).catch((err) => {
|
|
2867
|
+
console.error(`[worker] failed to close cloud task tab ${created.tabId}: ${err}`);
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
const createOutput = `[worker] created cloud session ${cloudSessionId} with the task's initial prompt`;
|
|
2871
|
+
if (!cloudTarget) {
|
|
2872
|
+
console.warn(`[worker] #${id} has no completion-detection target, treating session creation as completion`);
|
|
2873
|
+
result = { status: "completed", output: createOutput };
|
|
2874
|
+
} else {
|
|
2875
|
+
const outcome = await waitForCloudTask(id, cloudTarget);
|
|
2876
|
+
if (outcome === "completed") {
|
|
2877
|
+
await removeLabel(cloudTarget, id, CLOUD_DONE_LABEL).catch((err) => {
|
|
2878
|
+
console.error(`[worker] failed to remove ${CLOUD_DONE_LABEL} from ${cloudTarget} #${id}: ${err}`);
|
|
2879
|
+
});
|
|
2880
|
+
let reportBody = null;
|
|
2881
|
+
const startedAt = tasks.get(id)?.startedAt;
|
|
2882
|
+
if (startedAt) {
|
|
2883
|
+
try {
|
|
2884
|
+
reportBody = await findCommentSince(id, startedAt, CLOUD_REPORT_HEADING);
|
|
2885
|
+
} catch (err) {
|
|
2886
|
+
console.error(`[worker] failed to fetch the cloud report comment for ${cloudTarget} #${id}: ${err}`);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
result = {
|
|
2890
|
+
status: "completed",
|
|
2891
|
+
output: reportBody ?? `${createOutput}
|
|
2892
|
+
[worker] detected completion via the ${CLOUD_DONE_LABEL} label`
|
|
2893
|
+
};
|
|
2894
|
+
} else if (outcome === "timeout") {
|
|
2895
|
+
await addLabel(cloudTarget, id, "cc-need-human-check").catch((err) => {
|
|
2896
|
+
console.error(`[worker] failed to add cc-need-human-check to ${cloudTarget} #${id}: ${err}`);
|
|
2897
|
+
});
|
|
2898
|
+
result = {
|
|
2899
|
+
status: "failed",
|
|
2900
|
+
output: `${createOutput}
|
|
2901
|
+
[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.`
|
|
2902
|
+
};
|
|
2903
|
+
} else {
|
|
2904
|
+
await flagOrphanedCloudSession(cloudTarget, id, "shutdown", cloudSessionId);
|
|
2905
|
+
result = {
|
|
2906
|
+
status: "failed",
|
|
2907
|
+
output: `${createOutput}
|
|
2908
|
+
[worker] shutdown aborted the wait for the ${CLOUD_DONE_LABEL} label. The session may still be running; added cc-need-human-check.`
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
result.cloudSessionId = cloudSessionId;
|
|
2913
|
+
} catch (err) {
|
|
2914
|
+
console.error(`[worker] failed to run #${id} via cloud: ${err}`);
|
|
2915
|
+
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).";
|
|
2916
|
+
if (cloudTarget) {
|
|
2917
|
+
await flagOrphanedCloudSession(cloudTarget, id, "session-id", cloudSessionId);
|
|
2918
|
+
orphanNote += " added cc-need-human-check so a human can verify whether it completed on its own.";
|
|
2919
|
+
}
|
|
2920
|
+
result = {
|
|
2921
|
+
status: "failed",
|
|
2922
|
+
output: `[worker] failed to run the task via cloud: ${err}
|
|
2923
|
+
${orphanNote}`,
|
|
2924
|
+
...cloudSessionId ? { cloudSessionId } : {}
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
result = appendCloudFailureGuidance(result, true);
|
|
2928
|
+
await finishTask(id, result, onComplete);
|
|
2929
|
+
herdrTasks.delete(id);
|
|
2930
|
+
}
|
|
2931
|
+
function run(command, args, id, title, workerName, path2, onComplete, cwd, env, prompt, cloud, cloudTarget, model) {
|
|
2537
2932
|
tasks.delete(id);
|
|
2538
2933
|
tasks.set(id, {
|
|
2539
2934
|
id,
|
|
@@ -2546,6 +2941,10 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
|
|
|
2546
2941
|
ensureRenderInterval();
|
|
2547
2942
|
renderTable();
|
|
2548
2943
|
if (getRunMode() === "herdr") {
|
|
2944
|
+
if (cloud) {
|
|
2945
|
+
void runViaCloud(args, prompt ?? "", id, onComplete, cwd, env, cloudTarget, model);
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2549
2948
|
void runViaHerdr(args, prompt ?? "", id, onComplete, cwd, env);
|
|
2550
2949
|
return;
|
|
2551
2950
|
}
|
|
@@ -3058,22 +3457,59 @@ async function buildTokenLimitText() {
|
|
|
3058
3457
|
writeRuncatUsage(usage);
|
|
3059
3458
|
return formatTokenLimitText(usage);
|
|
3060
3459
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3460
|
+
function truncateOutputForNotification(output) {
|
|
3461
|
+
if (output.length <= 1e3) return output;
|
|
3462
|
+
if (output.startsWith(CLOUD_FAILURE_GUIDANCE)) {
|
|
3463
|
+
const rest = output.slice(CLOUD_FAILURE_GUIDANCE.length);
|
|
3464
|
+
return `${CLOUD_FAILURE_GUIDANCE}\u2026${rest.slice(-1e3)}`;
|
|
3465
|
+
}
|
|
3466
|
+
return `\u2026${output.slice(-1e3)}`;
|
|
3467
|
+
}
|
|
3468
|
+
function buildTaskNotificationText(params) {
|
|
3469
|
+
const { status, workerName, repoName, id, title, url, tokenText, output, cloud } = params;
|
|
3470
|
+
const emoji = status === "completed" ? "\u2705" : "\u274C";
|
|
3471
|
+
const label = status === "completed" ? "completed" : "failed";
|
|
3472
|
+
const truncatedOutput = output ? truncateOutputForNotification(output) : output;
|
|
3064
3473
|
const outputBlock = truncatedOutput ? `
|
|
3065
3474
|
\`\`\`${truncatedOutput}\`\`\`` : "";
|
|
3475
|
+
const body = `${emoji} [${workerName}] ${repoName} | Task ${label}: <${url}|#${id} ${title}>${tokenText}${outputBlock}`;
|
|
3476
|
+
let sessionUrlPrefix = "";
|
|
3477
|
+
if (cloud) {
|
|
3478
|
+
sessionUrlPrefix = cloud.sessionId?.trim() ? `https://claude.ai/code/${cloud.sessionId.trim()}
|
|
3479
|
+
` : "\u30BB\u30C3\u30B7\u30E7\u30F3URL\u4E0D\u660E\uFF08ID\u62BD\u51FA\u306B\u5931\u6557\uFF09\n";
|
|
3480
|
+
}
|
|
3481
|
+
return `${sessionUrlPrefix}${body}`;
|
|
3482
|
+
}
|
|
3483
|
+
async function notifyTaskCompleted(workerName, repoName, id, title, url, output, cloud) {
|
|
3484
|
+
const tokenText = await buildTokenLimitText();
|
|
3066
3485
|
await send({
|
|
3067
|
-
text:
|
|
3486
|
+
text: buildTaskNotificationText({
|
|
3487
|
+
status: "completed",
|
|
3488
|
+
workerName,
|
|
3489
|
+
repoName,
|
|
3490
|
+
id,
|
|
3491
|
+
title,
|
|
3492
|
+
url,
|
|
3493
|
+
tokenText,
|
|
3494
|
+
output,
|
|
3495
|
+
cloud
|
|
3496
|
+
})
|
|
3068
3497
|
});
|
|
3069
3498
|
}
|
|
3070
|
-
async function notifyTaskFailed(workerName, repoName, id, title, url, output) {
|
|
3499
|
+
async function notifyTaskFailed(workerName, repoName, id, title, url, output, cloud) {
|
|
3071
3500
|
const tokenText = await buildTokenLimitText();
|
|
3072
|
-
const truncatedOutput = output && output.length > 1e3 ? `\u2026${output.slice(-1e3)}` : output;
|
|
3073
|
-
const outputBlock = truncatedOutput ? `
|
|
3074
|
-
\`\`\`${truncatedOutput}\`\`\`` : "";
|
|
3075
3501
|
await send({
|
|
3076
|
-
text:
|
|
3502
|
+
text: buildTaskNotificationText({
|
|
3503
|
+
status: "failed",
|
|
3504
|
+
workerName,
|
|
3505
|
+
repoName,
|
|
3506
|
+
id,
|
|
3507
|
+
title,
|
|
3508
|
+
url,
|
|
3509
|
+
tokenText,
|
|
3510
|
+
output,
|
|
3511
|
+
cloud
|
|
3512
|
+
})
|
|
3077
3513
|
});
|
|
3078
3514
|
}
|
|
3079
3515
|
async function notifyError(workerName, repoName, error) {
|
|
@@ -3243,7 +3679,8 @@ async function removeStaleWorktrees() {
|
|
|
3243
3679
|
}
|
|
3244
3680
|
|
|
3245
3681
|
// src/workers/issue-worker.ts
|
|
3246
|
-
var
|
|
3682
|
+
var STICKY_LABELS = ["cc-triage-scope", "cc-issue-created"];
|
|
3683
|
+
var consumableTriggerLabels = (triggerLabels) => triggerLabels.filter((label) => !STICKY_LABELS.includes(label));
|
|
3247
3684
|
var PREFLIGHT_SEARCH_LIMIT = 5;
|
|
3248
3685
|
function createIssuePollingWorker(config) {
|
|
3249
3686
|
return async () => {
|
|
@@ -3289,9 +3726,9 @@ function createIssuePollingWorker(config) {
|
|
|
3289
3726
|
continue;
|
|
3290
3727
|
}
|
|
3291
3728
|
}
|
|
3292
|
-
const hadTriageScope = issue.labels.some((l) => l.name === LABEL_TRIAGE_SCOPE);
|
|
3293
3729
|
await addLabel("issue", issue.number, "cc-in-progress");
|
|
3294
3730
|
const worktreeId = generateWorktreeName();
|
|
3731
|
+
const cloud = isCloudWorker(config.name);
|
|
3295
3732
|
try {
|
|
3296
3733
|
const issueUrl = `https://github.com/${owner}/${name}/issues/${issue.number}`;
|
|
3297
3734
|
syncDefaultBranch(defaultBranch);
|
|
@@ -3299,6 +3736,11 @@ function createIssuePollingWorker(config) {
|
|
|
3299
3736
|
const command = skill || config.command;
|
|
3300
3737
|
const parentNumber = issue.parent?.number;
|
|
3301
3738
|
const mode = getRunMode();
|
|
3739
|
+
let baseBranch = defaultBranch;
|
|
3740
|
+
if (parentNumber !== void 0) {
|
|
3741
|
+
baseBranch = `cc-epic-${parentNumber}`;
|
|
3742
|
+
await ensureEpicBranch(baseBranch, defaultBranch);
|
|
3743
|
+
}
|
|
3302
3744
|
const execution = buildClaudeExecution({
|
|
3303
3745
|
mode,
|
|
3304
3746
|
prompt: `${command} ${issue.number}`,
|
|
@@ -3306,72 +3748,117 @@ function createIssuePollingWorker(config) {
|
|
|
3306
3748
|
effort,
|
|
3307
3749
|
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3308
3750
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
3309
|
-
permissionMode: getPermissionMode()
|
|
3751
|
+
permissionMode: getPermissionMode(),
|
|
3752
|
+
...cloud ? { cloud: true, baseRef: baseBranch } : {}
|
|
3310
3753
|
});
|
|
3311
|
-
let
|
|
3312
|
-
if (
|
|
3313
|
-
|
|
3314
|
-
|
|
3754
|
+
let cwd;
|
|
3755
|
+
if (cloud) {
|
|
3756
|
+
console.log(
|
|
3757
|
+
`[${config.name}] #${issue.number}: cloud execution, running without worktree on ${baseBranch}`
|
|
3758
|
+
);
|
|
3759
|
+
} else {
|
|
3760
|
+
await createWorktreeFromBranch(worktreeId, baseBranch);
|
|
3761
|
+
cwd = getWorktreePath(worktreeId);
|
|
3762
|
+
console.log(`[${config.name}] #${issue.number}: created worktree ${worktreeId} from ${baseBranch}`);
|
|
3315
3763
|
}
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3764
|
+
if (cloud) {
|
|
3765
|
+
await removeLabel("issue", issue.number, CLOUD_DONE_LABEL).catch(
|
|
3766
|
+
(err) => console.error(`[${config.name}] removeLabel ${CLOUD_DONE_LABEL} failed for #${issue.number}: ${err}`)
|
|
3767
|
+
);
|
|
3768
|
+
}
|
|
3769
|
+
const startedAt = Date.now();
|
|
3319
3770
|
run(
|
|
3320
3771
|
execution.command,
|
|
3321
3772
|
execution.args,
|
|
3322
3773
|
issue.number,
|
|
3323
3774
|
issue.title,
|
|
3324
3775
|
config.name,
|
|
3325
|
-
worktreeId,
|
|
3326
|
-
async (status, output) => {
|
|
3776
|
+
cloud ? void 0 : worktreeId,
|
|
3777
|
+
async (status, output, cloudSessionId) => {
|
|
3327
3778
|
lastCompletionAt = Date.now();
|
|
3328
|
-
for (const label of config.triggerLabels) {
|
|
3779
|
+
for (const label of consumableTriggerLabels(config.triggerLabels)) {
|
|
3329
3780
|
await removeLabel("issue", issue.number, label).catch(
|
|
3330
3781
|
(err) => console.error(`[${config.name}] removeLabel ${label} failed for #${issue.number}: ${err}`)
|
|
3331
3782
|
);
|
|
3332
3783
|
}
|
|
3333
|
-
if (hadTriageScope) {
|
|
3334
|
-
await addLabel("issue", issue.number, LABEL_TRIAGE_SCOPE).catch(
|
|
3335
|
-
(err) => console.error(
|
|
3336
|
-
`[${config.name}] addLabel ${LABEL_TRIAGE_SCOPE} failed for #${issue.number}: ${err}`
|
|
3337
|
-
)
|
|
3338
|
-
);
|
|
3339
|
-
}
|
|
3340
3784
|
try {
|
|
3341
3785
|
if (status === "completed") {
|
|
3342
|
-
const verified = await config.onCompleted?.(issue.number, worktreeId, output
|
|
3786
|
+
const verified = await config.onCompleted?.(issue.number, worktreeId, output, {
|
|
3787
|
+
cloud,
|
|
3788
|
+
baseBranch,
|
|
3789
|
+
startedAt
|
|
3790
|
+
}) ?? true;
|
|
3343
3791
|
if (verified === false) {
|
|
3344
|
-
await notifyTaskFailed(
|
|
3792
|
+
await notifyTaskFailed(
|
|
3793
|
+
config.name,
|
|
3794
|
+
name,
|
|
3795
|
+
issue.number,
|
|
3796
|
+
issue.title,
|
|
3797
|
+
issueUrl,
|
|
3798
|
+
output,
|
|
3799
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3800
|
+
);
|
|
3345
3801
|
} else {
|
|
3346
|
-
await notifyTaskCompleted(
|
|
3802
|
+
await notifyTaskCompleted(
|
|
3803
|
+
config.name,
|
|
3804
|
+
name,
|
|
3805
|
+
issue.number,
|
|
3806
|
+
issue.title,
|
|
3807
|
+
issueUrl,
|
|
3808
|
+
output,
|
|
3809
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3810
|
+
);
|
|
3347
3811
|
}
|
|
3348
3812
|
} else {
|
|
3349
|
-
await notifyTaskFailed(
|
|
3813
|
+
await notifyTaskFailed(
|
|
3814
|
+
config.name,
|
|
3815
|
+
name,
|
|
3816
|
+
issue.number,
|
|
3817
|
+
issue.title,
|
|
3818
|
+
issueUrl,
|
|
3819
|
+
output,
|
|
3820
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3821
|
+
);
|
|
3350
3822
|
}
|
|
3351
3823
|
} catch (err) {
|
|
3352
3824
|
console.error(`[${config.name}] post-task error for #${issue.number}: ${err}`);
|
|
3353
|
-
await notifyTaskFailed(
|
|
3825
|
+
await notifyTaskFailed(
|
|
3826
|
+
config.name,
|
|
3827
|
+
name,
|
|
3828
|
+
issue.number,
|
|
3829
|
+
issue.title,
|
|
3830
|
+
issueUrl,
|
|
3831
|
+
output,
|
|
3832
|
+
cloud ? { sessionId: cloudSessionId } : void 0
|
|
3833
|
+
).catch(
|
|
3354
3834
|
(notifyErr) => console.error(`[${config.name}] notifyTaskFailed failed for #${issue.number}: ${notifyErr}`)
|
|
3355
3835
|
);
|
|
3356
3836
|
} finally {
|
|
3357
3837
|
await removeLabel("issue", issue.number, "cc-in-progress").catch(
|
|
3358
3838
|
(err) => console.error(`[${config.name}] removeLabel cc-in-progress failed for #${issue.number}: ${err}`)
|
|
3359
3839
|
);
|
|
3360
|
-
|
|
3361
|
-
(
|
|
3362
|
-
|
|
3840
|
+
if (!cloud) {
|
|
3841
|
+
await removeWorktree(worktreeId).catch(
|
|
3842
|
+
(err) => console.error(`[${config.name}] removeWorktree failed for #${issue.number}: ${err}`)
|
|
3843
|
+
);
|
|
3844
|
+
}
|
|
3363
3845
|
}
|
|
3364
3846
|
},
|
|
3365
3847
|
cwd,
|
|
3366
|
-
buildClaudeEnv(mode),
|
|
3367
|
-
execution.prompt
|
|
3848
|
+
buildClaudeEnv(mode, cloud),
|
|
3849
|
+
execution.prompt,
|
|
3850
|
+
cloud,
|
|
3851
|
+
cloud ? "issue" : void 0,
|
|
3852
|
+
model
|
|
3368
3853
|
);
|
|
3369
3854
|
} catch (err) {
|
|
3370
3855
|
console.error(`[${config.name}] setup error for #${issue.number}: ${err}`);
|
|
3371
3856
|
await removeLabel("issue", issue.number, "cc-in-progress").catch(() => {
|
|
3372
3857
|
});
|
|
3373
|
-
|
|
3374
|
-
|
|
3858
|
+
if (!cloud) {
|
|
3859
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
3860
|
+
});
|
|
3861
|
+
}
|
|
3375
3862
|
await notifyError(config.name, name, err);
|
|
3376
3863
|
}
|
|
3377
3864
|
}
|
|
@@ -3394,56 +3881,89 @@ function formatSessionReport(output) {
|
|
|
3394
3881
|
${trimmed.slice(-REPORT_TAIL_LIMIT)}` : trimmed;
|
|
3395
3882
|
return ["```", tail, "```"].join("\n");
|
|
3396
3883
|
}
|
|
3397
|
-
function
|
|
3884
|
+
function selectOwnedClosingPr(candidates, ctx) {
|
|
3885
|
+
const open = candidates.filter((c) => c.state === "MERGED" || c.state === "OPEN");
|
|
3886
|
+
if (!ctx.cloud) {
|
|
3887
|
+
const found2 = open.find((c) => c.headRefName === ctx.expectedHeadRefName);
|
|
3888
|
+
return found2 ? found2.number : null;
|
|
3889
|
+
}
|
|
3890
|
+
const found = open.find((c) => {
|
|
3891
|
+
if (c.baseRefName !== ctx.baseBranch) return false;
|
|
3892
|
+
const createdAt = Date.parse(c.createdAt);
|
|
3893
|
+
return !Number.isNaN(createdAt) && createdAt >= ctx.startedAt && createdAt <= ctx.now;
|
|
3894
|
+
});
|
|
3895
|
+
return found ? found.number : null;
|
|
3896
|
+
}
|
|
3897
|
+
function prMissingComment(worktreeId, output, cloud) {
|
|
3898
|
+
const stateSection = cloud ? [
|
|
3899
|
+
"## \u72B6\u614B\u306E\u78BA\u8A8D",
|
|
3900
|
+
"- \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"
|
|
3901
|
+
] : [
|
|
3902
|
+
"## \u72B6\u614B\u306E\u78BA\u8A8D",
|
|
3903
|
+
`- \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`
|
|
3904
|
+
];
|
|
3398
3905
|
return [
|
|
3399
3906
|
"## 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",
|
|
3400
|
-
`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`,
|
|
3907
|
+
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`,
|
|
3401
3908
|
"",
|
|
3402
3909
|
"## PR\u3092\u4F5C\u6210\u3057\u306A\u304B\u3063\u305F\u7406\u7531\uFF08\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u7D42\u5831\u544A\uFF09",
|
|
3403
3910
|
formatSessionReport(output),
|
|
3404
3911
|
"",
|
|
3405
|
-
|
|
3406
|
-
`- \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`,
|
|
3912
|
+
...stateSection,
|
|
3407
3913
|
"",
|
|
3408
3914
|
"## \u5BFE\u5FDC\u5F8C\u306E\u9032\u3081\u65B9",
|
|
3409
3915
|
"- \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",
|
|
3410
3916
|
"- \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"
|
|
3411
3917
|
].join("\n");
|
|
3412
3918
|
}
|
|
3919
|
+
async function verifyPrCreated(issueNumber, worktreeId, output, ctx) {
|
|
3920
|
+
if (await hasLabel("issue", issueNumber, "cc-need-human-check")) {
|
|
3921
|
+
console.log(`[exec-issue] #${issueNumber}: cc-need-human-check present, skip cc-pr-created`);
|
|
3922
|
+
return false;
|
|
3923
|
+
}
|
|
3924
|
+
if (await getIssueState(issueNumber) === "CLOSED") {
|
|
3925
|
+
console.log(`[exec-issue] #${issueNumber}: issue closed by skill (no-change path), skip cc-pr-created`);
|
|
3926
|
+
return;
|
|
3927
|
+
}
|
|
3928
|
+
let prNumber = null;
|
|
3929
|
+
if (!ctx.cloud) {
|
|
3930
|
+
prNumber = await findPrNumberByHeadRef(worktreeId, "all");
|
|
3931
|
+
}
|
|
3932
|
+
if (prNumber === null) {
|
|
3933
|
+
const candidates = await listPrsClosingIssue(issueNumber);
|
|
3934
|
+
prNumber = selectOwnedClosingPr(candidates, {
|
|
3935
|
+
cloud: ctx.cloud,
|
|
3936
|
+
expectedHeadRefName: worktreeId,
|
|
3937
|
+
baseBranch: ctx.baseBranch,
|
|
3938
|
+
startedAt: ctx.startedAt,
|
|
3939
|
+
now: Date.now()
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3942
|
+
if (prNumber !== null) {
|
|
3943
|
+
await addLabel("issue", issueNumber, "cc-pr-created");
|
|
3944
|
+
return;
|
|
3945
|
+
}
|
|
3946
|
+
console.error(
|
|
3947
|
+
`[exec-issue] #${issueNumber}: session exited without a PR (branch: ${worktreeId}); marking cc-need-human-check`
|
|
3948
|
+
);
|
|
3949
|
+
await addLabel("issue", issueNumber, "cc-need-human-check");
|
|
3950
|
+
await commentOnIssue(issueNumber, prMissingComment(worktreeId, output, ctx.cloud)).catch(
|
|
3951
|
+
(err) => console.error(`[exec-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
|
|
3952
|
+
);
|
|
3953
|
+
return false;
|
|
3954
|
+
}
|
|
3413
3955
|
var execIssueWorker = (opts = {}) => createIssuePollingWorker({
|
|
3414
3956
|
name: "exec-issue",
|
|
3415
3957
|
command: "/claude-task-worker:exec-issue",
|
|
3416
3958
|
triggerLabels: ["cc-exec-issue"],
|
|
3417
3959
|
epicFilters: opts.epicFilters,
|
|
3418
3960
|
labelFilters: opts.labelFilters,
|
|
3419
|
-
onCompleted:
|
|
3420
|
-
if (await hasLabel("issue", issueNumber, "cc-need-human-check")) {
|
|
3421
|
-
console.log(`[exec-issue] #${issueNumber}: cc-need-human-check present, skip cc-pr-created`);
|
|
3422
|
-
return false;
|
|
3423
|
-
}
|
|
3424
|
-
if (await getIssueState(issueNumber) === "CLOSED") {
|
|
3425
|
-
console.log(`[exec-issue] #${issueNumber}: issue closed by skill (no-change path), skip cc-pr-created`);
|
|
3426
|
-
return;
|
|
3427
|
-
}
|
|
3428
|
-
const prNumber = await findPrNumberByHeadRef(worktreeId, "all") ?? await findPrNumberClosingIssue(issueNumber, worktreeId);
|
|
3429
|
-
if (prNumber !== null) {
|
|
3430
|
-
await addLabel("issue", issueNumber, "cc-pr-created");
|
|
3431
|
-
return;
|
|
3432
|
-
}
|
|
3433
|
-
console.error(
|
|
3434
|
-
`[exec-issue] #${issueNumber}: session exited without a PR (branch: ${worktreeId}); marking cc-need-human-check`
|
|
3435
|
-
);
|
|
3436
|
-
await addLabel("issue", issueNumber, "cc-need-human-check");
|
|
3437
|
-
await commentOnIssue(issueNumber, prMissingComment(worktreeId, output)).catch(
|
|
3438
|
-
(err) => console.error(`[exec-issue] commentOnIssue failed for #${issueNumber}: ${err}`)
|
|
3439
|
-
);
|
|
3440
|
-
return false;
|
|
3441
|
-
}
|
|
3961
|
+
onCompleted: verifyPrCreated
|
|
3442
3962
|
})();
|
|
3443
3963
|
|
|
3444
3964
|
// src/workers/pr-worker.ts
|
|
3445
3965
|
var LABEL_IN_PROGRESS = "cc-in-progress";
|
|
3446
|
-
var
|
|
3966
|
+
var LABEL_TRIAGE_SCOPE = "cc-triage-scope";
|
|
3447
3967
|
function createPrPollingWorker(config) {
|
|
3448
3968
|
return async () => {
|
|
3449
3969
|
const { owner, name, defaultBranch } = await getRepoInfo();
|
|
@@ -3465,23 +3985,26 @@ function createPrPollingWorker(config) {
|
|
|
3465
3985
|
if (isRunning(pr.number)) continue;
|
|
3466
3986
|
if (isWorkerAtCapacity(config.name)) break;
|
|
3467
3987
|
const prUrl = `https://github.com/${owner}/${name}/pull/${pr.number}`;
|
|
3468
|
-
const hadTriageScope = pr.labels.some((l) => l.name ===
|
|
3988
|
+
const hadTriageScope = pr.labels.some((l) => l.name === LABEL_TRIAGE_SCOPE);
|
|
3469
3989
|
await addLabel("pr", pr.number, LABEL_IN_PROGRESS);
|
|
3470
|
-
const
|
|
3990
|
+
const isCloud = isCloudWorker(config.name);
|
|
3991
|
+
const worktreeId = isCloud ? void 0 : generateWorktreeName();
|
|
3471
3992
|
try {
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3993
|
+
if (worktreeId) {
|
|
3994
|
+
await removeWorktreeByBranch(pr.headRefName);
|
|
3995
|
+
await deleteLocalBranch(pr.headRefName);
|
|
3996
|
+
if (await localBranchExists(pr.headRefName)) {
|
|
3997
|
+
console.error(
|
|
3998
|
+
`[${config.name}] PR #${pr.number}: branch ${pr.headRefName} is still checked out by another worktree; skipping this tick`
|
|
3999
|
+
);
|
|
4000
|
+
await removeLabel("pr", pr.number, LABEL_IN_PROGRESS).catch(() => {
|
|
4001
|
+
});
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
syncDefaultBranch(defaultBranch);
|
|
4005
|
+
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3481
4006
|
}
|
|
3482
|
-
|
|
3483
|
-
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3484
|
-
const cwd = getWorktreePath(worktreeId);
|
|
4007
|
+
const cwd = worktreeId ? getWorktreePath(worktreeId) : void 0;
|
|
3485
4008
|
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
3486
4009
|
const command = skill || config.command;
|
|
3487
4010
|
const mode = getRunMode();
|
|
@@ -3492,8 +4015,15 @@ function createPrPollingWorker(config) {
|
|
|
3492
4015
|
effort,
|
|
3493
4016
|
// config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
|
|
3494
4017
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
3495
|
-
permissionMode: getPermissionMode()
|
|
4018
|
+
permissionMode: getPermissionMode(),
|
|
4019
|
+
cloud: isCloud,
|
|
4020
|
+
onBranch: isCloud ? pr.headRefName : void 0
|
|
3496
4021
|
});
|
|
4022
|
+
if (isCloud) {
|
|
4023
|
+
await removeLabel("pr", pr.number, CLOUD_DONE_LABEL).catch(
|
|
4024
|
+
(err) => console.error(`[${config.name}] removeLabel ${CLOUD_DONE_LABEL} failed for PR #${pr.number}: ${err}`)
|
|
4025
|
+
);
|
|
4026
|
+
}
|
|
3497
4027
|
run(
|
|
3498
4028
|
execution.command,
|
|
3499
4029
|
execution.args,
|
|
@@ -3501,14 +4031,30 @@ function createPrPollingWorker(config) {
|
|
|
3501
4031
|
`PR #${pr.number} (${pr.headRefName})`,
|
|
3502
4032
|
config.name,
|
|
3503
4033
|
worktreeId,
|
|
3504
|
-
async (status, output) => {
|
|
4034
|
+
async (status, output, cloudSessionId) => {
|
|
3505
4035
|
lastCompletionAt = Date.now();
|
|
3506
4036
|
try {
|
|
3507
4037
|
if (status === "completed") {
|
|
3508
4038
|
await config.onCompleted?.(pr, output);
|
|
3509
|
-
await notifyTaskCompleted(
|
|
4039
|
+
await notifyTaskCompleted(
|
|
4040
|
+
config.name,
|
|
4041
|
+
name,
|
|
4042
|
+
pr.number,
|
|
4043
|
+
pr.title,
|
|
4044
|
+
prUrl,
|
|
4045
|
+
output,
|
|
4046
|
+
isCloud ? { sessionId: cloudSessionId } : void 0
|
|
4047
|
+
);
|
|
3510
4048
|
} else {
|
|
3511
|
-
await notifyTaskFailed(
|
|
4049
|
+
await notifyTaskFailed(
|
|
4050
|
+
config.name,
|
|
4051
|
+
name,
|
|
4052
|
+
pr.number,
|
|
4053
|
+
pr.title,
|
|
4054
|
+
prUrl,
|
|
4055
|
+
output,
|
|
4056
|
+
isCloud ? { sessionId: cloudSessionId } : void 0
|
|
4057
|
+
);
|
|
3512
4058
|
}
|
|
3513
4059
|
} catch (err) {
|
|
3514
4060
|
console.error(`[${config.name}] post-task error for PR #${pr.number}: ${err}`);
|
|
@@ -3521,9 +4067,9 @@ function createPrPollingWorker(config) {
|
|
|
3521
4067
|
);
|
|
3522
4068
|
}
|
|
3523
4069
|
if (hadTriageScope) {
|
|
3524
|
-
await addLabel("pr", pr.number,
|
|
4070
|
+
await addLabel("pr", pr.number, LABEL_TRIAGE_SCOPE).catch(
|
|
3525
4071
|
(err) => console.error(
|
|
3526
|
-
`[${config.name}] addLabel ${
|
|
4072
|
+
`[${config.name}] addLabel ${LABEL_TRIAGE_SCOPE} failed for PR #${pr.number}: ${err}`
|
|
3527
4073
|
)
|
|
3528
4074
|
);
|
|
3529
4075
|
}
|
|
@@ -3535,21 +4081,28 @@ function createPrPollingWorker(config) {
|
|
|
3535
4081
|
`[${config.name}] removeLabel ${LABEL_IN_PROGRESS} failed for PR #${pr.number}: ${err}`
|
|
3536
4082
|
)
|
|
3537
4083
|
);
|
|
3538
|
-
|
|
3539
|
-
(
|
|
3540
|
-
|
|
4084
|
+
if (worktreeId) {
|
|
4085
|
+
await removeWorktree(worktreeId).catch(
|
|
4086
|
+
(err) => console.error(`[${config.name}] removeWorktree failed for PR #${pr.number}: ${err}`)
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
3541
4089
|
}
|
|
3542
4090
|
},
|
|
3543
4091
|
cwd,
|
|
3544
|
-
buildClaudeEnv(mode),
|
|
3545
|
-
execution.prompt
|
|
4092
|
+
buildClaudeEnv(mode, isCloud),
|
|
4093
|
+
execution.prompt,
|
|
4094
|
+
isCloud,
|
|
4095
|
+
isCloud ? "pr" : void 0,
|
|
4096
|
+
model
|
|
3546
4097
|
);
|
|
3547
4098
|
} catch (err) {
|
|
3548
4099
|
console.error(`[${config.name}] setup error for PR #${pr.number}: ${err}`);
|
|
3549
4100
|
await removeLabel("pr", pr.number, LABEL_IN_PROGRESS).catch(() => {
|
|
3550
4101
|
});
|
|
3551
|
-
|
|
3552
|
-
|
|
4102
|
+
if (worktreeId) {
|
|
4103
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
3553
4106
|
await notifyError(config.name, name, err);
|
|
3554
4107
|
}
|
|
3555
4108
|
}
|
|
@@ -3642,10 +4195,9 @@ var triageCreatedIssueWorker = (opts = {}) => createIssuePollingWorker({
|
|
|
3642
4195
|
"cc-ui-design-pr-created"
|
|
3643
4196
|
],
|
|
3644
4197
|
epicFilters: opts.epicFilters,
|
|
3645
|
-
labelFilters: opts.labelFilters
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
}
|
|
4198
|
+
labelFilters: opts.labelFilters
|
|
4199
|
+
// トリガーラベルはどちらも STICKY_LABELS なのでワーカーが外さない。
|
|
4200
|
+
// 完了時に付け直す必要はない。
|
|
3649
4201
|
})();
|
|
3650
4202
|
|
|
3651
4203
|
// src/workers/triage-pr.ts
|
|
@@ -3981,7 +4533,7 @@ import { execFile as execFile3 } from "node:child_process";
|
|
|
3981
4533
|
import { promisify as promisify4 } from "node:util";
|
|
3982
4534
|
var execFileAsync3 = promisify4(execFile3);
|
|
3983
4535
|
var CONFIG_FILE = "claude-task-worker.json";
|
|
3984
|
-
var
|
|
4536
|
+
var LABEL_TRIAGE_SCOPE2 = "cc-triage-scope";
|
|
3985
4537
|
function lastRunBranchName(workerName) {
|
|
3986
4538
|
return `ctw-last-run-${workerName}`;
|
|
3987
4539
|
}
|
|
@@ -4028,7 +4580,7 @@ async function publishLastRunPr(workerName, defaultBranch, at) {
|
|
|
4028
4580
|
branch,
|
|
4029
4581
|
lastRunPrTitle(workerName),
|
|
4030
4582
|
lastRunPrBody(workerName, at),
|
|
4031
|
-
{ labels: [
|
|
4583
|
+
{ labels: [LABEL_TRIAGE_SCOPE2], assignee: await getCurrentUser() }
|
|
4032
4584
|
);
|
|
4033
4585
|
console.log(`[${workerName}] opened lastRun PR #${prNumber}`);
|
|
4034
4586
|
} finally {
|
|
@@ -4057,9 +4609,11 @@ function createScheduledWorker(config) {
|
|
|
4057
4609
|
if (lastRunAt > 0 && now - lastRunAt < SCHEDULE_INTERVAL_MS) return;
|
|
4058
4610
|
const worktreeId = generateWorktreeName();
|
|
4059
4611
|
startedAt = now;
|
|
4612
|
+
let cloud = false;
|
|
4060
4613
|
try {
|
|
4061
4614
|
syncDefaultBranch(defaultBranch);
|
|
4062
4615
|
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
4616
|
+
cloud = isCloudWorker(config.name);
|
|
4063
4617
|
const command = skill || config.command;
|
|
4064
4618
|
const mode = getRunMode();
|
|
4065
4619
|
const execution = buildClaudeExecution({
|
|
@@ -4068,14 +4622,20 @@ function createScheduledWorker(config) {
|
|
|
4068
4622
|
model,
|
|
4069
4623
|
effort,
|
|
4070
4624
|
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
4071
|
-
permissionMode: getPermissionMode()
|
|
4625
|
+
permissionMode: getPermissionMode(),
|
|
4626
|
+
...cloud ? { cloud: true, baseRef: defaultBranch } : {}
|
|
4072
4627
|
});
|
|
4073
4628
|
await publishLastRunPr(config.name, defaultBranch, new Date(now)).catch(
|
|
4074
4629
|
(err) => console.error(`[${config.name}] publishLastRunPr failed: ${err}`)
|
|
4075
4630
|
);
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4631
|
+
let cwd;
|
|
4632
|
+
if (cloud) {
|
|
4633
|
+
console.log(`[${config.name}] cloud execution, running without worktree on ${defaultBranch}`);
|
|
4634
|
+
} else {
|
|
4635
|
+
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
4636
|
+
cwd = getWorktreePath(worktreeId);
|
|
4637
|
+
console.log(`[${config.name}] created worktree ${worktreeId} from ${defaultBranch}`);
|
|
4638
|
+
}
|
|
4079
4639
|
const repoUrl = `https://github.com/${owner}/${repoName}`;
|
|
4080
4640
|
run(
|
|
4081
4641
|
execution.command,
|
|
@@ -4083,7 +4643,7 @@ function createScheduledWorker(config) {
|
|
|
4083
4643
|
config.taskId,
|
|
4084
4644
|
config.name,
|
|
4085
4645
|
config.name,
|
|
4086
|
-
worktreeId,
|
|
4646
|
+
cloud ? void 0 : worktreeId,
|
|
4087
4647
|
async (status, output) => {
|
|
4088
4648
|
try {
|
|
4089
4649
|
if (status === "completed") {
|
|
@@ -4094,19 +4654,26 @@ function createScheduledWorker(config) {
|
|
|
4094
4654
|
} catch (err) {
|
|
4095
4655
|
console.error(`[${config.name}] post-task error: ${err}`);
|
|
4096
4656
|
} finally {
|
|
4097
|
-
|
|
4098
|
-
(
|
|
4099
|
-
|
|
4657
|
+
if (!cloud) {
|
|
4658
|
+
await removeWorktree(worktreeId).catch(
|
|
4659
|
+
(err) => console.error(`[${config.name}] removeWorktree failed: ${err}`)
|
|
4660
|
+
);
|
|
4661
|
+
}
|
|
4100
4662
|
}
|
|
4101
4663
|
},
|
|
4102
4664
|
cwd,
|
|
4103
|
-
buildClaudeEnv(mode),
|
|
4104
|
-
execution.prompt
|
|
4665
|
+
buildClaudeEnv(mode, cloud),
|
|
4666
|
+
execution.prompt,
|
|
4667
|
+
cloud,
|
|
4668
|
+
void 0,
|
|
4669
|
+
model
|
|
4105
4670
|
);
|
|
4106
4671
|
} catch (err) {
|
|
4107
4672
|
console.error(`[${config.name}] setup error: ${err}`);
|
|
4108
|
-
|
|
4109
|
-
|
|
4673
|
+
if (!cloud) {
|
|
4674
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4110
4677
|
await notifyError(config.name, repoName, err);
|
|
4111
4678
|
}
|
|
4112
4679
|
};
|
|
@@ -4264,8 +4831,10 @@ var LABELS = [
|
|
|
4264
4831
|
// vivid amber (H44 S100 L57 / L*83 C*79)
|
|
4265
4832
|
{ name: "cc-ui-design-pr-created", color: "947100" },
|
|
4266
4833
|
// vivid bronze (H46 S100 L29 / L*50 C*56)
|
|
4267
|
-
{ name: "cc-ui-design-ready", color: "0c73e9" }
|
|
4834
|
+
{ name: "cc-ui-design-ready", color: "0c73e9" },
|
|
4268
4835
|
// vivid azure (H212 S90 L48 / L*50 C*69)
|
|
4836
|
+
{ name: CLOUD_DONE_LABEL, color: "33cfff" }
|
|
4837
|
+
// vivid sky blue (H194 S100 L60 / L*78 C*42)
|
|
4269
4838
|
];
|
|
4270
4839
|
var ISSUE_TEMPLATE = `name: "[claude-task-worker] Issue\u4F5C\u6210\u4F9D\u983C"
|
|
4271
4840
|
description: claude-task-worker\u3067GitHub Issue\u3092\u4F5C\u6210\u3059\u308B
|
|
@@ -4612,50 +5181,10 @@ function version() {
|
|
|
4612
5181
|
}
|
|
4613
5182
|
}
|
|
4614
5183
|
|
|
4615
|
-
// src/dispatch-args.ts
|
|
4616
|
-
var PROJECT_INCOMPATIBLE_COMMANDS = ["init", "install", "update", "usage", "version"];
|
|
4617
|
-
function collectFlagValues(argv, flag) {
|
|
4618
|
-
const values = [];
|
|
4619
|
-
for (let i = 0; i < argv.length; i++) {
|
|
4620
|
-
if (argv[i] !== flag) continue;
|
|
4621
|
-
const raw = argv[i + 1];
|
|
4622
|
-
if (!raw || raw.startsWith("--")) {
|
|
4623
|
-
console.error(`[dispatcher] ${flag} requires a value`);
|
|
4624
|
-
process.exit(1);
|
|
4625
|
-
}
|
|
4626
|
-
values.push(raw);
|
|
4627
|
-
}
|
|
4628
|
-
return values;
|
|
4629
|
-
}
|
|
4630
|
-
function parseProjectFilters() {
|
|
4631
|
-
return collectFlagValues(process.argv, "--project");
|
|
4632
|
-
}
|
|
4633
|
-
function hasProjectFilter() {
|
|
4634
|
-
return process.argv.includes("--project");
|
|
4635
|
-
}
|
|
4636
|
-
function assertProjectCompatibleCommand(command) {
|
|
4637
|
-
if (PROJECT_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
4638
|
-
console.error(`[dispatcher] --project cannot be used with the "${command}" command`);
|
|
4639
|
-
process.exit(1);
|
|
4640
|
-
}
|
|
4641
|
-
}
|
|
4642
|
-
function shellQuote(value) {
|
|
4643
|
-
if (value === "") return "''";
|
|
4644
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4645
|
-
}
|
|
4646
|
-
function buildForwardedCommand(argv) {
|
|
4647
|
-
const tokens = [];
|
|
4648
|
-
for (let i = 0; i < argv.length; i++) {
|
|
4649
|
-
if (argv[i] === "--project") {
|
|
4650
|
-
i++;
|
|
4651
|
-
continue;
|
|
4652
|
-
}
|
|
4653
|
-
tokens.push(argv[i]);
|
|
4654
|
-
}
|
|
4655
|
-
return ["claude-task-worker", ...tokens.map(shellQuote)].join(" ");
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
5184
|
// src/index.ts
|
|
5185
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
5186
|
+
import { promisify as promisify5 } from "node:util";
|
|
5187
|
+
var execFileAsync4 = promisify5(execFile4);
|
|
4659
5188
|
var WORKERS = {
|
|
4660
5189
|
"exec-issue": execIssueWorker,
|
|
4661
5190
|
"fix-review-point": fixReviewPointWorker,
|
|
@@ -4677,7 +5206,7 @@ function printUsage() {
|
|
|
4677
5206
|
console.log(`Usage: claude-task-worker <command> [--project <name>] [--epic <issue-number>] [--label <label-name>]
|
|
4678
5207
|
|
|
4679
5208
|
Commands:
|
|
4680
|
-
init [--force]
|
|
5209
|
+
init [--force] Create required GitHub labels and config file (use --force to overwrite existing files)
|
|
4681
5210
|
install Add the claude-task-worker marketplace, install the plugin, and install/update the CLI
|
|
4682
5211
|
update Update the claude-task-worker plugin/marketplace and the CLI itself
|
|
4683
5212
|
usage Notify current usage to Slack
|
|
@@ -4738,6 +5267,9 @@ if (workerType !== "all" && workerType !== "yolo" && workerType !== "init" && wo
|
|
|
4738
5267
|
if (hasProjectFilter()) {
|
|
4739
5268
|
assertProjectCompatibleCommand(workerType);
|
|
4740
5269
|
}
|
|
5270
|
+
if (hasCloudFlag()) {
|
|
5271
|
+
assertCloudCompatibleCommand(workerType);
|
|
5272
|
+
}
|
|
4741
5273
|
function collectFlagValues2(flag) {
|
|
4742
5274
|
const values = [];
|
|
4743
5275
|
for (let i = 0; i < process.argv.length; i++) {
|
|
@@ -4781,10 +5313,60 @@ async function assertRunModeAvailable() {
|
|
|
4781
5313
|
}
|
|
4782
5314
|
console.log("[worker] run mode: herdr (each task runs as a TUI session in its own herdr tab)");
|
|
4783
5315
|
}
|
|
5316
|
+
async function readCloudAuthStatus() {
|
|
5317
|
+
let stdout;
|
|
5318
|
+
try {
|
|
5319
|
+
const result = await execFileAsync4("claude", ["auth", "status", "--json"]);
|
|
5320
|
+
stdout = result.stdout;
|
|
5321
|
+
} catch (err) {
|
|
5322
|
+
const maybeStdout = err.stdout;
|
|
5323
|
+
if (!maybeStdout) return { kind: "unknown" };
|
|
5324
|
+
stdout = maybeStdout;
|
|
5325
|
+
}
|
|
5326
|
+
try {
|
|
5327
|
+
const parsed = JSON.parse(stdout);
|
|
5328
|
+
return {
|
|
5329
|
+
kind: "ok",
|
|
5330
|
+
loggedIn: parsed.loggedIn === true,
|
|
5331
|
+
authMethod: String(parsed.authMethod ?? ""),
|
|
5332
|
+
apiProvider: String(parsed.apiProvider ?? ""),
|
|
5333
|
+
apiKeySource: typeof parsed.apiKeySource === "string" ? parsed.apiKeySource : void 0
|
|
5334
|
+
};
|
|
5335
|
+
} catch {
|
|
5336
|
+
return { kind: "unknown" };
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
async function assertCloudAvailable() {
|
|
5340
|
+
const cloud = hasCloudFlag();
|
|
5341
|
+
const labelReady = cloud ? await createLabel(CLOUD_DONE_LABEL, "33cfff", true) : true;
|
|
5342
|
+
const status = cloud ? await readCloudAuthStatus() : void 0;
|
|
5343
|
+
const errors = checkCloudConfig({
|
|
5344
|
+
cloud,
|
|
5345
|
+
mode: getRunMode(),
|
|
5346
|
+
auth: status ? { status, baseUrl: process.env.ANTHROPIC_BASE_URL } : void 0
|
|
5347
|
+
});
|
|
5348
|
+
if (!labelReady) {
|
|
5349
|
+
errors.push(
|
|
5350
|
+
`${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`
|
|
5351
|
+
);
|
|
5352
|
+
}
|
|
5353
|
+
if (errors.length > 0) {
|
|
5354
|
+
for (const message of errors) {
|
|
5355
|
+
console.error(`[worker] ${message}`);
|
|
5356
|
+
}
|
|
5357
|
+
process.exit(1);
|
|
5358
|
+
}
|
|
5359
|
+
if (cloud) {
|
|
5360
|
+
console.log(
|
|
5361
|
+
`[worker] cloud execution enabled (--cloud); these workers stay local: ${CLOUD_DENIED_WORKERS.join(", ")}`
|
|
5362
|
+
);
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
4784
5365
|
async function assertRunPrerequisites() {
|
|
4785
5366
|
captureConsole();
|
|
4786
5367
|
ensureRenderInterval();
|
|
4787
5368
|
await assertRunModeAvailable();
|
|
5369
|
+
await assertCloudAvailable();
|
|
4788
5370
|
}
|
|
4789
5371
|
if (!hasProjectFilter()) {
|
|
4790
5372
|
process.on("SIGTERM", async () => {
|
|
@@ -4851,7 +5433,8 @@ if (hasProjectFilter()) {
|
|
|
4851
5433
|
}
|
|
4852
5434
|
})();
|
|
4853
5435
|
} else if (workerType === "init") {
|
|
4854
|
-
const
|
|
5436
|
+
const initArgs = process.argv.slice(3);
|
|
5437
|
+
const force = initArgs.includes("--force");
|
|
4855
5438
|
init({ force });
|
|
4856
5439
|
} else if (workerType === "install") {
|
|
4857
5440
|
(async () => {
|