claude-task-worker 0.28.0 → 0.29.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 +10 -1
- package/dist/index.js +40 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -312,7 +312,7 @@ claude-task-worker exec-issue --project app-a --epic 100 --label priority-high
|
|
|
312
312
|
|
|
313
313
|
`mode: "herdr"` のときの1タスクの流れ:
|
|
314
314
|
|
|
315
|
-
1. worktree を作成し、`ctw:<プロジェクト名>:#<Issue/PR番号>` ラベルのタブで claude を TUI
|
|
315
|
+
1. worktree を作成し、`ctw:<プロジェクト名>:#<Issue/PR番号>` ラベルのタブで claude を TUI 起動する
|
|
316
316
|
2. herdr が持つ agent ステータスを監視し、`working` → `idle` の遷移をタスク完了とみなす
|
|
317
317
|
3. 完了したらペインの内容を回収して通知に使い、タブを閉じてラベル・worktree を後片付けする
|
|
318
318
|
|
|
@@ -321,6 +321,15 @@ claude-task-worker exec-issue --project app-a --epic 100 --label priority-high
|
|
|
321
321
|
- タブは `--project` で起動した場合そのプロジェクトのワークスペース内に作られる(herdrが各ペインへ注入する `HERDR_WORKSPACE_ID` を利用する)
|
|
322
322
|
- `blocked`(claudeが入力待ち)になってもタスクは自動失敗にせず待機し続ける。ステータステーブルに `running:blocked` と表示されるので、herdrのタブを開いて直接対応できる
|
|
323
323
|
- `mode: "herdr"` でherdrが未インストール・未起動の場合、ワーカーは起動時にエラー終了する(`"default"` へ勝手にフォールバックしない)
|
|
324
|
+
- **タスク完了時の通知音はワーカー側から止められない**。herdr のエージェント状態遷移音を再生するのは herdr サーバープロセスで、`HERDR_DISABLE_SOUND` もそのプロセスの環境変数として読まれるため、タスクペインへ渡しても効かない(socket API にもペイン単位のミュートは無い)。無音にしたい場合は herdr 側の設定で行う:
|
|
325
|
+
|
|
326
|
+
```toml
|
|
327
|
+
# ~/.config/herdr/config.toml
|
|
328
|
+
[ui.sound]
|
|
329
|
+
enabled = false
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
適用は `herdr server reload-config`。この設定は herdr サーバー全体に効くため、ワーカー以外の対話セッションの完了音も鳴らなくなる(`[ui.sound.agents] claude = "off"` でも実質同じ範囲)。ワーカーだけを無音にしたい場合は、`HERDR_DISABLE_SOUND=1 herdr --session <name>` で別セッションを起動し、その中でディスパッチャーを動かす
|
|
324
333
|
|
|
325
334
|
### exec-issue
|
|
326
335
|
|
package/dist/index.js
CHANGED
|
@@ -75,6 +75,7 @@ __export(herdr_exports, {
|
|
|
75
75
|
tabRename: () => tabRename,
|
|
76
76
|
workspaceClose: () => workspaceClose,
|
|
77
77
|
workspaceCreate: () => workspaceCreate,
|
|
78
|
+
workspaceFocus: () => workspaceFocus,
|
|
78
79
|
workspaceList: () => workspaceList
|
|
79
80
|
});
|
|
80
81
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -210,12 +211,16 @@ async function workspaceList() {
|
|
|
210
211
|
}
|
|
211
212
|
return result.workspaces.map((workspace) => ({
|
|
212
213
|
workspaceId: workspace.workspace_id,
|
|
213
|
-
label: workspace.label ?? ""
|
|
214
|
+
label: workspace.label ?? "",
|
|
215
|
+
focused: workspace.focused === true
|
|
214
216
|
}));
|
|
215
217
|
}
|
|
216
218
|
async function workspaceClose(workspaceId) {
|
|
217
219
|
await execHerdr(["workspace", "close", workspaceId]);
|
|
218
220
|
}
|
|
221
|
+
async function workspaceFocus(workspaceId) {
|
|
222
|
+
await execHerdr(["workspace", "focus", workspaceId]);
|
|
223
|
+
}
|
|
219
224
|
async function agentStart({
|
|
220
225
|
name,
|
|
221
226
|
cwd,
|
|
@@ -585,6 +590,7 @@ __export(dispatcher_exports, {
|
|
|
585
590
|
WORKER_STARTUP_POLL_INTERVAL_MS: () => WORKER_STARTUP_POLL_INTERVAL_MS,
|
|
586
591
|
WORKER_STARTUP_TIMEOUT_MS: () => WORKER_STARTUP_TIMEOUT_MS,
|
|
587
592
|
createDispatcherShutdownHandler: () => createDispatcherShutdownHandler,
|
|
593
|
+
focusedWorkspaceId: () => focusedWorkspaceId,
|
|
588
594
|
formatUptime: () => formatUptime,
|
|
589
595
|
isShellProcess: () => isShellProcess,
|
|
590
596
|
isWorkerProcess: () => isWorkerProcess,
|
|
@@ -592,6 +598,7 @@ __export(dispatcher_exports, {
|
|
|
592
598
|
pollOnce: () => pollOnce,
|
|
593
599
|
removeSession: () => removeSession,
|
|
594
600
|
renderSessionTable: () => renderSessionTable,
|
|
601
|
+
restoreWorkspaceFocus: () => restoreWorkspaceFocus,
|
|
595
602
|
runDispatcher: () => runDispatcher,
|
|
596
603
|
shutdownDispatcher: () => shutdownDispatcher,
|
|
597
604
|
startWorkerInPane: () => startWorkerInPane,
|
|
@@ -608,6 +615,25 @@ async function loadTable() {
|
|
|
608
615
|
function workspaceLabelFor(projectName) {
|
|
609
616
|
return `${LABEL_PREFIX}${projectName}`;
|
|
610
617
|
}
|
|
618
|
+
async function focusedWorkspaceId(herdr) {
|
|
619
|
+
try {
|
|
620
|
+
const workspaces = await herdr.workspaceList();
|
|
621
|
+
return workspaces.find((workspace) => workspace.focused)?.workspaceId;
|
|
622
|
+
} catch (error) {
|
|
623
|
+
console.error(`[dispatcher] failed to read the focused workspace: ${error}`);
|
|
624
|
+
return void 0;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
async function restoreWorkspaceFocus(herdr, previousWorkspaceId, closingWorkspaceIds) {
|
|
628
|
+
if (!previousWorkspaceId || closingWorkspaceIds.has(previousWorkspaceId)) return;
|
|
629
|
+
const currentWorkspaceId = await focusedWorkspaceId(herdr);
|
|
630
|
+
if (currentWorkspaceId === previousWorkspaceId) return;
|
|
631
|
+
try {
|
|
632
|
+
await herdr.workspaceFocus(previousWorkspaceId);
|
|
633
|
+
} catch (error) {
|
|
634
|
+
console.error(`[dispatcher] failed to restore focus to workspace "${previousWorkspaceId}": ${error}`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
611
637
|
function sleep2(ms) {
|
|
612
638
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
613
639
|
}
|
|
@@ -719,23 +745,27 @@ async function runDispatcher(projects, forwardedCommand, timing) {
|
|
|
719
745
|
console.error(`[dispatcher] failed to dispatch project "${project.name}": ${error}`);
|
|
720
746
|
sessions.delete(project.name);
|
|
721
747
|
if (createdWorkspaceId !== void 0) {
|
|
748
|
+
const focusedBefore = await focusedWorkspaceId(herdr);
|
|
722
749
|
try {
|
|
723
750
|
await workspaceClose2(createdWorkspaceId);
|
|
724
751
|
} catch (closeError) {
|
|
725
752
|
console.error(`[dispatcher] failed to close dangling workspace for project "${project.name}": ${closeError}`);
|
|
726
753
|
}
|
|
754
|
+
await restoreWorkspaceFocus(herdr, focusedBefore, /* @__PURE__ */ new Set([createdWorkspaceId]));
|
|
727
755
|
}
|
|
728
756
|
}
|
|
729
757
|
}
|
|
730
758
|
return sessions;
|
|
731
759
|
}
|
|
732
|
-
async function removeSession(sessions, name, { closeWorkspace }) {
|
|
760
|
+
async function removeSession(sessions, name, { closeWorkspace }, herdrModule) {
|
|
733
761
|
const session = sessions.get(name);
|
|
734
762
|
if (!session) return;
|
|
735
763
|
sessions.delete(name);
|
|
736
764
|
if (closeWorkspace) {
|
|
737
|
-
const
|
|
738
|
-
await
|
|
765
|
+
const herdr = herdrModule ?? await loadHerdr2();
|
|
766
|
+
const focusedBefore = await focusedWorkspaceId(herdr);
|
|
767
|
+
await herdr.workspaceClose(session.workspaceId);
|
|
768
|
+
await restoreWorkspaceFocus(herdr, focusedBefore, /* @__PURE__ */ new Set([session.workspaceId]));
|
|
739
769
|
}
|
|
740
770
|
}
|
|
741
771
|
async function pollOnce(sessions, herdr) {
|
|
@@ -743,11 +773,11 @@ async function pollOnce(sessions, herdr) {
|
|
|
743
773
|
try {
|
|
744
774
|
const { foregroundProcesses } = await herdr.paneProcessInfo(session.paneId);
|
|
745
775
|
if (!foregroundProcesses.some(isWorkerProcess)) {
|
|
746
|
-
await removeSession(sessions, name, { closeWorkspace: true });
|
|
776
|
+
await removeSession(sessions, name, { closeWorkspace: true }, herdr);
|
|
747
777
|
}
|
|
748
778
|
} catch (error) {
|
|
749
779
|
if (error instanceof herdr.HerdrError && error.code === "pane_not_found") {
|
|
750
|
-
await removeSession(sessions, name, { closeWorkspace: false });
|
|
780
|
+
await removeSession(sessions, name, { closeWorkspace: false }, herdr);
|
|
751
781
|
continue;
|
|
752
782
|
}
|
|
753
783
|
console.error(`[dispatcher] failed to poll session "${name}": ${error}`);
|
|
@@ -845,6 +875,8 @@ async function waitUntilSessionsEmpty(sessions, herdr, pollIntervalMs, timeoutMs
|
|
|
845
875
|
return true;
|
|
846
876
|
}
|
|
847
877
|
async function closeRemainingWorkspaces(sessions, herdr, timeoutMs) {
|
|
878
|
+
const closingWorkspaceIds = new Set([...sessions.values()].map((session) => session.workspaceId));
|
|
879
|
+
const focusedBefore = await focusedWorkspaceId(herdr);
|
|
848
880
|
const results = await Promise.all(
|
|
849
881
|
[...sessions.values()].map(async (session) => {
|
|
850
882
|
try {
|
|
@@ -863,6 +895,7 @@ async function closeRemainingWorkspaces(sessions, herdr, timeoutMs) {
|
|
|
863
895
|
}
|
|
864
896
|
})
|
|
865
897
|
);
|
|
898
|
+
await restoreWorkspaceFocus(herdr, focusedBefore, closingWorkspaceIds);
|
|
866
899
|
return results.every((closed) => closed);
|
|
867
900
|
}
|
|
868
901
|
async function forceKillAllSessions(sessions, herdr, workspaceCloseTimeoutMs) {
|
|
@@ -1273,8 +1306,7 @@ function buildClaudeArgs({ mode, prompt, model, effort }) {
|
|
|
1273
1306
|
function buildClaudeEnv(mode) {
|
|
1274
1307
|
if (mode === "herdr") {
|
|
1275
1308
|
return {
|
|
1276
|
-
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS
|
|
1277
|
-
HERDR_DISABLE_SOUND: "1"
|
|
1309
|
+
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: CLAUDE_SPAWN_ENV.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS
|
|
1278
1310
|
};
|
|
1279
1311
|
}
|
|
1280
1312
|
return { ...CLAUDE_SPAWN_ENV };
|