claude-task-worker 0.115.0 → 0.117.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.
Files changed (64) hide show
  1. package/README.md +53 -7
  2. package/dist/chunk-3RY43CYR.js +41 -0
  3. package/dist/chunk-FAD5CNP4.js +3396 -0
  4. package/dist/chunk-N42YU3GJ.js +184 -0
  5. package/dist/claude-args.d.ts +62 -0
  6. package/dist/commands/cloud-setup.d.ts +14 -0
  7. package/dist/commands/codegraph.d.ts +39 -0
  8. package/dist/commands/design-md.d.ts +11 -0
  9. package/dist/commands/init.d.ts +17 -0
  10. package/dist/commands/install.d.ts +14 -0
  11. package/dist/commands/pen.d.ts +15 -0
  12. package/dist/commands/playwright.d.ts +27 -0
  13. package/dist/commands/run-command.d.ts +10 -0
  14. package/dist/commands/update.d.ts +1 -0
  15. package/dist/commands/version.d.ts +4 -0
  16. package/dist/config.d.ts +77 -0
  17. package/dist/custom-workers.d.ts +16 -0
  18. package/dist/dispatch-args.d.ts +13 -0
  19. package/dist/dispatcher-JKUMXTJ2.js +410 -0
  20. package/dist/dispatcher.d.ts +77 -0
  21. package/dist/gh.d.ts +112 -0
  22. package/dist/git.d.ts +7 -0
  23. package/dist/herdr-NDLUM6JF.js +309 -0
  24. package/dist/herdr-runner-33D6C2KI.js +307 -0
  25. package/dist/herdr-runner.d.ts +136 -0
  26. package/dist/herdr.d.ts +85 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +855 -4984
  29. package/dist/last-run-pr.d.ts +16 -0
  30. package/dist/lib.d.ts +20 -0
  31. package/dist/lib.js +123 -0
  32. package/dist/process-manager.d.ts +38 -0
  33. package/dist/random-name.d.ts +2 -0
  34. package/dist/run-command-O2TMRPZK.js +8 -0
  35. package/dist/runcat.d.ts +23 -0
  36. package/dist/slack.d.ts +46 -0
  37. package/dist/table-DWVMNDS3.js +32 -0
  38. package/dist/table.d.ts +55 -0
  39. package/dist/task-result.d.ts +25 -0
  40. package/dist/transcript.d.ts +27 -0
  41. package/dist/user-config.d.ts +30 -0
  42. package/dist/workers/answer-issue-questions.d.ts +1 -0
  43. package/dist/workers/apply-ui-design.d.ts +1 -0
  44. package/dist/workers/check-dependabot.d.ts +1 -0
  45. package/dist/workers/create-issue.d.ts +1 -0
  46. package/dist/workers/create-ui-design.d.ts +2 -0
  47. package/dist/workers/epic-issue.d.ts +2 -0
  48. package/dist/workers/exec-issue.d.ts +15 -0
  49. package/dist/workers/fix-review-point.d.ts +1 -0
  50. package/dist/workers/issue-worker.d.ts +18 -0
  51. package/dist/workers/pr-worker.d.ts +18 -0
  52. package/dist/workers/registry.d.ts +8 -0
  53. package/dist/workers/resolve-conflict.d.ts +2 -0
  54. package/dist/workers/scheduled-worker.d.ts +8 -0
  55. package/dist/workers/triage-created-issue.d.ts +1 -0
  56. package/dist/workers/triage-pr.d.ts +5 -0
  57. package/dist/workers/ui-design.d.ts +14 -0
  58. package/dist/workers/update-coding-guidelines.d.ts +1 -0
  59. package/dist/workers/update-design-md.d.ts +1 -0
  60. package/dist/workers/update-issue.d.ts +1 -0
  61. package/dist/workers/update-requirement-rules.d.ts +1 -0
  62. package/dist/workers/worker-definition.d.ts +18 -0
  63. package/dist/worktree.d.ts +21 -0
  64. package/package.json +14 -3
@@ -0,0 +1,410 @@
1
+ // src/dispatcher.ts
2
+ async function loadHerdr() {
3
+ return await import("./herdr-NDLUM6JF.js");
4
+ }
5
+ async function loadTable() {
6
+ return await import("./table-DWVMNDS3.js");
7
+ }
8
+ var { getDisplayWidth, truncateToWidth, padToWidth, buildLogTableLines, logLines, writeScreen } = await loadTable();
9
+ var POLL_INTERVAL_MS = 7 * 1e3;
10
+ var SHUTDOWN_TIMEOUT_MS = 10 * 60 * 1e3;
11
+ var PANE_READY_TIMEOUT_MS = 30 * 1e3;
12
+ var PANE_READY_POLL_INTERVAL_MS = 200;
13
+ var WORKER_STARTUP_TIMEOUT_MS = 30 * 1e3;
14
+ var WORKER_STARTUP_POLL_INTERVAL_MS = 500;
15
+ var SEND_MAX_ATTEMPTS = 3;
16
+ var LABEL_PREFIX = "ctw:";
17
+ function workspaceLabelFor(projectName) {
18
+ return `${LABEL_PREFIX}${projectName}`;
19
+ }
20
+ async function focusedWorkspaceId(herdr) {
21
+ try {
22
+ const workspaces = await herdr.workspaceList();
23
+ return workspaces.find((workspace) => workspace.focused)?.workspaceId;
24
+ } catch (error) {
25
+ console.error(`[dispatcher] failed to read the focused workspace: ${error}`);
26
+ return void 0;
27
+ }
28
+ }
29
+ async function restoreWorkspaceFocus(herdr, previousWorkspaceId, closingWorkspaceIds) {
30
+ if (!previousWorkspaceId || closingWorkspaceIds.has(previousWorkspaceId)) return;
31
+ const currentWorkspaceId = await focusedWorkspaceId(herdr);
32
+ if (currentWorkspaceId === previousWorkspaceId) return;
33
+ try {
34
+ await herdr.workspaceFocus(previousWorkspaceId);
35
+ } catch (error) {
36
+ console.error(`[dispatcher] failed to restore focus to workspace "${previousWorkspaceId}": ${error}`);
37
+ }
38
+ }
39
+ function sleep(ms) {
40
+ return new Promise((resolve) => setTimeout(resolve, ms));
41
+ }
42
+ function isWorkerProcess(process2) {
43
+ return process2.cmdline?.includes("claude-task-worker") ?? false;
44
+ }
45
+ var SHELL_NAME_PATTERN = /^-?(zsh|bash|sh)$/;
46
+ function isShellProcess(process2) {
47
+ return SHELL_NAME_PATTERN.test(process2.name ?? "") || SHELL_NAME_PATTERN.test(process2.cmdline ?? "");
48
+ }
49
+ async function waitForPaneReady(paneId, herdr, options) {
50
+ const timeoutMs = options?.timeoutMs ?? PANE_READY_TIMEOUT_MS;
51
+ const pollIntervalMs = options?.pollIntervalMs ?? PANE_READY_POLL_INTERVAL_MS;
52
+ const deadline = Date.now() + timeoutMs;
53
+ for (; ; ) {
54
+ const content = await herdr.paneRead(paneId, { source: "visible" });
55
+ if (content.trim() !== "") return true;
56
+ if (Date.now() >= deadline) return false;
57
+ await sleep(pollIntervalMs);
58
+ }
59
+ }
60
+ async function waitForWorkerStartup(paneId, herdr, options) {
61
+ const timeoutMs = options?.timeoutMs ?? WORKER_STARTUP_TIMEOUT_MS;
62
+ const pollIntervalMs = options?.pollIntervalMs ?? WORKER_STARTUP_POLL_INTERVAL_MS;
63
+ const deadline = Date.now() + timeoutMs;
64
+ for (; ; ) {
65
+ const { foregroundProcesses } = await herdr.paneProcessInfo(paneId);
66
+ if (foregroundProcesses.some(isWorkerProcess)) return "started";
67
+ if (foregroundProcesses.length > 0 && !foregroundProcesses.every(isShellProcess)) return "other";
68
+ if (Date.now() >= deadline) return "shell";
69
+ await sleep(pollIntervalMs);
70
+ }
71
+ }
72
+ async function startWorkerInPane(paneId, forwardedCommand, herdr, options) {
73
+ const maxAttempts = options?.sendMaxAttempts ?? SEND_MAX_ATTEMPTS;
74
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
75
+ const ready = await waitForPaneReady(paneId, herdr, {
76
+ timeoutMs: options?.paneReadyTimeoutMs,
77
+ pollIntervalMs: options?.paneReadyPollIntervalMs
78
+ });
79
+ if (!ready) {
80
+ console.warn(`[dispatcher] pane ${paneId} produced no prompt before the timeout, sending the command anyway`);
81
+ }
82
+ await herdr.paneSendText(paneId, forwardedCommand);
83
+ await herdr.paneSendKeys(paneId, "enter");
84
+ const result = await waitForWorkerStartup(paneId, herdr, {
85
+ timeoutMs: options?.workerStartupTimeoutMs,
86
+ pollIntervalMs: options?.workerStartupPollIntervalMs
87
+ });
88
+ if (result === "started") return true;
89
+ if (result === "other") {
90
+ console.warn(
91
+ `[dispatcher] pane ${paneId} foreground is neither the shell nor the worker, giving up without resending`
92
+ );
93
+ return false;
94
+ }
95
+ if (attempt < maxAttempts) {
96
+ console.warn(
97
+ `[dispatcher] worker did not start in pane ${paneId} (attempt ${attempt}/${maxAttempts}), resending the command`
98
+ );
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ async function runDispatcher(projects, forwardedCommand, timing) {
104
+ const herdr = await loadHerdr();
105
+ const { checkHerdrAvailable, workspaceCreate, workspaceClose, workspaceList, tabRename } = herdr;
106
+ try {
107
+ await checkHerdrAvailable();
108
+ } catch (error) {
109
+ const message = error instanceof Error ? error.message : String(error);
110
+ console.error(`[dispatcher] ${message}`);
111
+ throw error;
112
+ }
113
+ const existingWorkspaces = await workspaceList();
114
+ const existingLabels = new Set(existingWorkspaces.map((workspace) => workspace.label));
115
+ const sessions = /* @__PURE__ */ new Map();
116
+ for (const project of projects) {
117
+ const label = workspaceLabelFor(project.name);
118
+ if (existingLabels.has(label)) {
119
+ console.warn(`[dispatcher] project "${project.name}" already has a running workspace, skipping`);
120
+ continue;
121
+ }
122
+ let createdWorkspaceId;
123
+ try {
124
+ const { workspaceId, tabId, paneId } = await workspaceCreate({
125
+ label,
126
+ cwd: project.path,
127
+ env: { CTW_PROJECT_NAME: project.name }
128
+ });
129
+ createdWorkspaceId = workspaceId;
130
+ await tabRename(tabId, label).catch(
131
+ (error) => console.error(`[dispatcher] failed to rename the root tab for project "${project.name}": ${error}`)
132
+ );
133
+ sessions.set(project.name, {
134
+ name: project.name,
135
+ workspaceId,
136
+ tabId,
137
+ paneId,
138
+ startedAt: /* @__PURE__ */ new Date(),
139
+ status: "running"
140
+ });
141
+ const started = await startWorkerInPane(paneId, forwardedCommand, herdr, timing);
142
+ if (!started) {
143
+ throw new Error(
144
+ `worker did not start in pane ${paneId} after ${timing?.sendMaxAttempts ?? SEND_MAX_ATTEMPTS} attempt(s)`
145
+ );
146
+ }
147
+ } catch (error) {
148
+ console.error(`[dispatcher] failed to dispatch project "${project.name}": ${error}`);
149
+ sessions.delete(project.name);
150
+ if (createdWorkspaceId !== void 0) {
151
+ const focusedBefore = await focusedWorkspaceId(herdr);
152
+ try {
153
+ await workspaceClose(createdWorkspaceId);
154
+ } catch (closeError) {
155
+ console.error(`[dispatcher] failed to close dangling workspace for project "${project.name}": ${closeError}`);
156
+ }
157
+ await restoreWorkspaceFocus(herdr, focusedBefore, /* @__PURE__ */ new Set([createdWorkspaceId]));
158
+ }
159
+ }
160
+ }
161
+ return sessions;
162
+ }
163
+ async function removeSession(sessions, name, { closeWorkspace }, herdrModule) {
164
+ const session = sessions.get(name);
165
+ if (!session) return;
166
+ sessions.delete(name);
167
+ if (closeWorkspace) {
168
+ const herdr = herdrModule ?? await loadHerdr();
169
+ const focusedBefore = await focusedWorkspaceId(herdr);
170
+ await herdr.workspaceClose(session.workspaceId);
171
+ await restoreWorkspaceFocus(herdr, focusedBefore, /* @__PURE__ */ new Set([session.workspaceId]));
172
+ }
173
+ }
174
+ async function pollOnce(sessions, herdr) {
175
+ for (const [name, session] of [...sessions.entries()]) {
176
+ try {
177
+ const { foregroundProcesses } = await herdr.paneProcessInfo(session.paneId);
178
+ if (!foregroundProcesses.some(isWorkerProcess)) {
179
+ await removeSession(sessions, name, { closeWorkspace: true }, herdr);
180
+ }
181
+ } catch (error) {
182
+ if (error instanceof herdr.HerdrError && error.code === "pane_not_found") {
183
+ await removeSession(sessions, name, { closeWorkspace: false }, herdr);
184
+ continue;
185
+ }
186
+ console.error(`[dispatcher] failed to poll session "${name}": ${error}`);
187
+ }
188
+ }
189
+ }
190
+ function formatUptime(start, now) {
191
+ const totalSeconds = Math.max(0, Math.floor((now.getTime() - start.getTime()) / 1e3));
192
+ const hours = Math.floor(totalSeconds / 3600);
193
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
194
+ const seconds = totalSeconds % 60;
195
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
196
+ }
197
+ function renderSessionTable(sessions) {
198
+ const entries = [...sessions.values()];
199
+ const logTableLines = buildLogTableLines(logLines);
200
+ if (entries.length === 0) {
201
+ if (logTableLines.length > 0) writeScreen(["Logs", ...logTableLines]);
202
+ return;
203
+ }
204
+ const maxProjectWidth = 20;
205
+ const rows = entries.map((session) => ({
206
+ project: getDisplayWidth(session.name) > maxProjectWidth ? truncateToWidth(session.name, maxProjectWidth) : session.name,
207
+ workspace: session.workspaceId,
208
+ pane: session.paneId,
209
+ status: session.status,
210
+ uptime: formatUptime(session.startedAt, /* @__PURE__ */ new Date())
211
+ }));
212
+ const colWidths = {
213
+ project: Math.max(7, ...rows.map((r) => getDisplayWidth(r.project))),
214
+ workspace: Math.max(9, ...rows.map((r) => r.workspace.length)),
215
+ pane: Math.max(4, ...rows.map((r) => r.pane.length)),
216
+ status: Math.max(6, ...rows.map((r) => r.status.length)),
217
+ uptime: Math.max(6, ...rows.map((r) => r.uptime.length))
218
+ };
219
+ const cols = [colWidths.project, colWidths.workspace, colWidths.pane, colWidths.status, colWidths.uptime];
220
+ const line = (l, m, r, f) => `${l}${cols.map((w) => f.repeat(w + 2)).join(m)}${r}`;
221
+ const row = (project, workspace, pane, status, uptime) => `\u2502 ${padToWidth(project, colWidths.project)} \u2502 ${workspace.padEnd(colWidths.workspace)} \u2502 ${pane.padEnd(colWidths.pane)} \u2502 ${status.padEnd(colWidths.status)} \u2502 ${uptime.padEnd(colWidths.uptime)} \u2502`;
222
+ const lines = [];
223
+ lines.push(line("\u250C", "\u252C", "\u2510", "\u2500"));
224
+ lines.push(row("Project", "Workspace", "Pane", "Status", "Uptime"));
225
+ lines.push(line("\u251C", "\u253C", "\u2524", "\u2500"));
226
+ for (const r of rows) {
227
+ lines.push(row(r.project, r.workspace, r.pane, r.status, r.uptime));
228
+ }
229
+ lines.push(line("\u2514", "\u2534", "\u2518", "\u2500"));
230
+ if (logTableLines.length > 0) lines.push("", "Logs", ...logTableLines);
231
+ writeScreen(lines);
232
+ }
233
+ function monitorSessions(sessions, herdr, options) {
234
+ const pollIntervalMs = options?.pollIntervalMs ?? POLL_INTERVAL_MS;
235
+ const renderIntervalMs = options?.renderIntervalMs ?? 1e3;
236
+ let resolveDone;
237
+ const done = new Promise((resolve) => {
238
+ resolveDone = resolve;
239
+ });
240
+ let settled = false;
241
+ const finish = () => {
242
+ if (settled) return;
243
+ settled = true;
244
+ clearInterval(pollInterval);
245
+ clearInterval(renderInterval);
246
+ resolveDone();
247
+ };
248
+ const pollInterval = setInterval(() => {
249
+ void pollOnce(sessions, herdr).then(() => {
250
+ if (sessions.size === 0) {
251
+ finish();
252
+ }
253
+ });
254
+ }, pollIntervalMs);
255
+ const renderInterval = setInterval(() => {
256
+ renderSessionTable(sessions);
257
+ }, renderIntervalMs);
258
+ return {
259
+ stop: finish,
260
+ done
261
+ };
262
+ }
263
+ var SHUTDOWN_RETRY_TIMEOUT_MS = 90 * 1e3;
264
+ var CTRL_C_KEY = "ctrl+c";
265
+ async function sendCtrlCToAllSessions(sessions, herdr) {
266
+ await Promise.all(
267
+ [...sessions.values()].map(async (session) => {
268
+ try {
269
+ await herdr.paneSendKeys(session.paneId, CTRL_C_KEY);
270
+ } catch (error) {
271
+ console.error(`[dispatcher] failed to send ctrl-c to session "${session.name}": ${error}`);
272
+ }
273
+ })
274
+ );
275
+ }
276
+ async function waitUntilSessionsEmpty(sessions, herdr, pollIntervalMs, timeoutMs) {
277
+ const deadline = Date.now() + timeoutMs;
278
+ while (sessions.size > 0) {
279
+ await pollOnce(sessions, herdr);
280
+ if (sessions.size === 0) return true;
281
+ if (Date.now() >= deadline) return false;
282
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
283
+ }
284
+ return true;
285
+ }
286
+ async function closeRemainingWorkspaces(sessions, herdr, timeoutMs) {
287
+ const closingWorkspaceIds = new Set([...sessions.values()].map((session) => session.workspaceId));
288
+ const focusedBefore = await focusedWorkspaceId(herdr);
289
+ const results = await Promise.all(
290
+ [...sessions.values()].map(async (session) => {
291
+ try {
292
+ await Promise.race([
293
+ herdr.workspaceClose(session.workspaceId),
294
+ new Promise((_, reject) => {
295
+ setTimeout(() => reject(new Error("workspaceClose timed out")), timeoutMs).unref();
296
+ })
297
+ ]);
298
+ return true;
299
+ } catch (error) {
300
+ console.error(
301
+ `[dispatcher] failed to close workspace "${session.workspaceId}" for session "${session.name}" during shutdown: ${error}`
302
+ );
303
+ return false;
304
+ }
305
+ })
306
+ );
307
+ await restoreWorkspaceFocus(herdr, focusedBefore, closingWorkspaceIds);
308
+ return results.every((closed) => closed);
309
+ }
310
+ var shutdownPromise;
311
+ async function forceKillAllSessions(sessions, herdr, workspaceCloseTimeoutMs) {
312
+ console.log(`[dispatcher] force-kill requested, sending ctrl-c and closing ${sessions.size} session(s) immediately`);
313
+ await sendCtrlCToAllSessions(sessions, herdr);
314
+ await closeRemainingWorkspaces(sessions, herdr, workspaceCloseTimeoutMs);
315
+ process.exit(1);
316
+ }
317
+ async function shutdownDispatcher(sessions, monitorHandle, options) {
318
+ if (shutdownPromise) {
319
+ if (options?.forceKill) {
320
+ const herdr = options.herdr ?? await loadHerdr();
321
+ const workspaceCloseTimeoutMs = options.workspaceCloseTimeoutMs ?? 5e3;
322
+ await forceKillAllSessions(sessions, herdr, workspaceCloseTimeoutMs);
323
+ return;
324
+ }
325
+ console.log("[dispatcher] shutdown already in progress, ignoring duplicate request");
326
+ return shutdownPromise;
327
+ }
328
+ shutdownPromise = (async () => {
329
+ const herdr = options?.herdr ?? await loadHerdr();
330
+ const pollIntervalMs = options?.pollIntervalMs ?? POLL_INTERVAL_MS;
331
+ const shutdownTimeoutMs = options?.shutdownTimeoutMs ?? SHUTDOWN_TIMEOUT_MS;
332
+ const retryTimeoutMs = options?.retryTimeoutMs ?? SHUTDOWN_RETRY_TIMEOUT_MS;
333
+ const workspaceCloseTimeoutMs = options?.workspaceCloseTimeoutMs ?? 5e3;
334
+ if (monitorHandle) {
335
+ monitorHandle.stop();
336
+ await monitorHandle.done;
337
+ }
338
+ console.log(`[dispatcher] shutting down, sending ctrl-c to ${sessions.size} session(s)`);
339
+ await sendCtrlCToAllSessions(sessions, herdr);
340
+ let finishedInTime = await waitUntilSessionsEmpty(sessions, herdr, pollIntervalMs, shutdownTimeoutMs);
341
+ if (!finishedInTime) {
342
+ console.log(
343
+ `[dispatcher] ${sessions.size} session(s) still alive after ${shutdownTimeoutMs}ms, resending ctrl-c once`
344
+ );
345
+ await sendCtrlCToAllSessions(sessions, herdr);
346
+ finishedInTime = await waitUntilSessionsEmpty(sessions, herdr, pollIntervalMs, retryTimeoutMs);
347
+ }
348
+ const allWorkspacesClosed = await closeRemainingWorkspaces(sessions, herdr, workspaceCloseTimeoutMs);
349
+ process.exit(finishedInTime && allWorkspacesClosed ? 0 : 1);
350
+ })();
351
+ try {
352
+ await shutdownPromise;
353
+ } finally {
354
+ shutdownPromise = void 0;
355
+ }
356
+ }
357
+ function createDispatcherShutdownHandler(shutdown) {
358
+ let shuttingDown = false;
359
+ let forceKilling = false;
360
+ const handle = async () => {
361
+ if (shuttingDown) {
362
+ if (forceKilling) return;
363
+ forceKilling = true;
364
+ console.log("\n[dispatcher] Force killing sessions immediately...");
365
+ try {
366
+ await shutdown({ forceKill: true });
367
+ } catch (error) {
368
+ console.error(`[dispatcher] force-kill shutdown failed: ${error}`);
369
+ process.exit(1);
370
+ }
371
+ return;
372
+ }
373
+ shuttingDown = true;
374
+ console.log("\n[dispatcher] Stopping sessions. Waiting for them to finish... (Press Ctrl-C again to force kill)");
375
+ try {
376
+ await shutdown();
377
+ } catch (error) {
378
+ console.error(`[dispatcher] shutdown failed: ${error}`);
379
+ process.exit(1);
380
+ }
381
+ };
382
+ return { handle, isShuttingDown: () => shuttingDown };
383
+ }
384
+ export {
385
+ LABEL_PREFIX,
386
+ PANE_READY_POLL_INTERVAL_MS,
387
+ PANE_READY_TIMEOUT_MS,
388
+ POLL_INTERVAL_MS,
389
+ SEND_MAX_ATTEMPTS,
390
+ SHUTDOWN_RETRY_TIMEOUT_MS,
391
+ SHUTDOWN_TIMEOUT_MS,
392
+ WORKER_STARTUP_POLL_INTERVAL_MS,
393
+ WORKER_STARTUP_TIMEOUT_MS,
394
+ createDispatcherShutdownHandler,
395
+ focusedWorkspaceId,
396
+ formatUptime,
397
+ isShellProcess,
398
+ isWorkerProcess,
399
+ monitorSessions,
400
+ pollOnce,
401
+ removeSession,
402
+ renderSessionTable,
403
+ restoreWorkspaceFocus,
404
+ runDispatcher,
405
+ shutdownDispatcher,
406
+ startWorkerInPane,
407
+ waitForPaneReady,
408
+ waitForWorkerStartup,
409
+ workspaceLabelFor
410
+ };
@@ -0,0 +1,77 @@
1
+ import type * as HerdrModule from "./herdr";
2
+ import type { ResolvedProject } from "./user-config";
3
+ export declare const POLL_INTERVAL_MS: number;
4
+ export declare const SHUTDOWN_TIMEOUT_MS: number;
5
+ export declare const PANE_READY_TIMEOUT_MS: number;
6
+ export declare const PANE_READY_POLL_INTERVAL_MS = 200;
7
+ export declare const WORKER_STARTUP_TIMEOUT_MS: number;
8
+ export declare const WORKER_STARTUP_POLL_INTERVAL_MS = 500;
9
+ export declare const SEND_MAX_ATTEMPTS = 3;
10
+ export declare const LABEL_PREFIX = "ctw:";
11
+ export declare function workspaceLabelFor(projectName: string): string;
12
+ export declare function focusedWorkspaceId(herdr: typeof HerdrModule): Promise<string | undefined>;
13
+ export declare function restoreWorkspaceFocus(herdr: typeof HerdrModule, previousWorkspaceId: string | undefined, closingWorkspaceIds: Set<string>): Promise<void>;
14
+ export type WorkerSessionStatus = "running";
15
+ export interface WorkerSession {
16
+ name: string;
17
+ workspaceId: string;
18
+ tabId: string;
19
+ paneId: string;
20
+ startedAt: Date;
21
+ status: WorkerSessionStatus;
22
+ }
23
+ export type SessionRegistry = Map<string, WorkerSession>;
24
+ export interface DispatchTimingOptions {
25
+ paneReadyTimeoutMs?: number;
26
+ paneReadyPollIntervalMs?: number;
27
+ workerStartupTimeoutMs?: number;
28
+ workerStartupPollIntervalMs?: number;
29
+ sendMaxAttempts?: number;
30
+ }
31
+ export declare function isWorkerProcess(process: {
32
+ cmdline?: string;
33
+ }): boolean;
34
+ export declare function isShellProcess(process: {
35
+ name?: string;
36
+ cmdline?: string;
37
+ }): boolean;
38
+ export declare function waitForPaneReady(paneId: string, herdr: typeof HerdrModule, options?: {
39
+ timeoutMs?: number;
40
+ pollIntervalMs?: number;
41
+ }): Promise<boolean>;
42
+ export type WorkerStartupResult = "started" | "shell" | "other";
43
+ export declare function waitForWorkerStartup(paneId: string, herdr: typeof HerdrModule, options?: {
44
+ timeoutMs?: number;
45
+ pollIntervalMs?: number;
46
+ }): Promise<WorkerStartupResult>;
47
+ export declare function startWorkerInPane(paneId: string, forwardedCommand: string, herdr: typeof HerdrModule, options?: DispatchTimingOptions): Promise<boolean>;
48
+ export declare function runDispatcher(projects: ResolvedProject[], forwardedCommand: string, timing?: DispatchTimingOptions): Promise<SessionRegistry>;
49
+ export interface MonitorHandle {
50
+ stop(): void;
51
+ done: Promise<void>;
52
+ }
53
+ export declare function removeSession(sessions: SessionRegistry, name: string, { closeWorkspace }: {
54
+ closeWorkspace: boolean;
55
+ }, herdrModule?: typeof HerdrModule): Promise<void>;
56
+ export declare function pollOnce(sessions: SessionRegistry, herdr: typeof HerdrModule): Promise<void>;
57
+ export declare function formatUptime(start: Date, now: Date): string;
58
+ export declare function renderSessionTable(sessions: SessionRegistry): void;
59
+ export declare function monitorSessions(sessions: SessionRegistry, herdr: typeof HerdrModule, options?: {
60
+ pollIntervalMs?: number;
61
+ renderIntervalMs?: number;
62
+ }): MonitorHandle;
63
+ export declare const SHUTDOWN_RETRY_TIMEOUT_MS: number;
64
+ export interface ShutdownOptions {
65
+ herdr?: typeof HerdrModule;
66
+ pollIntervalMs?: number;
67
+ shutdownTimeoutMs?: number;
68
+ retryTimeoutMs?: number;
69
+ workspaceCloseTimeoutMs?: number;
70
+ forceKill?: boolean;
71
+ }
72
+ export declare function shutdownDispatcher(sessions: SessionRegistry, monitorHandle?: MonitorHandle, options?: ShutdownOptions): Promise<void>;
73
+ export interface DispatcherShutdownController {
74
+ handle: () => Promise<void>;
75
+ isShuttingDown: () => boolean;
76
+ }
77
+ export declare function createDispatcherShutdownHandler(shutdown: (options?: ShutdownOptions) => Promise<void>): DispatcherShutdownController;
package/dist/gh.d.ts ADDED
@@ -0,0 +1,112 @@
1
+ export interface Issue {
2
+ number: number;
3
+ title: string;
4
+ labels: {
5
+ name: string;
6
+ }[];
7
+ parent: {
8
+ number: number;
9
+ } | null;
10
+ }
11
+ export interface SubIssuesSummary {
12
+ total: number;
13
+ completed: number;
14
+ percentCompleted: number;
15
+ }
16
+ interface PullRequest {
17
+ number: number;
18
+ headRefName: string;
19
+ labels: {
20
+ name: string;
21
+ }[];
22
+ title: string;
23
+ }
24
+ interface RepoInfo {
25
+ owner: string;
26
+ name: string;
27
+ defaultBranch: string;
28
+ }
29
+ export declare function getCurrentUser(): Promise<string>;
30
+ export declare function getRepoInfo(): Promise<RepoInfo>;
31
+ export declare function listIssuesByLabel(assignee: string, labels: string[], excludeLabels?: string[], epicFilter?: {
32
+ owner: string;
33
+ repo: string;
34
+ numbers: number[];
35
+ }, limit?: number): Promise<Issue[]>;
36
+ export declare function listIssuesByNumbers(assignee: string, labels: string[], excludeLabels: string[], numbers: number[]): Promise<Issue[]>;
37
+ export declare function findOpenPrNumberByHeadRef(headRefName: string): Promise<number | null>;
38
+ export declare function findPrNumberByHeadRef(headRefName: string, state?: "open" | "all"): Promise<number | null>;
39
+ export interface PullRequestStateInfo {
40
+ number: number;
41
+ state: string;
42
+ mergedAt: string | null;
43
+ }
44
+ export declare function findPrStateByHeadRef(headRefName: string): Promise<PullRequestStateInfo | null>;
45
+ export declare function getPrMergeable(prNumber: number): Promise<string>;
46
+ export declare function getIssueBody(issueNumber: number): Promise<string>;
47
+ export declare function getIssueState(issueNumber: number): Promise<string>;
48
+ export interface ClosingPrRef {
49
+ number: number;
50
+ state: string;
51
+ headRefName: string;
52
+ baseRefName: string;
53
+ createdAt: string;
54
+ }
55
+ export declare function listPrsClosingIssue(issueNumber: number): Promise<ClosingPrRef[]>;
56
+ export declare function getPrDetail(prNumber: number): Promise<(ClosingPrRef & {
57
+ body: string;
58
+ }) | null>;
59
+ export declare function closeIssue(issueNumber: number, reason?: "completed" | "not_planned"): Promise<void>;
60
+ export declare function parseClosingIssueNumbers(body: string): number[];
61
+ export declare function bodyClosesIssue(body: string, issueNumber: number): boolean;
62
+ export declare function listPrsCrossReferencingIssue(issueNumber: number): Promise<ClosingPrRef[]>;
63
+ export declare function linkClosingPr(issueNumber: number, prNumber: number): Promise<void>;
64
+ export declare function findPrNumberClosingIssue(issueNumber: number, expectedHeadRefName: string): Promise<number | null>;
65
+ export declare function getIssueSubIssuesSummary(issueNumber: number): Promise<SubIssuesSummary>;
66
+ export interface PRCheck {
67
+ state: string;
68
+ }
69
+ export interface PullRequestWithChecks extends PullRequest {
70
+ checks: PRCheck[];
71
+ }
72
+ export declare function isCICompleted(checks: PRCheck[]): boolean;
73
+ export declare function listPullRequestsWithChecks(assignee?: string, label?: string, excludeLabels?: string[], limit?: number): Promise<PullRequestWithChecks[]>;
74
+ export declare function addLabel(type: "issue" | "pr", number: number, label: string): Promise<void>;
75
+ export declare function addAssignee(type: "issue" | "pr", number: number, login: string): Promise<void>;
76
+ export declare function hasLabel(type: "issue" | "pr", number: number, label: string): Promise<boolean>;
77
+ /**
78
+ * 指定時刻以降に投稿されたコメントのうち、見出しが一致する最新(配列末尾)の1件の本文を返す。
79
+ * クラウドタスクの完了検知(cc-cloud-done ラベル)後に、最終報告コメントを1回だけ回収する
80
+ * ために使う想定で、ポーリングでは呼ばない。
81
+ * `{owner}/{repo}` は `gh api` が解決するため getRepoInfo() は使わない。PR も issue と
82
+ * 番号空間・エンドポイント(`issues/<number>/comments`)を共有するため type 分岐は不要。
83
+ */
84
+ export declare function findCommentSince(number: number, since: Date, heading: string): Promise<string | null>;
85
+ /**
86
+ * 指定ラベルが付いた issue/PR 番号を列挙する。実行中のクラウドタスク全体を1クエリで
87
+ * 判定するための列挙であり、個別番号の `gh issue view` ポーリングにはしない。
88
+ * `--state all` にするのは、exec-issue の「コード変更なし」経路が Issue をクローズして
89
+ * からラベルを付けるため、既定の open 限定だと検知できないため。
90
+ */
91
+ export declare function listNumbersWithLabel(type: "issue" | "pr", label: string, limit?: number): Promise<number[]>;
92
+ /**
93
+ * Issue が Open な blockedBy(GitHub Issue Dependencies)を持つかを実体から判定する。
94
+ *
95
+ * listIssuesByLabel の `-is:blocked` は検索インデックス経由のため、
96
+ * (1) `gh issue create --blocked-by` が「Issue作成 → 依存付与」の2フェーズで動き
97
+ * ラベル付与から依存登録まで実測2〜3秒の隙間があること、
98
+ * (2) その後もインデックス反映に遅延があること、
99
+ * の2点で「まだブロック扱いになっていない」Issueを拾いうる。起動直前に本関数で
100
+ * 引き直すことで、検索インデックスを経由しない実体の状態で最終判定する。
101
+ */
102
+ export declare function hasOpenBlockers(issueNumber: number): Promise<boolean>;
103
+ export declare function removeLabel(type: "issue" | "pr", number: number, label: string): Promise<void>;
104
+ export declare function getLastIssueComment(issueNumber: number): Promise<{
105
+ author: string;
106
+ body: string;
107
+ } | null>;
108
+ export declare function commentOnPR(prNumber: number, body: string): Promise<void>;
109
+ export declare function commentOnIssue(issueNumber: number, body: string): Promise<void>;
110
+ export declare function createPullRequest(base: string, head: string, title: string, body: string): Promise<number>;
111
+ export declare function createLabel(name: string, color?: string, force?: boolean): Promise<boolean>;
112
+ export {};
package/dist/git.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare function syncDefaultBranch(branch: string): void;
2
+ /**
3
+ * epic ブランチの remote-tracking ref (refs/remotes/origin/<epicBranch>) を
4
+ * ローカルに用意する。remote に epic ブランチが無ければ defaultBranch から派生させて push する。
5
+ * 完了時点で origin/<epicBranch> が必ず解決できることを保証する(できなければ throw)。
6
+ */
7
+ export declare function ensureEpicBranch(epicBranch: string, defaultBranch: string): Promise<void>;