claude-task-worker 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +100 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1639,10 +1639,91 @@ function isGeneratedWorktreeName(name) {
1639
1639
 
1640
1640
  // src/slack.ts
1641
1641
  import { exec } from "node:child_process";
1642
- import { readFileSync as readFileSync2, writeFileSync } from "node:fs";
1643
- import { homedir } from "node:os";
1644
- import { join as join2 } from "node:path";
1642
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
1643
+ import { homedir as homedir2 } from "node:os";
1644
+ import { join as join3 } from "node:path";
1645
1645
  import { promisify as promisify2 } from "node:util";
1646
+
1647
+ // src/runcat.ts
1648
+ import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
1649
+ import { homedir } from "node:os";
1650
+ import { dirname, join as join2 } from "node:path";
1651
+ var RUNCAT_OUT_FILE = process.env.RUNCAT_OUT_FILE ?? join2(process.env.HOME ?? homedir(), ".claude", "runcat-usage.json");
1652
+ var JST = "Asia/Tokyo";
1653
+ function formatG(value) {
1654
+ return String(Number(value.toPrecision(6)));
1655
+ }
1656
+ function jstFields(date) {
1657
+ const parts = new Intl.DateTimeFormat("en-US", {
1658
+ timeZone: JST,
1659
+ month: "2-digit",
1660
+ day: "2-digit",
1661
+ hour: "2-digit",
1662
+ minute: "2-digit",
1663
+ hourCycle: "h23"
1664
+ }).formatToParts(date);
1665
+ const pick2 = (type) => parts.find((p) => p.type === type)?.value ?? "";
1666
+ return { month: pick2("month"), day: pick2("day"), hour: pick2("hour"), minute: pick2("minute") };
1667
+ }
1668
+ var MINUTE_MS = 6e4;
1669
+ function ceilToMinute(date) {
1670
+ const remainder = date.getTime() % MINUTE_MS;
1671
+ return remainder === 0 ? date : new Date(date.getTime() + (MINUTE_MS - remainder));
1672
+ }
1673
+ function parseResetsAt(isoString) {
1674
+ const date = new Date(isoString);
1675
+ return Number.isNaN(date.getTime()) ? null : ceilToMinute(date);
1676
+ }
1677
+ function resetStamp(isoString, now = /* @__PURE__ */ new Date()) {
1678
+ const date = parseResetsAt(isoString);
1679
+ if (!date) return "";
1680
+ const reset = jstFields(date);
1681
+ const today = jstFields(now);
1682
+ const time = `${reset.hour}:${reset.minute}`;
1683
+ if (reset.month === today.month && reset.day === today.day) return time;
1684
+ return `${reset.month}/${reset.day} ${time}`;
1685
+ }
1686
+ function resetHour(isoString) {
1687
+ const date = parseResetsAt(isoString);
1688
+ return date ? jstFields(date).hour : "";
1689
+ }
1690
+ function metric(title, pct, resetsAt, now) {
1691
+ const stamp = resetStamp(resetsAt, now);
1692
+ return {
1693
+ title,
1694
+ formattedValue: `${formatG(pct)}%` + (stamp ? ` \u21BB${stamp}` : ""),
1695
+ normalizedValue: Math.round(pct / 100 * 1e4) / 1e4
1696
+ };
1697
+ }
1698
+ function buildRuncatSnapshot(usage, now = /* @__PURE__ */ new Date()) {
1699
+ const bar = `${formatG(usage.fiveHourUtilization)}/${formatG(usage.sevenDayUtilization)}`;
1700
+ const stamp = resetHour(usage.fiveHourResetsAt);
1701
+ return {
1702
+ title: "Claude Code",
1703
+ symbol: "staroflife",
1704
+ metrics: [
1705
+ metric("5h", usage.fiveHourUtilization, usage.fiveHourResetsAt, now),
1706
+ metric("7d", usage.sevenDayUtilization, usage.sevenDayResetsAt, now)
1707
+ ],
1708
+ metricsBarValue: stamp ? `${bar} \u21BB${stamp}` : bar,
1709
+ lastUpdatedDate: now.toISOString().replace(/\.\d{3}Z$/, "Z")
1710
+ };
1711
+ }
1712
+ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
1713
+ const snapshot = buildRuncatSnapshot(usage);
1714
+ const dir = dirname(outFile);
1715
+ const tmp = join2(dir, `.runcat-${process.pid}-${Date.now()}.json`);
1716
+ try {
1717
+ mkdirSync(dir, { recursive: true });
1718
+ writeFileSync(tmp, JSON.stringify(snapshot), "utf-8");
1719
+ renameSync(tmp, outFile);
1720
+ } catch (err) {
1721
+ rmSync(tmp, { force: true });
1722
+ console.error(`[runcat] Failed to write ${outFile}: ${err}`);
1723
+ }
1724
+ }
1725
+
1726
+ // src/slack.ts
1646
1727
  var execAsync = promisify2(exec);
1647
1728
  var WEBHOOK_URL = process.env.CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL;
1648
1729
  var USAGE_CACHE_PATH = "/tmp/claude-usage-cache.json";
@@ -1668,7 +1749,7 @@ async function getOAuthToken() {
1668
1749
  const { stdout } = await execAsync('security find-generic-password -s "Claude Code-credentials" -w');
1669
1750
  return extractToken(JSON.parse(stdout.trim()));
1670
1751
  } catch {
1671
- const raw = readFileSync2(join2(homedir(), ".claude", ".credentials.json"), "utf-8");
1752
+ const raw = readFileSync2(join3(homedir2(), ".claude", ".credentials.json"), "utf-8");
1672
1753
  return extractToken(JSON.parse(raw));
1673
1754
  }
1674
1755
  }
@@ -1685,7 +1766,7 @@ function readUsageCache() {
1685
1766
  }
1686
1767
  function writeUsageCache(data) {
1687
1768
  try {
1688
- writeFileSync(USAGE_CACHE_PATH, JSON.stringify({ timestamp: Date.now(), data }));
1769
+ writeFileSync2(USAGE_CACHE_PATH, JSON.stringify({ timestamp: Date.now(), data }));
1689
1770
  } catch {
1690
1771
  }
1691
1772
  }
@@ -1733,9 +1814,7 @@ function formatResetTimeJST(isoString) {
1733
1814
  minute: "2-digit"
1734
1815
  });
1735
1816
  }
1736
- async function buildTokenLimitText() {
1737
- const usage = await fetchUsageInfo();
1738
- if (!usage) return "";
1817
+ function formatTokenLimitText(usage) {
1739
1818
  const fiveH = usage.fiveHourUtilization.toFixed(1);
1740
1819
  const sevenD = usage.sevenDayUtilization.toFixed(1);
1741
1820
  const emoji = utilizationEmoji(Math.max(usage.fiveHourUtilization, usage.sevenDayUtilization));
@@ -1743,6 +1822,12 @@ async function buildTokenLimitText() {
1743
1822
  const sevenDReset = formatResetTimeJST(usage.sevenDayResetsAt);
1744
1823
  return ` | ${emoji} 5h: ${fiveH}% (reset: ${fiveHReset}) / 7d: ${sevenD}% (reset: ${sevenDReset})`;
1745
1824
  }
1825
+ async function buildTokenLimitText() {
1826
+ const usage = await fetchUsageInfo();
1827
+ if (!usage) return "";
1828
+ writeRuncatUsage(usage);
1829
+ return formatTokenLimitText(usage);
1830
+ }
1746
1831
  async function notifyTaskCompleted(workerName, repoName, id, title, url, output) {
1747
1832
  const tokenText = await buildTokenLimitText();
1748
1833
  const truncatedOutput = output && output.length > 1e3 ? `\u2026${output.slice(-1e3)}` : output;
@@ -2602,11 +2687,11 @@ async function update() {
2602
2687
 
2603
2688
  // src/commands/version.ts
2604
2689
  import { readFileSync as readFileSync3 } from "node:fs";
2605
- import { dirname, join as join3 } from "node:path";
2690
+ import { dirname as dirname2, join as join4 } from "node:path";
2606
2691
  import { fileURLToPath } from "node:url";
2607
2692
  function version() {
2608
- const here = dirname(fileURLToPath(import.meta.url));
2609
- const pkgPath = join3(here, "..", "package.json");
2693
+ const here = dirname2(fileURLToPath(import.meta.url));
2694
+ const pkgPath = join4(here, "..", "package.json");
2610
2695
  try {
2611
2696
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
2612
2697
  console.log(pkg.version ?? "unknown");
@@ -2661,8 +2746,8 @@ function buildForwardedCommand(argv) {
2661
2746
 
2662
2747
  // src/projects-config.ts
2663
2748
  import { readFileSync as readFileSync4, statSync } from "node:fs";
2664
- import { homedir as homedir2 } from "node:os";
2665
- import { isAbsolute, join as join4 } from "node:path";
2749
+ import { homedir as homedir3 } from "node:os";
2750
+ import { isAbsolute, join as join5 } from "node:path";
2666
2751
  var RESERVED_ALL = "all";
2667
2752
  var ProjectsConfigError = class extends Error {
2668
2753
  constructor(message) {
@@ -2672,8 +2757,8 @@ var ProjectsConfigError = class extends Error {
2672
2757
  };
2673
2758
  function getProjectsConfigPath() {
2674
2759
  const xdg = process.env.XDG_CONFIG_HOME;
2675
- const configHome = xdg && xdg.length > 0 ? xdg : join4(homedir2(), ".config");
2676
- return join4(configHome, "claude-task-worker", "projects.json");
2760
+ const configHome = xdg && xdg.length > 0 ? xdg : join5(homedir3(), ".config");
2761
+ return join5(configHome, "claude-task-worker", "projects.json");
2677
2762
  }
2678
2763
  var PROJECTS_CONFIG_PATH = getProjectsConfigPath();
2679
2764
  function isDirectory(path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-worker",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
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",