claude-task-worker 0.137.0 → 0.139.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 CHANGED
@@ -139,7 +139,7 @@ claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--
139
139
  | `install` / `update` | 上記「セットアップ」を参照 |
140
140
  | `cloud-setup [--force]` | クラウド VM 側の準備(下記「`--cloud`」を参照) |
141
141
  | `list-workers` | プリセットとカスタムの全ワーカーを名前・種別・有効/無効・ロード元で表示(`workerFiles` のロード失敗時は exit 1) |
142
- | `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack にも通知 |
142
+ | `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack にも通知。以降は360秒(6分)ごとに取得し直して表示・RunCat 用スナップショットを更新し続ける(Ctrl-C で終了。Slack 通知は起動時のみ)。プランによって返らない窓(Ultra の5時間枠など)は省いて表示する |
143
143
  | `version` | CLI のバージョンを表示(`--version` / `-v` も可) |
144
144
 
145
145
  ### `--epic <issue-number>`
@@ -1303,6 +1303,9 @@ function buildCloudGitHubAccessInstruction() {
1303
1303
  function buildCloudToolRestriction() {
1304
1304
  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(", ")}`;
1305
1305
  }
1306
+ function buildCloudFinalMessageInstruction() {
1307
+ return "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u7D42\u30E1\u30C3\u30BB\u30FC\u30B8\uFF08\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u308B\u3068\u304D\u306E\u6700\u5F8C\u306E\u767A\u8A00\uFF09\u306F\u3001\u4F55\u3092\u3057\u305F\u304B\u30FB\u3069\u3053\u3067\u6B62\u307E\u3063\u305F\u304B\u306E\u4E8B\u5B9F\u306E\u5831\u544A\u306B\u3068\u3069\u3081\u308B\u3053\u3068\u3002\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u8FD4\u4FE1\u3059\u308B\u4EBA\u306F\u3044\u306A\u3044\u305F\u3081\u3001\u8AAD\u307F\u624B\u3078\u306E\u4F9D\u983C\u30FB\u8CEA\u554F\u30FB\u5224\u65AD\u306E\u8981\u6C42\uFF08\u300C\u301C\u3092\u5224\u65AD\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u300D\u300C\u301C\u3057\u3066\u304F\u3060\u3055\u3044\u300D\u300C\u301C\u3057\u307E\u3059\u304B\u300D\uFF09\u3067\u7DE0\u3081\u306A\u3044\u3053\u3068\u3002\u4EBA\u306E\u5224\u65AD\u304C\u5FC5\u8981\u306A\u4E8B\u9805\u306F\u3001\u30B9\u30AD\u30EB\u306E\u624B\u9806\u3069\u304A\u308A GitHub \u306E\u30E9\u30D9\u30EB\u3068\u30B3\u30E1\u30F3\u30C8\u3067\u5F15\u304D\u7D99\u304E\u3001\u6700\u7D42\u30E1\u30C3\u30BB\u30FC\u30B8\u306B\u306F\u300C`cc-need-human-check` \u3092\u4ED8\u3051\u3066 PR #N \u306B\u7406\u7531\u3092\u30B3\u30E1\u30F3\u30C8\u3057\u305F\u300D\u306E\u3088\u3046\u306B\u3001\u5F15\u304D\u7D99\u3044\u3060\u4E8B\u5B9F\u3060\u3051\u3092\u66F8\u304F\u3053\u3068\u3002";
1308
+ }
1306
1309
  function buildCloudPrompt(prompt, model, target, debug = false, startedAt) {
1307
1310
  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
1308
1311
 
@@ -1310,7 +1313,9 @@ ${systemPromptFor(model)}
1310
1313
 
1311
1314
  ${buildCloudToolRestriction()}
1312
1315
 
1313
- ${buildCloudGitHubAccessInstruction()}`;
1316
+ ${buildCloudGitHubAccessInstruction()}
1317
+
1318
+ ${buildCloudFinalMessageInstruction()}`;
1314
1319
  const withPrinciples = `${prompt}
1315
1320
 
1316
1321
  ${principles}`;
@@ -2167,6 +2172,7 @@ function ceilToMinute(date) {
2167
2172
  return remainder === 0 ? date : new Date(date.getTime() + (MINUTE_MS - remainder));
2168
2173
  }
2169
2174
  function parseResetsAt(isoString) {
2175
+ if (!isoString) return null;
2170
2176
  const date = new Date(isoString);
2171
2177
  return Number.isNaN(date.getTime()) ? null : ceilToMinute(date);
2172
2178
  }
@@ -2192,15 +2198,17 @@ function metric(title, pct, resetsAt, now) {
2192
2198
  };
2193
2199
  }
2194
2200
  function buildRuncatSnapshot(usage, now = /* @__PURE__ */ new Date()) {
2195
- const bar = `${formatG(usage.fiveHourUtilization)}/${formatG(usage.sevenDayUtilization)}`;
2196
- const stamp = resetHour(usage.fiveHourResetsAt);
2201
+ const windows = [
2202
+ ["5h", usage.fiveHourUtilization, usage.fiveHourResetsAt],
2203
+ ["7d", usage.sevenDayUtilization, usage.sevenDayResetsAt]
2204
+ ];
2205
+ const present = windows.filter((w) => w[1] !== null);
2206
+ const bar = present.map(([, pct]) => formatG(pct)).join("/");
2207
+ const stamp = present.length > 0 ? resetHour(present[0][2]) : "";
2197
2208
  return {
2198
2209
  title: "Claude Code",
2199
2210
  symbol: "staroflife",
2200
- metrics: [
2201
- metric("5h", usage.fiveHourUtilization, usage.fiveHourResetsAt, now),
2202
- metric("7d", usage.sevenDayUtilization, usage.sevenDayResetsAt, now)
2203
- ],
2211
+ metrics: present.map(([title, pct, resetsAt]) => metric(title, pct, resetsAt, now)),
2204
2212
  metricsBarValue: stamp ? `${bar} \u21BB${stamp}` : bar,
2205
2213
  lastUpdatedDate: now.toISOString().replace(/\.\d{3}Z$/, "Z")
2206
2214
  };
@@ -2214,7 +2222,10 @@ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
2214
2222
  writeFileSync3(tmp, JSON.stringify(snapshot), "utf-8");
2215
2223
  renameSync2(tmp, outFile);
2216
2224
  } catch (err) {
2217
- rmSync(tmp, { force: true });
2225
+ try {
2226
+ rmSync(tmp, { force: true });
2227
+ } catch {
2228
+ }
2218
2229
  console.error(`[runcat] Failed to write ${outFile}: ${err}`);
2219
2230
  }
2220
2231
  }
@@ -2223,6 +2234,7 @@ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
2223
2234
  var execAsync = promisify2(exec);
2224
2235
  var WEBHOOK_URL = process.env.CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL;
2225
2236
  var USAGE_CACHE_PATH = "/tmp/claude-usage-cache.json";
2237
+ var USAGE_POLL_INTERVAL_SECONDS = 360;
2226
2238
  var USAGE_CACHE_TTL_SECONDS = 360;
2227
2239
  async function send(payload) {
2228
2240
  if (!WEBHOOK_URL) return;
@@ -2236,6 +2248,14 @@ async function send(payload) {
2236
2248
  console.error(`[slack] Failed to send notification: ${err}`);
2237
2249
  }
2238
2250
  }
2251
+ function parseUsageResponse(body) {
2252
+ return {
2253
+ fiveHourUtilization: body.five_hour?.utilization ?? null,
2254
+ fiveHourResetsAt: body.five_hour?.resets_at ?? null,
2255
+ sevenDayUtilization: body.seven_day?.utilization ?? null,
2256
+ sevenDayResetsAt: body.seven_day?.resets_at ?? null
2257
+ };
2258
+ }
2239
2259
  function extractToken(credentials) {
2240
2260
  if (typeof credentials === "string") return credentials;
2241
2261
  return credentials.claudeAiOauth?.accessToken ?? credentials.oauth_token ?? credentials.access_token ?? "";
@@ -2266,8 +2286,8 @@ function writeUsageCache(data) {
2266
2286
  } catch {
2267
2287
  }
2268
2288
  }
2269
- async function fetchUsageInfo() {
2270
- const cached = readUsageCache();
2289
+ async function fetchUsageInfo(fresh) {
2290
+ const cached = fresh ? null : readUsageCache();
2271
2291
  if (cached) return cached;
2272
2292
  try {
2273
2293
  const token = await getOAuthToken();
@@ -2281,13 +2301,7 @@ async function fetchUsageInfo() {
2281
2301
  console.error(`[slack] Usage API returned ${res.status}`);
2282
2302
  return null;
2283
2303
  }
2284
- const body = await res.json();
2285
- const data = {
2286
- fiveHourUtilization: body.five_hour.utilization,
2287
- fiveHourResetsAt: body.five_hour.resets_at,
2288
- sevenDayUtilization: body.seven_day.utilization,
2289
- sevenDayResetsAt: body.seven_day.resets_at
2290
- };
2304
+ const data = parseUsageResponse(await res.json());
2291
2305
  writeUsageCache(data);
2292
2306
  return data;
2293
2307
  } catch (err) {
@@ -2310,17 +2324,23 @@ function formatResetTimeJST(isoString) {
2310
2324
  minute: "2-digit"
2311
2325
  });
2312
2326
  }
2327
+ function formatWindow(label, utilization, resetsAt) {
2328
+ if (utilization === null) return null;
2329
+ const reset = resetsAt ? ` (reset: ${formatResetTimeJST(resetsAt)})` : "";
2330
+ return `${label}: ${utilization.toFixed(1)}%${reset}`;
2331
+ }
2313
2332
  function formatTokenLimitText(usage) {
2314
- const fiveH = usage.fiveHourUtilization.toFixed(1);
2315
- const sevenD = usage.sevenDayUtilization.toFixed(1);
2316
- const emoji = utilizationEmoji(Math.max(usage.fiveHourUtilization, usage.sevenDayUtilization));
2317
- const fiveHReset = formatResetTimeJST(usage.fiveHourResetsAt);
2318
- const sevenDReset = formatResetTimeJST(usage.sevenDayResetsAt);
2319
- return ` | ${emoji} 5h: ${fiveH}% (reset: ${fiveHReset}) / 7d: ${sevenD}% (reset: ${sevenDReset})`;
2320
- }
2321
- async function buildTokenLimitText() {
2322
- const usage = await fetchUsageInfo();
2323
- if (!usage) return "";
2333
+ const parts = [
2334
+ formatWindow("5h", usage.fiveHourUtilization, usage.fiveHourResetsAt),
2335
+ formatWindow("7d", usage.sevenDayUtilization, usage.sevenDayResetsAt)
2336
+ ].filter((p) => p !== null);
2337
+ if (parts.length === 0) return "";
2338
+ const values = [usage.fiveHourUtilization, usage.sevenDayUtilization].filter((v) => v !== null);
2339
+ return ` | ${utilizationEmoji(Math.max(...values))} ${parts.join(" / ")}`;
2340
+ }
2341
+ async function buildTokenLimitText(fresh = false) {
2342
+ const usage = await fetchUsageInfo(fresh);
2343
+ if (!usage) return null;
2324
2344
  writeRuncatUsage(usage);
2325
2345
  return formatTokenLimitText(usage);
2326
2346
  }
@@ -2348,7 +2368,7 @@ function buildTaskNotificationText(params) {
2348
2368
  return `${sessionUrlPrefix}${body}`;
2349
2369
  }
2350
2370
  async function notifyTaskCompleted(workerName, repoName, id, title, url, output, cloud) {
2351
- const tokenText = await buildTokenLimitText();
2371
+ const tokenText = await buildTokenLimitText() ?? "";
2352
2372
  await send({
2353
2373
  text: buildTaskNotificationText({
2354
2374
  status: "completed",
@@ -2364,7 +2384,7 @@ async function notifyTaskCompleted(workerName, repoName, id, title, url, output,
2364
2384
  });
2365
2385
  }
2366
2386
  async function notifyTaskFailed(workerName, repoName, id, title, url, output, cloud) {
2367
- const tokenText = await buildTokenLimitText();
2387
+ const tokenText = await buildTokenLimitText() ?? "";
2368
2388
  await send({
2369
2389
  text: buildTaskNotificationText({
2370
2390
  status: "failed",
@@ -3179,6 +3199,7 @@ export {
3179
3199
  waitForAllProcesses,
3180
3200
  shutdown,
3181
3201
  generateWorktreeName,
3202
+ USAGE_POLL_INTERVAL_SECONDS,
3182
3203
  send,
3183
3204
  buildTokenLimitText,
3184
3205
  notifyTaskCompleted,
@@ -41,6 +41,7 @@ export declare function buildCloudWorktreeInstruction(prompt: string): string;
41
41
  export declare function buildCloudCheckoutInstruction(target: CloudPromptTarget): string;
42
42
  export declare function buildCloudGitHubAccessInstruction(): string;
43
43
  export declare function buildCloudToolRestriction(): string;
44
+ export declare function buildCloudFinalMessageInstruction(): string;
44
45
  export declare function buildCloudPrompt(prompt: string, model: string, target?: CloudPromptTarget, debug?: boolean, startedAt?: Date): string;
45
46
  export declare function shellQuote(value: string): string;
46
47
  export declare function buildScriptCommand(command: string, args: string[], platform?: string): {
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  INHERIT_CONFIG_KEY,
8
8
  LOCAL_CONFIG_PATH,
9
9
  SCHEDULED_WORKER_NAMES,
10
+ USAGE_POLL_INTERVAL_SECONDS,
10
11
  addAssignee,
11
12
  addLabel,
12
13
  bodyMentionsIssue,
@@ -64,7 +65,7 @@ import {
64
65
  shutdown,
65
66
  waitForAllProcesses,
66
67
  writeLastRun
67
- } from "./chunk-R4T3CDMO.js";
68
+ } from "./chunk-74OXBTVA.js";
68
69
  import {
69
70
  UserConfigError,
70
71
  assertCloudCompatibleCommand,
@@ -1822,15 +1823,18 @@ if (hasProjectFilter()) {
1822
1823
  await cloudSetup({ force: process.argv.slice(3).includes("--force") });
1823
1824
  })();
1824
1825
  } else if (workerType === "usage") {
1825
- (async () => {
1826
- const text = await buildTokenLimitText();
1827
- if (!text) {
1828
- console.error("Failed to fetch usage info");
1829
- process.exit(1);
1826
+ const pollUsage = async (notify) => {
1827
+ const text = await buildTokenLimitText(true);
1828
+ if (text === null) {
1829
+ console.error(`[usage] ${(/* @__PURE__ */ new Date()).toISOString()} Failed to fetch usage info`);
1830
+ return;
1830
1831
  }
1831
- console.log(text.trim());
1832
- await send({ text: `\u{1F4CA} Usage${text}` });
1833
- })();
1832
+ console.log(`[usage] ${(/* @__PURE__ */ new Date()).toISOString()}${text}`);
1833
+ if (notify) await send({ text: `\u{1F4CA} Usage${text}` });
1834
+ };
1835
+ void pollUsage(true);
1836
+ setInterval(() => void pollUsage(false), USAGE_POLL_INTERVAL_SECONDS * 1e3);
1837
+ console.log(`[usage] polling every ${USAGE_POLL_INTERVAL_SECONDS}s (Ctrl-C to stop)`);
1834
1838
  } else if (workerType === "list-workers") {
1835
1839
  for (const e of PRESET_WORKERS) printWorkerRow(e.definition.name, "preset", "preset");
1836
1840
  for (const c of customWorkers) printWorkerRow(c.definition.name, "custom", c.source);
package/dist/lib.js CHANGED
@@ -55,7 +55,7 @@ import {
55
55
  removeWorktreeByBranch,
56
56
  run,
57
57
  syncDefaultBranch
58
- } from "./chunk-R4T3CDMO.js";
58
+ } from "./chunk-74OXBTVA.js";
59
59
  import {
60
60
  getPermissionMode,
61
61
  getRunMode,
package/dist/runcat.d.ts CHANGED
@@ -12,9 +12,9 @@ export interface RuncatSnapshot {
12
12
  lastUpdatedDate: string;
13
13
  }
14
14
  /** 'HH:MM' 形式。日を跨ぐ場合は 'MM/DD HH:MM' として日付も付ける。不正値は空文字。 */
15
- export declare function resetStamp(isoString: string, now?: Date): string;
15
+ export declare function resetStamp(isoString: string | null, now?: Date): string;
16
16
  /** 'HH' のみ。バーのように幅が限られる用途向け。不正値は空文字。 */
17
- export declare function resetHour(isoString: string): string;
17
+ export declare function resetHour(isoString: string | null): string;
18
18
  export declare function buildRuncatSnapshot(usage: UsageInfo, now?: Date): RuncatSnapshot;
19
19
  /**
20
20
  * RunCat Neo 用のスナップショットを書き出す。
package/dist/slack.d.ts CHANGED
@@ -1,16 +1,30 @@
1
+ /** `usage` コマンドが利用状況をポーリングする間隔 */
2
+ export declare const USAGE_POLL_INTERVAL_SECONDS = 360;
1
3
  export declare function send(payload: Record<string, unknown>): Promise<void>;
2
4
  export interface UsageInfo {
3
- fiveHourUtilization: number;
4
- fiveHourResetsAt: string;
5
- sevenDayUtilization: number;
6
- sevenDayResetsAt: string;
5
+ fiveHourUtilization: number | null;
6
+ fiveHourResetsAt: string | null;
7
+ sevenDayUtilization: number | null;
8
+ sevenDayResetsAt: string | null;
7
9
  }
10
+ interface UsageWindow {
11
+ utilization?: number | null;
12
+ resets_at?: string | null;
13
+ }
14
+ /** usage API のレスポンスを UsageInfo へ。欠けている窓は null にする(throw しない) */
15
+ export declare function parseUsageResponse(body: {
16
+ five_hour?: UsageWindow | null;
17
+ seven_day?: UsageWindow | null;
18
+ }): UsageInfo;
19
+ /** 返ってきた窓だけを並べる。どちらも無ければ空文字 */
20
+ export declare function formatTokenLimitText(usage: UsageInfo): string;
8
21
  /**
9
22
  * 現在の利用状況を取得し、RunCat Neo 用スナップショットを更新したうえで通知に添えるテキストを返す。
10
23
  * ワーカーの完了/失敗通知はすべてここを通るため、タスクが終わるたびに RunCat 側も最新化される
11
24
  * (Slack webhook 未設定でも通知が no-op になるだけで、スナップショットの更新は行われる)。
25
+ * 取得に失敗したときだけ null を返す(どの枠も返らない正常応答は空文字)。
12
26
  */
13
- export declare function buildTokenLimitText(): Promise<string>;
27
+ export declare function buildTokenLimitText(fresh?: boolean): Promise<string | null>;
14
28
  /**
15
29
  * 通知本文へ載せる output を末尾1000文字に切り詰める。
16
30
  * 案内文(CLOUD_FAILURE_GUIDANCE)は appendCloudFailureGuidance が output の先頭に
@@ -44,3 +58,4 @@ export declare function notifyTaskFailed(workerName: string, repoName: string, i
44
58
  sessionId?: string;
45
59
  }): Promise<void>;
46
60
  export declare function notifyError(workerName: string, repoName: string, error: unknown): Promise<void>;
61
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-worker",
3
- "version": "0.137.0",
3
+ "version": "0.139.0",
4
4
  "description": "CLI tool that polls GitHub Issues/PRs and delegates work to Claude CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",