claude-task-worker 0.80.0 → 0.81.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/dist/index.js +206 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1892,7 +1892,7 @@ function buildClaudeEnv(mode) {
|
|
|
1892
1892
|
}
|
|
1893
1893
|
|
|
1894
1894
|
// src/config.ts
|
|
1895
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1895
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1896
1896
|
import { isAbsolute as isAbsolute2, join as join2, normalize, sep as SEP } from "node:path";
|
|
1897
1897
|
var DEFAULT_UI_DESIGN_CONFIG = {
|
|
1898
1898
|
enabled: false,
|
|
@@ -2016,11 +2016,44 @@ var WORKER_DEFAULTS = {
|
|
|
2016
2016
|
pollingIntervalSeconds: 300,
|
|
2017
2017
|
cooldownSeconds: 0,
|
|
2018
2018
|
maxConcurrentTasks: 1
|
|
2019
|
+
},
|
|
2020
|
+
// 以下3つは定期ワーカー(createScheduledWorker)。実行間隔そのものは
|
|
2021
|
+
// SCHEDULE_INTERVAL_HOURS(24時間)と実行ログで決まり、pollingIntervalSeconds は
|
|
2022
|
+
// 「24時間経過したかを確認する頻度」でしかない。
|
|
2023
|
+
// model が opus なのは、成果物(CODING_GUIDELINES.md / .claude/requirements/ / DESIGN.md)が
|
|
2024
|
+
// 後続の全 Issue・全デザインの前提として読まれ、誤った一般化がそのまま下流の手戻りになるため。
|
|
2025
|
+
"update-coding-guidelines": {
|
|
2026
|
+
skill: "/claude-task-worker:update-coding-guidelines",
|
|
2027
|
+
model: "opus",
|
|
2028
|
+
advisorModel: "",
|
|
2029
|
+
effort: "high",
|
|
2030
|
+
pollingIntervalSeconds: 3600,
|
|
2031
|
+
cooldownSeconds: 0,
|
|
2032
|
+
maxConcurrentTasks: 1
|
|
2033
|
+
},
|
|
2034
|
+
"update-requirement-rules": {
|
|
2035
|
+
skill: "/claude-task-worker:update-requirement-rules",
|
|
2036
|
+
model: "opus",
|
|
2037
|
+
advisorModel: "",
|
|
2038
|
+
effort: "high",
|
|
2039
|
+
pollingIntervalSeconds: 3600,
|
|
2040
|
+
cooldownSeconds: 0,
|
|
2041
|
+
maxConcurrentTasks: 1
|
|
2042
|
+
},
|
|
2043
|
+
"update-design-md": {
|
|
2044
|
+
skill: "/claude-task-worker:update-design-md",
|
|
2045
|
+
model: "opus",
|
|
2046
|
+
advisorModel: "",
|
|
2047
|
+
effort: "high",
|
|
2048
|
+
pollingIntervalSeconds: 3600,
|
|
2049
|
+
cooldownSeconds: 0,
|
|
2050
|
+
maxConcurrentTasks: 1
|
|
2019
2051
|
}
|
|
2020
2052
|
};
|
|
2021
2053
|
var DEFAULT_CONFIG = {
|
|
2022
2054
|
fixReviewPointCallbackCommentMessage: "",
|
|
2023
2055
|
uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG },
|
|
2056
|
+
lastRun: {},
|
|
2024
2057
|
workers: {}
|
|
2025
2058
|
};
|
|
2026
2059
|
var CONFIG_PATH = join2(process.cwd(), "claude-task-worker.json");
|
|
@@ -2135,6 +2168,21 @@ function parseUiDesignEntry(val) {
|
|
|
2135
2168
|
}
|
|
2136
2169
|
return result;
|
|
2137
2170
|
}
|
|
2171
|
+
function parseLastRunEntry(val) {
|
|
2172
|
+
if (typeof val !== "object" || val === null || Array.isArray(val)) {
|
|
2173
|
+
console.warn(`[config] invalid lastRun: expected object, ignoring`);
|
|
2174
|
+
return {};
|
|
2175
|
+
}
|
|
2176
|
+
const result = {};
|
|
2177
|
+
for (const [name, at] of Object.entries(val)) {
|
|
2178
|
+
if (typeof at === "string" && !Number.isNaN(Date.parse(at))) {
|
|
2179
|
+
result[name] = at;
|
|
2180
|
+
} else {
|
|
2181
|
+
console.warn(`[config] invalid lastRun.${name}: ${String(at)}, ignoring`);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
return result;
|
|
2185
|
+
}
|
|
2138
2186
|
function loadConfig() {
|
|
2139
2187
|
const configPath = CONFIG_PATH;
|
|
2140
2188
|
let raw;
|
|
@@ -2142,11 +2190,14 @@ function loadConfig() {
|
|
|
2142
2190
|
raw = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
2143
2191
|
} catch (err) {
|
|
2144
2192
|
if (err.code === "ENOENT") {
|
|
2145
|
-
return { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, workers: {} };
|
|
2193
|
+
return { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, lastRun: {}, workers: {} };
|
|
2146
2194
|
}
|
|
2147
2195
|
throw err;
|
|
2148
2196
|
}
|
|
2149
|
-
const result = { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, workers: {} };
|
|
2197
|
+
const result = { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, lastRun: {}, workers: {} };
|
|
2198
|
+
if ("lastRun" in raw) {
|
|
2199
|
+
result.lastRun = parseLastRunEntry(raw["lastRun"]);
|
|
2200
|
+
}
|
|
2150
2201
|
if ("fixReviewPointCallbackCommentMessage" in raw) {
|
|
2151
2202
|
const val = raw["fixReviewPointCallbackCommentMessage"];
|
|
2152
2203
|
if (typeof val === "string") {
|
|
@@ -2173,6 +2224,36 @@ function getWorkerConfig(workerName) {
|
|
|
2173
2224
|
const config = loadConfig();
|
|
2174
2225
|
return config.workers[workerName] ?? { ...defaultsFor(workerName) };
|
|
2175
2226
|
}
|
|
2227
|
+
function getLastRunAt(workerName) {
|
|
2228
|
+
let at;
|
|
2229
|
+
try {
|
|
2230
|
+
at = loadConfig().lastRun[workerName];
|
|
2231
|
+
} catch (err) {
|
|
2232
|
+
console.warn(`[config] failed to load lastRun, treating ${workerName} as never run: ${err}`);
|
|
2233
|
+
return void 0;
|
|
2234
|
+
}
|
|
2235
|
+
if (at === void 0) return void 0;
|
|
2236
|
+
const parsed = Date.parse(at);
|
|
2237
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2238
|
+
}
|
|
2239
|
+
function writeLastRun(repoRoot, workerName, at = /* @__PURE__ */ new Date()) {
|
|
2240
|
+
const path2 = join2(repoRoot, "claude-task-worker.json");
|
|
2241
|
+
let raw = {};
|
|
2242
|
+
try {
|
|
2243
|
+
const parsed = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
2244
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2245
|
+
raw = parsed;
|
|
2246
|
+
}
|
|
2247
|
+
} catch (err) {
|
|
2248
|
+
if (err.code !== "ENOENT") {
|
|
2249
|
+
console.warn(`[config] failed to read ${path2}, rewriting it with lastRun only: ${err}`);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
const current = typeof raw["lastRun"] === "object" && raw["lastRun"] !== null ? raw["lastRun"] : {};
|
|
2253
|
+
raw["lastRun"] = { ...current, [workerName]: at.toISOString() };
|
|
2254
|
+
writeFileSync2(path2, `${JSON.stringify(raw, null, 2)}
|
|
2255
|
+
`, "utf-8");
|
|
2256
|
+
}
|
|
2176
2257
|
function getUiDesignConfig() {
|
|
2177
2258
|
try {
|
|
2178
2259
|
return loadConfig().uiDesign;
|
|
@@ -2722,13 +2803,13 @@ function isGeneratedWorktreeName(name) {
|
|
|
2722
2803
|
|
|
2723
2804
|
// src/slack.ts
|
|
2724
2805
|
import { exec } from "node:child_process";
|
|
2725
|
-
import { readFileSync as readFileSync4, writeFileSync as
|
|
2806
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2726
2807
|
import { homedir as homedir4 } from "node:os";
|
|
2727
2808
|
import { join as join5 } from "node:path";
|
|
2728
2809
|
import { promisify as promisify2 } from "node:util";
|
|
2729
2810
|
|
|
2730
2811
|
// src/runcat.ts
|
|
2731
|
-
import { mkdirSync as mkdirSync2, renameSync as renameSync2, rmSync, writeFileSync as
|
|
2812
|
+
import { mkdirSync as mkdirSync2, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2732
2813
|
import { homedir as homedir3 } from "node:os";
|
|
2733
2814
|
import { dirname, join as join4 } from "node:path";
|
|
2734
2815
|
var RUNCAT_OUT_FILE = process.env.RUNCAT_OUT_FILE ?? join4(process.env.HOME ?? homedir3(), ".claude", "runcat-usage.json");
|
|
@@ -2798,7 +2879,7 @@ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
|
|
|
2798
2879
|
const tmp = join4(dir, `.runcat-${process.pid}-${Date.now()}.json`);
|
|
2799
2880
|
try {
|
|
2800
2881
|
mkdirSync2(dir, { recursive: true });
|
|
2801
|
-
|
|
2882
|
+
writeFileSync3(tmp, JSON.stringify(snapshot), "utf-8");
|
|
2802
2883
|
renameSync2(tmp, outFile);
|
|
2803
2884
|
} catch (err) {
|
|
2804
2885
|
rmSync(tmp, { force: true });
|
|
@@ -2849,7 +2930,7 @@ function readUsageCache() {
|
|
|
2849
2930
|
}
|
|
2850
2931
|
function writeUsageCache(data) {
|
|
2851
2932
|
try {
|
|
2852
|
-
|
|
2933
|
+
writeFileSync4(USAGE_CACHE_PATH, JSON.stringify({ timestamp: Date.now(), data }));
|
|
2853
2934
|
} catch {
|
|
2854
2935
|
}
|
|
2855
2936
|
}
|
|
@@ -3816,6 +3897,107 @@ var applyUiDesignWorker = async (opts = {}) => {
|
|
|
3816
3897
|
})();
|
|
3817
3898
|
};
|
|
3818
3899
|
|
|
3900
|
+
// src/workers/scheduled-worker.ts
|
|
3901
|
+
var SCHEDULE_INTERVAL_HOURS = 24;
|
|
3902
|
+
var SCHEDULE_INTERVAL_MS = SCHEDULE_INTERVAL_HOURS * 60 * 60 * 1e3;
|
|
3903
|
+
var SCOPE_DAYS = SCHEDULE_INTERVAL_HOURS / 24;
|
|
3904
|
+
function createScheduledWorker(config) {
|
|
3905
|
+
return async () => {
|
|
3906
|
+
const { owner, name: repoName, defaultBranch } = await getRepoInfo();
|
|
3907
|
+
const { pollingIntervalSeconds } = getWorkerConfig(config.name);
|
|
3908
|
+
console.log(
|
|
3909
|
+
`[${config.name}] Checking every ${pollingIntervalSeconds} seconds (runs at most once per ${SCHEDULE_INTERVAL_HOURS}h for ${owner}/${repoName})`
|
|
3910
|
+
);
|
|
3911
|
+
let startedAt = 0;
|
|
3912
|
+
const tick = async () => {
|
|
3913
|
+
if (isShuttingDown()) return;
|
|
3914
|
+
if (config.enabled && !config.enabled()) return;
|
|
3915
|
+
if (isRunning(config.taskId)) return;
|
|
3916
|
+
const now = Date.now();
|
|
3917
|
+
const lastRunAt = Math.max(getLastRunAt(config.name) ?? 0, startedAt);
|
|
3918
|
+
if (lastRunAt > 0 && now - lastRunAt < SCHEDULE_INTERVAL_MS) return;
|
|
3919
|
+
const worktreeId = generateWorktreeName();
|
|
3920
|
+
startedAt = now;
|
|
3921
|
+
try {
|
|
3922
|
+
syncDefaultBranch(defaultBranch);
|
|
3923
|
+
const { model, effort, skill, advisorModel } = getWorkerConfig(config.name);
|
|
3924
|
+
const command = skill || config.command;
|
|
3925
|
+
const mode = getRunMode();
|
|
3926
|
+
const execution = buildClaudeExecution({
|
|
3927
|
+
mode,
|
|
3928
|
+
prompt: `${command} ${SCOPE_DAYS}`,
|
|
3929
|
+
model,
|
|
3930
|
+
effort,
|
|
3931
|
+
advisorModel: isAdvisorEnabled() ? advisorModel : "",
|
|
3932
|
+
permissionMode: getPermissionMode()
|
|
3933
|
+
});
|
|
3934
|
+
await createWorktreeFromBranch(worktreeId, defaultBranch);
|
|
3935
|
+
const cwd = getWorktreePath(worktreeId);
|
|
3936
|
+
writeLastRun(cwd, config.name, new Date(now));
|
|
3937
|
+
console.log(`[${config.name}] created worktree ${worktreeId} from ${defaultBranch}`);
|
|
3938
|
+
const repoUrl = `https://github.com/${owner}/${repoName}`;
|
|
3939
|
+
run(
|
|
3940
|
+
execution.command,
|
|
3941
|
+
execution.args,
|
|
3942
|
+
config.taskId,
|
|
3943
|
+
config.name,
|
|
3944
|
+
config.name,
|
|
3945
|
+
worktreeId,
|
|
3946
|
+
async (status, output) => {
|
|
3947
|
+
try {
|
|
3948
|
+
if (status === "completed") {
|
|
3949
|
+
await notifyTaskCompleted(config.name, repoName, config.taskId, config.name, repoUrl, output);
|
|
3950
|
+
} else {
|
|
3951
|
+
await notifyTaskFailed(config.name, repoName, config.taskId, config.name, repoUrl, output);
|
|
3952
|
+
}
|
|
3953
|
+
} catch (err) {
|
|
3954
|
+
console.error(`[${config.name}] post-task error: ${err}`);
|
|
3955
|
+
} finally {
|
|
3956
|
+
await removeWorktree(worktreeId).catch(
|
|
3957
|
+
(err) => console.error(`[${config.name}] removeWorktree failed: ${err}`)
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
},
|
|
3961
|
+
cwd,
|
|
3962
|
+
buildClaudeEnv(mode)
|
|
3963
|
+
);
|
|
3964
|
+
} catch (err) {
|
|
3965
|
+
console.error(`[${config.name}] setup error: ${err}`);
|
|
3966
|
+
await removeWorktree(worktreeId).catch(() => {
|
|
3967
|
+
});
|
|
3968
|
+
await notifyError(config.name, repoName, err);
|
|
3969
|
+
}
|
|
3970
|
+
};
|
|
3971
|
+
await tick();
|
|
3972
|
+
setInterval(tick, pollingIntervalSeconds * 1e3);
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// src/workers/update-coding-guidelines.ts
|
|
3977
|
+
var updateCodingGuidelinesWorker = createScheduledWorker({
|
|
3978
|
+
name: "update-coding-guidelines",
|
|
3979
|
+
command: "/claude-task-worker:update-coding-guidelines",
|
|
3980
|
+
taskId: -1
|
|
3981
|
+
});
|
|
3982
|
+
|
|
3983
|
+
// src/workers/update-requirement-rules.ts
|
|
3984
|
+
var updateRequirementRulesWorker = createScheduledWorker({
|
|
3985
|
+
name: "update-requirement-rules",
|
|
3986
|
+
command: "/claude-task-worker:update-requirement-rules",
|
|
3987
|
+
taskId: -2
|
|
3988
|
+
});
|
|
3989
|
+
|
|
3990
|
+
// src/workers/update-design-md.ts
|
|
3991
|
+
var updateDesignMdWorker = createScheduledWorker({
|
|
3992
|
+
name: "update-design-md",
|
|
3993
|
+
command: "/claude-task-worker:update-design-md",
|
|
3994
|
+
taskId: -3,
|
|
3995
|
+
// DESIGN.md の材料は `cc-ui-design` ラベル付きのマージ済みデザインPRで、それを作るのは
|
|
3996
|
+
// uiDesign.enabled のデザイン先行フローだけ。無効なリポジトリでは収集対象が原理的に
|
|
3997
|
+
// 存在しないため、空振りのセッションを毎日起動しないようここで止める。
|
|
3998
|
+
enabled: () => getUiDesignConfig().enabled
|
|
3999
|
+
});
|
|
4000
|
+
|
|
3819
4001
|
// src/index.ts
|
|
3820
4002
|
init_table();
|
|
3821
4003
|
|
|
@@ -4223,7 +4405,10 @@ var WORKERS = {
|
|
|
4223
4405
|
"check-dependabot": checkDependabotWorker,
|
|
4224
4406
|
"epic-issue": epicIssueWorker,
|
|
4225
4407
|
"create-ui-design": createUiDesignWorker,
|
|
4226
|
-
"apply-ui-design": applyUiDesignWorker
|
|
4408
|
+
"apply-ui-design": applyUiDesignWorker,
|
|
4409
|
+
"update-coding-guidelines": updateCodingGuidelinesWorker,
|
|
4410
|
+
"update-requirement-rules": updateRequirementRulesWorker,
|
|
4411
|
+
"update-design-md": updateDesignMdWorker
|
|
4227
4412
|
};
|
|
4228
4413
|
function printUsage() {
|
|
4229
4414
|
console.log(`Usage: claude-task-worker <command> [--project <name>] [--epic <issue-number>] [--label <label-name>]
|
|
@@ -4248,6 +4433,9 @@ Workers:
|
|
|
4248
4433
|
epic-issue Poll cc-epic-issue issues and create epic PR when all sub-issues are closed
|
|
4249
4434
|
create-ui-design Poll cc-create-ui-design issues and create a Pencil design PR (requires uiDesign.enabled)
|
|
4250
4435
|
apply-ui-design Poll cc-ui-design-pr-created issues and write the design reference back once the design PR is merged (requires uiDesign.enabled)
|
|
4436
|
+
update-coding-guidelines Run /update-coding-guidelines once every 24 hours over the last 24 hours
|
|
4437
|
+
update-requirement-rules Run /update-requirement-rules once every 24 hours over the last 24 hours
|
|
4438
|
+
update-design-md Run /update-design-md once every 24 hours over the last 24 hours (requires uiDesign.enabled)
|
|
4251
4439
|
all Poll all workers except triage-created-issue, triage-pr, check-dependabot
|
|
4252
4440
|
yolo Poll all workers including triage-created-issue, triage-pr, check-dependabot
|
|
4253
4441
|
|
|
@@ -4433,7 +4621,12 @@ if (hasProjectFilter()) {
|
|
|
4433
4621
|
resolveConflictWorker(),
|
|
4434
4622
|
epicIssueWorker({ epicFilters, labelFilters }),
|
|
4435
4623
|
createUiDesignWorker({ epicFilters, labelFilters }),
|
|
4436
|
-
applyUiDesignWorker({ epicFilters, labelFilters })
|
|
4624
|
+
applyUiDesignWorker({ epicFilters, labelFilters }),
|
|
4625
|
+
// 24時間おきの定期ワーカー。update-design-md は uiDesign.enabled が false のとき
|
|
4626
|
+
// 自身で no-op になる(create-ui-design / apply-ui-design と同じ扱い)。
|
|
4627
|
+
updateCodingGuidelinesWorker(),
|
|
4628
|
+
updateRequirementRulesWorker(),
|
|
4629
|
+
updateDesignMdWorker()
|
|
4437
4630
|
]);
|
|
4438
4631
|
})();
|
|
4439
4632
|
} else if (workerType === "yolo") {
|
|
@@ -4454,7 +4647,10 @@ if (hasProjectFilter()) {
|
|
|
4454
4647
|
resolveConflictWorker(),
|
|
4455
4648
|
epicIssueWorker({ epicFilters, labelFilters }),
|
|
4456
4649
|
createUiDesignWorker({ epicFilters, labelFilters }),
|
|
4457
|
-
applyUiDesignWorker({ epicFilters, labelFilters })
|
|
4650
|
+
applyUiDesignWorker({ epicFilters, labelFilters }),
|
|
4651
|
+
updateCodingGuidelinesWorker(),
|
|
4652
|
+
updateRequirementRulesWorker(),
|
|
4653
|
+
updateDesignMdWorker()
|
|
4458
4654
|
]);
|
|
4459
4655
|
})();
|
|
4460
4656
|
} else {
|