claude-task-worker 0.39.2 → 0.40.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 +134 -60
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12,9 +12,13 @@ var __export = (target, all) => {
|
|
|
12
12
|
// src/table.ts
|
|
13
13
|
var table_exports = {};
|
|
14
14
|
__export(table_exports, {
|
|
15
|
+
LOG_DISPLAY_LIMIT: () => LOG_DISPLAY_LIMIT,
|
|
16
|
+
TASK_DISPLAY_LIMIT: () => TASK_DISPLAY_LIMIT,
|
|
17
|
+
buildLogTableLines: () => buildLogTableLines,
|
|
15
18
|
buildTaskTableLines: () => buildTaskTableLines,
|
|
16
19
|
getDisplayWidth: () => getDisplayWidth,
|
|
17
20
|
padToWidth: () => padToWidth,
|
|
21
|
+
selectRecentTasks: () => selectRecentTasks,
|
|
18
22
|
truncateToWidth: () => truncateToWidth
|
|
19
23
|
});
|
|
20
24
|
function getDisplayWidth(str) {
|
|
@@ -61,74 +65,97 @@ function formatTime(date) {
|
|
|
61
65
|
const s = String(date.getSeconds()).padStart(2, "0");
|
|
62
66
|
return `${h}:${m}:${s}`;
|
|
63
67
|
}
|
|
68
|
+
function renderBoxTable(headers, groups) {
|
|
69
|
+
const allRows = groups.flat();
|
|
70
|
+
const widths = headers.map(
|
|
71
|
+
(h, i) => Math.max(1, getDisplayWidth(h), ...allRows.map((r) => getDisplayWidth(r[i] ?? "")))
|
|
72
|
+
);
|
|
73
|
+
const border = (l, m, r) => `${l}${widths.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}`;
|
|
74
|
+
const row = (cells) => `\u2502 ${cells.map((c, i) => padToWidth(c ?? "", widths[i])).join(" \u2502 ")} \u2502`;
|
|
75
|
+
const lines = [];
|
|
76
|
+
lines.push(border("\u250C", "\u252C", "\u2510"));
|
|
77
|
+
lines.push(row(headers));
|
|
78
|
+
lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
79
|
+
const nonEmpty = groups.filter((g) => g.length > 0);
|
|
80
|
+
nonEmpty.forEach((group, gi) => {
|
|
81
|
+
if (gi > 0) lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
82
|
+
for (const r of group) lines.push(row(r));
|
|
83
|
+
});
|
|
84
|
+
lines.push(border("\u2514", "\u2534", "\u2518"));
|
|
85
|
+
return lines;
|
|
86
|
+
}
|
|
87
|
+
function taskRecency(t) {
|
|
88
|
+
return (t.finishedAt ?? t.startedAt).getTime();
|
|
89
|
+
}
|
|
90
|
+
function selectRecentTasks(entries, limit = TASK_DISPLAY_LIMIT) {
|
|
91
|
+
const byId = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const e of entries) byId.set(e.id, e);
|
|
93
|
+
const unique = [...byId.values()];
|
|
94
|
+
const running2 = unique.filter((t) => t.status === "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
95
|
+
const finished = unique.filter((t) => t.status !== "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
96
|
+
return [...running2, ...finished].slice(0, limit);
|
|
97
|
+
}
|
|
64
98
|
function buildTaskTableLines(entries, now = /* @__PURE__ */ new Date()) {
|
|
65
99
|
if (entries.length === 0) return [];
|
|
66
100
|
const runningTasks = entries.filter((t) => t.status === "running");
|
|
67
101
|
const finishedTasks = entries.filter((t) => t.status !== "running");
|
|
68
102
|
const maxTitleWidth = 40;
|
|
69
103
|
const maxPathWidth = 40;
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (const r of runningRows) {
|
|
118
|
-
lines.push(row(r.id, r.title, r.worker, r.path, r.status, r.time, r.duration));
|
|
119
|
-
}
|
|
120
|
-
if (runningRows.length > 0 && finishedRows.length > 0) {
|
|
121
|
-
lines.push(line("\u251C", "\u253C", "\u2524", "\u2500"));
|
|
122
|
-
}
|
|
123
|
-
for (const r of finishedRows) {
|
|
124
|
-
lines.push(row(r.id, r.title, r.worker, r.path, r.status, r.time, r.duration));
|
|
125
|
-
}
|
|
126
|
-
lines.push(line("\u2514", "\u2534", "\u2518", "\u2500"));
|
|
127
|
-
return lines;
|
|
104
|
+
const truncTitle = (s) => getDisplayWidth(s) > maxTitleWidth ? truncateToWidth(s, maxTitleWidth) : s;
|
|
105
|
+
const truncPath = (p) => p ? getDisplayWidth(p) > maxPathWidth ? truncateToWidth(p, maxPathWidth) : p : "";
|
|
106
|
+
const hasPath = entries.some((t) => truncPath(t.path) !== "");
|
|
107
|
+
const runningRows = runningTasks.map(
|
|
108
|
+
(t) => taskRow(
|
|
109
|
+
t,
|
|
110
|
+
truncTitle(t.title),
|
|
111
|
+
truncPath(t.path),
|
|
112
|
+
hasPath,
|
|
113
|
+
t.agentStatus ? `${t.status}:${t.agentStatus}` : t.status,
|
|
114
|
+
formatTime(t.startedAt),
|
|
115
|
+
formatDuration(t.startedAt, now)
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
const finishedRows = finishedTasks.map(
|
|
119
|
+
(t) => taskRow(
|
|
120
|
+
t,
|
|
121
|
+
truncTitle(t.title),
|
|
122
|
+
truncPath(t.path),
|
|
123
|
+
hasPath,
|
|
124
|
+
t.status,
|
|
125
|
+
formatTime(t.finishedAt ?? t.startedAt),
|
|
126
|
+
formatDuration(t.startedAt, t.finishedAt ?? now)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
const headers = hasPath ? ["#", "Title", "Worker", "Worktree", "Status", "Time", "Duration"] : ["#", "Title", "Worker", "Status", "Time", "Duration"];
|
|
130
|
+
return renderBoxTable(headers, [runningRows, finishedRows]);
|
|
131
|
+
}
|
|
132
|
+
function taskRow(t, title, path2, hasPath, status, time, duration) {
|
|
133
|
+
return hasPath ? [`#${t.id}`, title, t.workerName, path2, status, time, duration] : [`#${t.id}`, title, t.workerName, status, time, duration];
|
|
134
|
+
}
|
|
135
|
+
function sanitizeLogText(text) {
|
|
136
|
+
return text.replace(CONTROL_CHARS, " ").replace(/\t/g, " ");
|
|
137
|
+
}
|
|
138
|
+
function buildLogTableLines(entries) {
|
|
139
|
+
if (entries.length === 0) return [];
|
|
140
|
+
const maxTextWidth = 100;
|
|
141
|
+
const rows = entries.map((e) => {
|
|
142
|
+
const text = sanitizeLogText(e.text);
|
|
143
|
+
return [
|
|
144
|
+
formatTime(e.time),
|
|
145
|
+
`#${e.id}`,
|
|
146
|
+
e.stream,
|
|
147
|
+
getDisplayWidth(text) > maxTextWidth ? truncateToWidth(text, maxTextWidth) : text
|
|
148
|
+
];
|
|
149
|
+
});
|
|
150
|
+
return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
|
|
128
151
|
}
|
|
152
|
+
var TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, CONTROL_CHARS;
|
|
129
153
|
var init_table = __esm({
|
|
130
154
|
"src/table.ts"() {
|
|
131
155
|
"use strict";
|
|
156
|
+
TASK_DISPLAY_LIMIT = 20;
|
|
157
|
+
LOG_DISPLAY_LIMIT = 20;
|
|
158
|
+
CONTROL_CHARS = /\x1b\[[0-9;?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
132
159
|
}
|
|
133
160
|
});
|
|
134
161
|
|
|
@@ -1843,6 +1870,7 @@ async function assertRemoteTrackingExists(epicBranch) {
|
|
|
1843
1870
|
// src/process-manager.ts
|
|
1844
1871
|
import { spawn } from "node:child_process";
|
|
1845
1872
|
import { basename } from "node:path";
|
|
1873
|
+
import { StringDecoder } from "node:string_decoder";
|
|
1846
1874
|
init_table();
|
|
1847
1875
|
|
|
1848
1876
|
// src/task-result.ts
|
|
@@ -2059,6 +2087,38 @@ var childProcesses = /* @__PURE__ */ new Map();
|
|
|
2059
2087
|
var herdrTasks = /* @__PURE__ */ new Map();
|
|
2060
2088
|
var herdrAbortSignal = { aborted: false };
|
|
2061
2089
|
var tasks = /* @__PURE__ */ new Map();
|
|
2090
|
+
var logLines = [];
|
|
2091
|
+
function pushLogLine(id, stream, text) {
|
|
2092
|
+
const trimmed = text.replace(/\r$/, "");
|
|
2093
|
+
if (trimmed.trim().length === 0) return;
|
|
2094
|
+
logLines.push({ id, stream, text: trimmed, time: /* @__PURE__ */ new Date() });
|
|
2095
|
+
if (logLines.length > LOG_DISPLAY_LIMIT) {
|
|
2096
|
+
logLines.splice(0, logLines.length - LOG_DISPLAY_LIMIT);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
function makeLogFeeder(id, stream) {
|
|
2100
|
+
let partial = "";
|
|
2101
|
+
const decoder = new StringDecoder("utf-8");
|
|
2102
|
+
return {
|
|
2103
|
+
feed(chunk) {
|
|
2104
|
+
partial += decoder.write(chunk);
|
|
2105
|
+
const parts = partial.split("\n");
|
|
2106
|
+
partial = parts.pop() ?? "";
|
|
2107
|
+
for (const part of parts) pushLogLine(id, stream, part);
|
|
2108
|
+
},
|
|
2109
|
+
flush() {
|
|
2110
|
+
partial += decoder.end();
|
|
2111
|
+
if (partial.length > 0) {
|
|
2112
|
+
pushLogLine(id, stream, partial);
|
|
2113
|
+
partial = "";
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
function pruneTaskHistory() {
|
|
2119
|
+
const finished = [...tasks.values()].filter((t) => t.status !== "running").sort((a, b) => (b.finishedAt ?? b.startedAt).getTime() - (a.finishedAt ?? a.startedAt).getTime());
|
|
2120
|
+
for (const t of finished.slice(TASK_DISPLAY_LIMIT)) tasks.delete(t.id);
|
|
2121
|
+
}
|
|
2062
2122
|
var shuttingDown = false;
|
|
2063
2123
|
function setShuttingDown() {
|
|
2064
2124
|
shuttingDown = true;
|
|
@@ -2088,8 +2148,14 @@ function isWorkerAtCapacity(workerName) {
|
|
|
2088
2148
|
return count >= getWorkerConfig(workerName).maxConcurrentTasks;
|
|
2089
2149
|
}
|
|
2090
2150
|
function renderTable() {
|
|
2091
|
-
const
|
|
2092
|
-
|
|
2151
|
+
const taskLines = buildTaskTableLines(selectRecentTasks([...tasks.values()]));
|
|
2152
|
+
const logTableLines = buildLogTableLines(logLines);
|
|
2153
|
+
if (taskLines.length === 0 && logTableLines.length === 0) return;
|
|
2154
|
+
const lines = [...taskLines];
|
|
2155
|
+
if (logTableLines.length > 0) {
|
|
2156
|
+
if (lines.length > 0) lines.push("");
|
|
2157
|
+
lines.push("Logs", ...logTableLines);
|
|
2158
|
+
}
|
|
2093
2159
|
console.clear();
|
|
2094
2160
|
console.log(lines.join("\n"));
|
|
2095
2161
|
}
|
|
@@ -2116,6 +2182,7 @@ async function finishTask(id, result, onComplete) {
|
|
|
2116
2182
|
task.finishedAt = /* @__PURE__ */ new Date();
|
|
2117
2183
|
task.agentStatus = void 0;
|
|
2118
2184
|
}
|
|
2185
|
+
pruneTaskHistory();
|
|
2119
2186
|
renderTable();
|
|
2120
2187
|
}
|
|
2121
2188
|
function resolveProjectName(cwd = process.cwd()) {
|
|
@@ -2159,6 +2226,7 @@ async function runViaHerdr(args, id, onComplete, cwd, env) {
|
|
|
2159
2226
|
herdrTasks.delete(id);
|
|
2160
2227
|
}
|
|
2161
2228
|
function run(command, args, id, title, workerName, path2, onComplete, cwd, env) {
|
|
2229
|
+
tasks.delete(id);
|
|
2162
2230
|
tasks.set(id, {
|
|
2163
2231
|
id,
|
|
2164
2232
|
title,
|
|
@@ -2180,9 +2248,12 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env)
|
|
|
2180
2248
|
...env ? { env: { ...process.env, ...env } } : {}
|
|
2181
2249
|
});
|
|
2182
2250
|
childProcesses.set(id, child);
|
|
2251
|
+
const stdoutFeeder = makeLogFeeder(id, "stdout");
|
|
2252
|
+
const stderrFeeder = makeLogFeeder(id, "stderr");
|
|
2183
2253
|
const outputChunks = [];
|
|
2184
2254
|
child.stdout?.on("data", (chunk) => {
|
|
2185
2255
|
outputChunks.push(chunk);
|
|
2256
|
+
stdoutFeeder.feed(chunk);
|
|
2186
2257
|
});
|
|
2187
2258
|
const stderrChunks = [];
|
|
2188
2259
|
let stderrLen = 0;
|
|
@@ -2193,8 +2264,11 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env)
|
|
|
2193
2264
|
stderrLen -= stderrChunks[0].length;
|
|
2194
2265
|
stderrChunks.shift();
|
|
2195
2266
|
}
|
|
2267
|
+
stderrFeeder.feed(chunk);
|
|
2196
2268
|
});
|
|
2197
2269
|
child.on("close", async (code) => {
|
|
2270
|
+
stdoutFeeder.flush();
|
|
2271
|
+
stderrFeeder.flush();
|
|
2198
2272
|
const result = buildTaskResult(
|
|
2199
2273
|
code,
|
|
2200
2274
|
Buffer.concat(outputChunks).toString("utf-8"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-task-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.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",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dev": "tsc --noEmit --watch",
|
|
13
13
|
"lint": "eslint .",
|
|
14
14
|
"lint:fix": "eslint . --fix",
|
|
15
|
-
"test": "node --experimental-strip-types --test \"src/**/*.test.ts\" \"plugin/scripts/**/*.test.mjs\"",
|
|
15
|
+
"test": "node --experimental-strip-types --import ./scripts/test-resolver.mjs --test \"src/**/*.test.ts\" \"plugin/scripts/**/*.test.mjs\"",
|
|
16
16
|
"format": "prettier --write .",
|
|
17
17
|
"format:check": "prettier --check .",
|
|
18
18
|
"version": "node scripts/sync-plugin-version.mjs && git add plugin/.claude-plugin/plugin.json",
|