@automagik/genie 0.260202.1833 → 0.260202.1901
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/.beads/README.md +81 -0
- package/.beads/config.yaml +67 -0
- package/.beads/interactions.jsonl +0 -0
- package/.beads/issues.jsonl +0 -0
- package/.beads/metadata.json +4 -0
- package/.gitattributes +3 -0
- package/AGENTS.md +40 -0
- package/dist/claudio.js +1 -1
- package/dist/genie.js +1 -1
- package/dist/term.js +98 -97
- package/package.json +1 -1
- package/src/lib/beads-registry.ts +546 -0
- package/src/lib/tmux.ts +15 -1
- package/src/lib/version.ts +1 -1
- package/src/term-commands/close.ts +38 -3
- package/src/term-commands/daemon.ts +176 -0
- package/src/term-commands/kill.ts +45 -2
- package/src/term-commands/split.ts +8 -7
- package/src/term-commands/work.ts +86 -4
- package/src/term-commands/workers.ts +36 -2
- package/src/term.ts +39 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon command - Manage beads daemon
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* term daemon start - Start beads daemon (auto-commit, auto-sync)
|
|
6
|
+
* term daemon stop - Stop beads daemon
|
|
7
|
+
* term daemon status - Show daemon status
|
|
8
|
+
* term daemon restart - Restart daemon
|
|
9
|
+
*
|
|
10
|
+
* Options:
|
|
11
|
+
* --auto-commit - Enable auto-commit (default: true for start)
|
|
12
|
+
* --auto-push - Enable auto-push to remote
|
|
13
|
+
* --json - Output as JSON
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as beadsRegistry from '../lib/beads-registry.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface DaemonStartOptions {
|
|
23
|
+
autoCommit?: boolean;
|
|
24
|
+
autoPush?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DaemonStatusOptions {
|
|
28
|
+
json?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Commands
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Start the beads daemon
|
|
37
|
+
*/
|
|
38
|
+
export async function startCommand(options: DaemonStartOptions = {}): Promise<void> {
|
|
39
|
+
try {
|
|
40
|
+
// Check if already running
|
|
41
|
+
const status = await beadsRegistry.checkDaemonStatus();
|
|
42
|
+
if (status.running) {
|
|
43
|
+
console.log('ℹ️ Daemon is already running');
|
|
44
|
+
if (status.pid) {
|
|
45
|
+
console.log(` PID: ${status.pid}`);
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('🚀 Starting beads daemon...');
|
|
51
|
+
const started = await beadsRegistry.startDaemon({
|
|
52
|
+
autoCommit: options.autoCommit !== false, // Default to true
|
|
53
|
+
autoPush: options.autoPush,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (started) {
|
|
57
|
+
console.log(' ✅ Daemon started');
|
|
58
|
+
|
|
59
|
+
// Show updated status
|
|
60
|
+
const newStatus = await beadsRegistry.checkDaemonStatus();
|
|
61
|
+
if (newStatus.pid) {
|
|
62
|
+
console.log(` PID: ${newStatus.pid}`);
|
|
63
|
+
}
|
|
64
|
+
if (newStatus.autoCommit) {
|
|
65
|
+
console.log(' Auto-commit: enabled');
|
|
66
|
+
}
|
|
67
|
+
if (newStatus.autoPush) {
|
|
68
|
+
console.log(' Auto-push: enabled');
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
console.error('❌ Failed to start daemon');
|
|
72
|
+
console.log(' Check `bd daemon status` for details');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
} catch (error: any) {
|
|
76
|
+
console.error(`❌ Error: ${error.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Stop the beads daemon
|
|
83
|
+
*/
|
|
84
|
+
export async function stopCommand(): Promise<void> {
|
|
85
|
+
try {
|
|
86
|
+
// Check if running
|
|
87
|
+
const status = await beadsRegistry.checkDaemonStatus();
|
|
88
|
+
if (!status.running) {
|
|
89
|
+
console.log('ℹ️ Daemon is not running');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('🛑 Stopping beads daemon...');
|
|
94
|
+
const stopped = await beadsRegistry.stopDaemon();
|
|
95
|
+
|
|
96
|
+
if (stopped) {
|
|
97
|
+
console.log(' ✅ Daemon stopped');
|
|
98
|
+
} else {
|
|
99
|
+
console.error('❌ Failed to stop daemon');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
} catch (error: any) {
|
|
103
|
+
console.error(`❌ Error: ${error.message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Show daemon status
|
|
110
|
+
*/
|
|
111
|
+
export async function statusCommand(options: DaemonStatusOptions = {}): Promise<void> {
|
|
112
|
+
try {
|
|
113
|
+
const status = await beadsRegistry.checkDaemonStatus();
|
|
114
|
+
|
|
115
|
+
if (options.json) {
|
|
116
|
+
console.log(JSON.stringify(status, null, 2));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('Beads Daemon Status');
|
|
121
|
+
console.log('───────────────────');
|
|
122
|
+
console.log(`Running: ${status.running ? '✅ yes' : '❌ no'}`);
|
|
123
|
+
|
|
124
|
+
if (status.running) {
|
|
125
|
+
if (status.pid) {
|
|
126
|
+
console.log(`PID: ${status.pid}`);
|
|
127
|
+
}
|
|
128
|
+
if (status.lastSync) {
|
|
129
|
+
console.log(`Last sync: ${status.lastSync}`);
|
|
130
|
+
}
|
|
131
|
+
if (status.autoCommit !== undefined) {
|
|
132
|
+
console.log(`Auto-commit: ${status.autoCommit ? 'enabled' : 'disabled'}`);
|
|
133
|
+
}
|
|
134
|
+
if (status.autoPush !== undefined) {
|
|
135
|
+
console.log(`Auto-push: ${status.autoPush ? 'enabled' : 'disabled'}`);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
console.log('\nRun `term daemon start` to start the daemon');
|
|
139
|
+
}
|
|
140
|
+
} catch (error: any) {
|
|
141
|
+
console.error(`❌ Error: ${error.message}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Restart the beads daemon
|
|
148
|
+
*/
|
|
149
|
+
export async function restartCommand(options: DaemonStartOptions = {}): Promise<void> {
|
|
150
|
+
try {
|
|
151
|
+
// Check if running and stop
|
|
152
|
+
const status = await beadsRegistry.checkDaemonStatus();
|
|
153
|
+
if (status.running) {
|
|
154
|
+
console.log('🛑 Stopping beads daemon...');
|
|
155
|
+
await beadsRegistry.stopDaemon();
|
|
156
|
+
console.log(' ✅ Stopped');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Start with new options
|
|
160
|
+
console.log('🚀 Starting beads daemon...');
|
|
161
|
+
const started = await beadsRegistry.startDaemon({
|
|
162
|
+
autoCommit: options.autoCommit !== false,
|
|
163
|
+
autoPush: options.autoPush,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (started) {
|
|
167
|
+
console.log(' ✅ Daemon restarted');
|
|
168
|
+
} else {
|
|
169
|
+
console.error('❌ Failed to restart daemon');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
} catch (error: any) {
|
|
173
|
+
console.error(`❌ Error: ${error.message}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
import { confirm } from '@inquirer/prompts';
|
|
13
13
|
import * as tmux from '../lib/tmux.js';
|
|
14
14
|
import * as registry from '../lib/worker-registry.js';
|
|
15
|
+
import * as beadsRegistry from '../lib/beads-registry.js';
|
|
15
16
|
import { WorktreeManager } from '../lib/worktree.js';
|
|
16
17
|
import { join } from 'path';
|
|
17
18
|
import { homedir } from 'os';
|
|
18
19
|
|
|
20
|
+
// Use beads registry when enabled
|
|
21
|
+
const useBeads = beadsRegistry.isBeadsRegistryEnabled();
|
|
22
|
+
|
|
19
23
|
// ============================================================================
|
|
20
24
|
// Types
|
|
21
25
|
// ============================================================================
|
|
@@ -49,8 +53,22 @@ async function killWorkerPane(paneId: string): Promise<boolean> {
|
|
|
49
53
|
|
|
50
54
|
/**
|
|
51
55
|
* Remove worktree
|
|
56
|
+
* Uses bd worktree when beads registry is enabled
|
|
57
|
+
* Falls back to WorktreeManager otherwise
|
|
52
58
|
*/
|
|
53
59
|
async function removeWorktree(taskId: string, repoPath: string): Promise<boolean> {
|
|
60
|
+
// Try bd worktree first when beads is enabled
|
|
61
|
+
if (useBeads) {
|
|
62
|
+
try {
|
|
63
|
+
const removed = await beadsRegistry.removeWorktree(taskId);
|
|
64
|
+
if (removed) return true;
|
|
65
|
+
// Fall through to WorktreeManager if bd worktree fails
|
|
66
|
+
} catch {
|
|
67
|
+
// Fall through
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback to WorktreeManager
|
|
54
72
|
try {
|
|
55
73
|
const manager = new WorktreeManager({
|
|
56
74
|
baseDir: WORKTREE_BASE,
|
|
@@ -77,19 +95,32 @@ export async function killCommand(
|
|
|
77
95
|
options: KillOptions = {}
|
|
78
96
|
): Promise<void> {
|
|
79
97
|
try {
|
|
80
|
-
// Find worker by ID or pane
|
|
98
|
+
// Find worker by ID or pane (check both registries during transition)
|
|
81
99
|
let worker = await registry.get(target);
|
|
82
100
|
|
|
101
|
+
if (!worker && useBeads) {
|
|
102
|
+
// Try beads registry
|
|
103
|
+
worker = await beadsRegistry.getWorker(target);
|
|
104
|
+
}
|
|
105
|
+
|
|
83
106
|
if (!worker) {
|
|
84
107
|
// Try finding by pane ID
|
|
85
108
|
worker = await registry.findByPane(target);
|
|
86
109
|
}
|
|
87
110
|
|
|
111
|
+
if (!worker && useBeads) {
|
|
112
|
+
worker = await beadsRegistry.findByPane(target);
|
|
113
|
+
}
|
|
114
|
+
|
|
88
115
|
if (!worker) {
|
|
89
116
|
// Try finding by task ID
|
|
90
117
|
worker = await registry.findByTask(target);
|
|
91
118
|
}
|
|
92
119
|
|
|
120
|
+
if (!worker && useBeads) {
|
|
121
|
+
worker = await beadsRegistry.findByTask(target);
|
|
122
|
+
}
|
|
123
|
+
|
|
93
124
|
if (!worker) {
|
|
94
125
|
console.error(`❌ Worker "${target}" not found.`);
|
|
95
126
|
console.log(` Run \`term workers\` to see active workers.`);
|
|
@@ -127,7 +158,19 @@ export async function killCommand(
|
|
|
127
158
|
}
|
|
128
159
|
}
|
|
129
160
|
|
|
130
|
-
// 3. Unregister worker
|
|
161
|
+
// 3. Unregister worker from both registries
|
|
162
|
+
if (useBeads) {
|
|
163
|
+
try {
|
|
164
|
+
// Unbind work from agent
|
|
165
|
+
await beadsRegistry.unbindWork(worker.id);
|
|
166
|
+
// Set agent state to error (killed, not done)
|
|
167
|
+
await beadsRegistry.setAgentState(worker.id, 'error');
|
|
168
|
+
// Delete agent bead
|
|
169
|
+
await beadsRegistry.deleteAgent(worker.id);
|
|
170
|
+
} catch {
|
|
171
|
+
// Non-fatal if beads cleanup fails
|
|
172
|
+
}
|
|
173
|
+
}
|
|
131
174
|
await registry.unregister(worker.id);
|
|
132
175
|
console.log(` ✅ Worker unregistered`);
|
|
133
176
|
|
|
@@ -38,6 +38,9 @@ export async function splitSessionPane(
|
|
|
38
38
|
// Determine direction
|
|
39
39
|
const splitDirection = direction === 'h' ? 'horizontal' : 'vertical';
|
|
40
40
|
|
|
41
|
+
// Get source pane's current working directory
|
|
42
|
+
const sourcePath = await tmux.executeTmux(`display-message -p -t '${paneId}' '#{pane_current_path}'`);
|
|
43
|
+
|
|
41
44
|
// Handle workspace and worktree options
|
|
42
45
|
let workingDir: string | undefined;
|
|
43
46
|
|
|
@@ -62,20 +65,18 @@ export async function splitSessionPane(
|
|
|
62
65
|
workingDir = manager.getWorktreePath(options.worktree);
|
|
63
66
|
} else if (options.workspace) {
|
|
64
67
|
workingDir = options.workspace;
|
|
68
|
+
} else {
|
|
69
|
+
// Default to source pane's current directory
|
|
70
|
+
workingDir = sourcePath.trim() || undefined;
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
// Split pane
|
|
68
|
-
const newPane = await tmux.splitPane(paneId, splitDirection);
|
|
73
|
+
// Split pane with working directory (tmux -c flag handles this natively)
|
|
74
|
+
const newPane = await tmux.splitPane(paneId, splitDirection, undefined, workingDir);
|
|
69
75
|
if (!newPane) {
|
|
70
76
|
console.error('❌ Failed to split pane');
|
|
71
77
|
process.exit(1);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
// Change to working directory if specified
|
|
75
|
-
if (workingDir && newPane) {
|
|
76
|
-
await tmux.executeTmux(`send-keys -t '${newPane.id}' 'cd ${workingDir}' Enter`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
80
|
console.log(`✅ Pane split ${splitDirection}ly in session "${sessionName}"`);
|
|
80
81
|
if (workingDir) {
|
|
81
82
|
console.log(` Working directory: ${workingDir}`);
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
import { $ } from 'bun';
|
|
16
16
|
import * as tmux from '../lib/tmux.js';
|
|
17
17
|
import * as registry from '../lib/worker-registry.js';
|
|
18
|
+
import * as beadsRegistry from '../lib/beads-registry.js';
|
|
18
19
|
import { WorktreeManager } from '../lib/worktree.js';
|
|
19
20
|
import { EventMonitor, detectState } from '../lib/orchestrator/index.js';
|
|
20
21
|
import { join } from 'path';
|
|
21
22
|
import { homedir } from 'os';
|
|
22
23
|
|
|
24
|
+
// Use beads registry when enabled
|
|
25
|
+
const useBeads = beadsRegistry.isBeadsRegistryEnabled();
|
|
26
|
+
|
|
23
27
|
// ============================================================================
|
|
24
28
|
// Types
|
|
25
29
|
// ============================================================================
|
|
@@ -135,11 +139,36 @@ async function getCurrentSession(): Promise<string | null> {
|
|
|
135
139
|
|
|
136
140
|
/**
|
|
137
141
|
* Create worktree for worker
|
|
142
|
+
* Uses bd worktree when beads registry is enabled (auto .beads redirect)
|
|
143
|
+
* Falls back to WorktreeManager otherwise
|
|
138
144
|
*/
|
|
139
145
|
async function createWorktree(
|
|
140
146
|
taskId: string,
|
|
141
147
|
repoPath: string
|
|
142
148
|
): Promise<string | null> {
|
|
149
|
+
// Try bd worktree first when beads is enabled
|
|
150
|
+
if (useBeads) {
|
|
151
|
+
try {
|
|
152
|
+
// Check if worktree exists via beads
|
|
153
|
+
const existing = await beadsRegistry.getWorktree(taskId);
|
|
154
|
+
if (existing) {
|
|
155
|
+
console.log(`ℹ️ Worktree for ${taskId} already exists`);
|
|
156
|
+
return existing.path;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Create via bd worktree (includes .beads redirect)
|
|
160
|
+
const info = await beadsRegistry.createWorktree(taskId);
|
|
161
|
+
if (info) {
|
|
162
|
+
return info.path;
|
|
163
|
+
}
|
|
164
|
+
// Fall through to WorktreeManager if bd worktree fails
|
|
165
|
+
console.log(`⚠️ bd worktree failed, falling back to git worktree`);
|
|
166
|
+
} catch (error: any) {
|
|
167
|
+
console.log(`⚠️ bd worktree error: ${error.message}, falling back`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Fallback to WorktreeManager
|
|
143
172
|
try {
|
|
144
173
|
const manager = new WorktreeManager({
|
|
145
174
|
baseDir: WORKTREE_BASE,
|
|
@@ -210,6 +239,7 @@ async function spawnWorkerPane(
|
|
|
210
239
|
|
|
211
240
|
/**
|
|
212
241
|
* Start monitoring worker state and update registry
|
|
242
|
+
* Updates both beads and JSON registry during transition
|
|
213
243
|
*/
|
|
214
244
|
function startWorkerMonitoring(
|
|
215
245
|
workerId: string,
|
|
@@ -250,6 +280,10 @@ function startWorkerMonitoring(
|
|
|
250
280
|
}
|
|
251
281
|
|
|
252
282
|
try {
|
|
283
|
+
// Update both registries during transition
|
|
284
|
+
if (useBeads) {
|
|
285
|
+
await beadsRegistry.updateState(workerId, newState);
|
|
286
|
+
}
|
|
253
287
|
await registry.updateState(workerId, newState);
|
|
254
288
|
} catch {
|
|
255
289
|
// Ignore errors in background monitoring
|
|
@@ -258,6 +292,9 @@ function startWorkerMonitoring(
|
|
|
258
292
|
|
|
259
293
|
monitor.on('poll_error', () => {
|
|
260
294
|
// Pane may have been killed - unregister worker
|
|
295
|
+
if (useBeads) {
|
|
296
|
+
beadsRegistry.unregister(workerId).catch(() => {});
|
|
297
|
+
}
|
|
261
298
|
registry.unregister(workerId).catch(() => {});
|
|
262
299
|
monitor.stop();
|
|
263
300
|
});
|
|
@@ -282,6 +319,20 @@ export async function workCommand(
|
|
|
282
319
|
// Get current working directory as repo path
|
|
283
320
|
const repoPath = process.cwd();
|
|
284
321
|
|
|
322
|
+
// Ensure beads daemon is running for auto-sync
|
|
323
|
+
if (useBeads) {
|
|
324
|
+
const daemonStatus = await beadsRegistry.checkDaemonStatus();
|
|
325
|
+
if (!daemonStatus.running) {
|
|
326
|
+
console.log('🔄 Starting beads daemon for auto-sync...');
|
|
327
|
+
const started = await beadsRegistry.startDaemon({ autoCommit: true });
|
|
328
|
+
if (started) {
|
|
329
|
+
console.log(' ✅ Daemon started');
|
|
330
|
+
} else {
|
|
331
|
+
console.log(' ⚠️ Daemon failed to start (non-fatal)');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
285
336
|
// 1. Resolve target
|
|
286
337
|
let issue: BeadsIssue | null = null;
|
|
287
338
|
|
|
@@ -307,8 +358,13 @@ export async function workCommand(
|
|
|
307
358
|
|
|
308
359
|
const taskId = issue.id;
|
|
309
360
|
|
|
310
|
-
// 2. Check not already assigned
|
|
311
|
-
|
|
361
|
+
// 2. Check not already assigned (check both registries)
|
|
362
|
+
let existingWorker = useBeads
|
|
363
|
+
? await beadsRegistry.findByTask(taskId)
|
|
364
|
+
: null;
|
|
365
|
+
if (!existingWorker) {
|
|
366
|
+
existingWorker = await registry.findByTask(taskId);
|
|
367
|
+
}
|
|
312
368
|
if (existingWorker) {
|
|
313
369
|
console.error(`❌ ${taskId} already has a worker (pane ${existingWorker.paneId})`);
|
|
314
370
|
console.log(` Run \`term kill ${existingWorker.id}\` first, or work on a different issue.`);
|
|
@@ -354,7 +410,7 @@ export async function workCommand(
|
|
|
354
410
|
|
|
355
411
|
const { paneId } = paneResult;
|
|
356
412
|
|
|
357
|
-
// 7. Register worker
|
|
413
|
+
// 7. Register worker (write to both registries during transition)
|
|
358
414
|
const worker: registry.Worker = {
|
|
359
415
|
id: taskId,
|
|
360
416
|
paneId,
|
|
@@ -368,6 +424,29 @@ export async function workCommand(
|
|
|
368
424
|
repoPath,
|
|
369
425
|
};
|
|
370
426
|
|
|
427
|
+
// Register in beads (creates agent bead)
|
|
428
|
+
if (useBeads) {
|
|
429
|
+
try {
|
|
430
|
+
const agentId = await beadsRegistry.ensureAgent(taskId, {
|
|
431
|
+
paneId,
|
|
432
|
+
session,
|
|
433
|
+
worktree: worktreePath,
|
|
434
|
+
repoPath,
|
|
435
|
+
taskId,
|
|
436
|
+
taskTitle: issue.title,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Bind work to agent
|
|
440
|
+
await beadsRegistry.bindWork(taskId, taskId);
|
|
441
|
+
|
|
442
|
+
// Set initial state
|
|
443
|
+
await beadsRegistry.setAgentState(taskId, 'spawning');
|
|
444
|
+
} catch (error: any) {
|
|
445
|
+
console.log(`⚠️ Beads registration failed: ${error.message} (non-fatal)`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Also register in JSON registry (parallel operation during transition)
|
|
371
450
|
await registry.register(worker);
|
|
372
451
|
|
|
373
452
|
// 8. Start Claude in pane
|
|
@@ -385,7 +464,10 @@ When you're done, commit your changes and let me know.`;
|
|
|
385
464
|
|
|
386
465
|
await tmux.executeCommand(paneId, prompt, false, false);
|
|
387
466
|
|
|
388
|
-
// 10. Update state to working
|
|
467
|
+
// 10. Update state to working (both registries)
|
|
468
|
+
if (useBeads) {
|
|
469
|
+
await beadsRegistry.setAgentState(taskId, 'working').catch(() => {});
|
|
470
|
+
}
|
|
389
471
|
await registry.updateState(taskId, 'working');
|
|
390
472
|
|
|
391
473
|
// 11. Start monitoring
|
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
import { $ } from 'bun';
|
|
11
11
|
import * as tmux from '../lib/tmux.js';
|
|
12
12
|
import * as registry from '../lib/worker-registry.js';
|
|
13
|
+
import * as beadsRegistry from '../lib/beads-registry.js';
|
|
13
14
|
import { detectState, stripAnsi } from '../lib/orchestrator/index.js';
|
|
14
15
|
|
|
16
|
+
// Use beads registry when enabled
|
|
17
|
+
const useBeads = beadsRegistry.isBeadsRegistryEnabled();
|
|
18
|
+
|
|
15
19
|
// ============================================================================
|
|
16
20
|
// Types
|
|
17
21
|
// ============================================================================
|
|
@@ -153,7 +157,30 @@ function formatElapsed(startedAt: string): string {
|
|
|
153
157
|
|
|
154
158
|
export async function workersCommand(options: WorkersOptions = {}): Promise<void> {
|
|
155
159
|
try {
|
|
156
|
-
|
|
160
|
+
// Get workers from beads or JSON registry
|
|
161
|
+
// During transition, merge results from both
|
|
162
|
+
let workers: registry.Worker[] = [];
|
|
163
|
+
|
|
164
|
+
if (useBeads) {
|
|
165
|
+
try {
|
|
166
|
+
const beadsWorkers = await beadsRegistry.listWorkers();
|
|
167
|
+
workers = beadsWorkers;
|
|
168
|
+
} catch {
|
|
169
|
+
// Fallback to JSON registry
|
|
170
|
+
workers = await registry.list();
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
workers = await registry.list();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Also check JSON registry for any workers not in beads
|
|
177
|
+
const jsonWorkers = await registry.list();
|
|
178
|
+
const beadsIds = new Set(workers.map(w => w.id));
|
|
179
|
+
for (const jw of jsonWorkers) {
|
|
180
|
+
if (!beadsIds.has(jw.id)) {
|
|
181
|
+
workers.push(jw);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
157
184
|
|
|
158
185
|
// Gather display data for each worker
|
|
159
186
|
const displayData: WorkerDisplay[] = [];
|
|
@@ -166,10 +193,17 @@ export async function workersCommand(options: WorkersOptions = {}): Promise<void
|
|
|
166
193
|
// Get live state from pane
|
|
167
194
|
currentState = await getCurrentState(worker.paneId);
|
|
168
195
|
|
|
169
|
-
// Update
|
|
196
|
+
// Update both registries if state differs
|
|
170
197
|
const mappedState = mapDisplayStateToRegistry(currentState);
|
|
171
198
|
if (mappedState && mappedState !== worker.state) {
|
|
199
|
+
if (useBeads) {
|
|
200
|
+
// Update beads and send heartbeat
|
|
201
|
+
await beadsRegistry.updateState(worker.id, mappedState).catch(() => {});
|
|
202
|
+
}
|
|
172
203
|
await registry.updateState(worker.id, mappedState);
|
|
204
|
+
} else if (useBeads) {
|
|
205
|
+
// Just send heartbeat even if state unchanged
|
|
206
|
+
await beadsRegistry.heartbeat(worker.id).catch(() => {});
|
|
173
207
|
}
|
|
174
208
|
} else {
|
|
175
209
|
currentState = '💀 dead';
|
package/src/term.ts
CHANGED
|
@@ -20,6 +20,7 @@ import * as workCmd from './term-commands/work.js';
|
|
|
20
20
|
import * as workersCmd from './term-commands/workers.js';
|
|
21
21
|
import * as closeCmd from './term-commands/close.js';
|
|
22
22
|
import * as killCmd from './term-commands/kill.js';
|
|
23
|
+
import * as daemonCmd from './term-commands/daemon.js';
|
|
23
24
|
|
|
24
25
|
const program = new Command();
|
|
25
26
|
|
|
@@ -40,7 +41,8 @@ Worker Orchestration:
|
|
|
40
41
|
term work next - Work on next ready issue
|
|
41
42
|
term workers - List all workers and states
|
|
42
43
|
term close <bd-id> - Close issue, cleanup worker
|
|
43
|
-
term kill <worker> - Force kill a stuck worker
|
|
44
|
+
term kill <worker> - Force kill a stuck worker
|
|
45
|
+
term daemon start - Start beads daemon for auto-sync`)
|
|
44
46
|
.version(VERSION);
|
|
45
47
|
|
|
46
48
|
// Session management
|
|
@@ -297,6 +299,42 @@ program
|
|
|
297
299
|
});
|
|
298
300
|
});
|
|
299
301
|
|
|
302
|
+
// Daemon management (beads auto-sync)
|
|
303
|
+
const daemonProgram = program.command('daemon').description('Manage beads daemon for auto-sync');
|
|
304
|
+
|
|
305
|
+
daemonProgram
|
|
306
|
+
.command('start')
|
|
307
|
+
.description('Start beads daemon (auto-commit, auto-sync)')
|
|
308
|
+
.option('--no-auto-commit', 'Disable auto-commit')
|
|
309
|
+
.option('--auto-push', 'Enable auto-push to remote')
|
|
310
|
+
.action(async (options: daemonCmd.DaemonStartOptions) => {
|
|
311
|
+
await daemonCmd.startCommand(options);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
daemonProgram
|
|
315
|
+
.command('stop')
|
|
316
|
+
.description('Stop beads daemon')
|
|
317
|
+
.action(async () => {
|
|
318
|
+
await daemonCmd.stopCommand();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
daemonProgram
|
|
322
|
+
.command('status')
|
|
323
|
+
.description('Show daemon status')
|
|
324
|
+
.option('--json', 'Output as JSON')
|
|
325
|
+
.action(async (options: daemonCmd.DaemonStatusOptions) => {
|
|
326
|
+
await daemonCmd.statusCommand(options);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
daemonProgram
|
|
330
|
+
.command('restart')
|
|
331
|
+
.description('Restart beads daemon')
|
|
332
|
+
.option('--no-auto-commit', 'Disable auto-commit')
|
|
333
|
+
.option('--auto-push', 'Enable auto-push to remote')
|
|
334
|
+
.action(async (options: daemonCmd.DaemonStartOptions) => {
|
|
335
|
+
await daemonCmd.restartCommand(options);
|
|
336
|
+
});
|
|
337
|
+
|
|
300
338
|
// Orchestration commands (Claude Code automation)
|
|
301
339
|
const orcProgram = program.command('orc').description('Orchestrate Claude Code sessions');
|
|
302
340
|
|