@automagik/genie 0.260202.1833 → 0.260203.43
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 +9 -0
- package/.beads/metadata.json +4 -0
- package/.claude/skills/brainstorm/SKILL.md +53 -0
- package/.claude/skills/genie-base/SKILL.md +66 -0
- package/.claude/skills/genie-base/assets/workspace/AGENTS.md +191 -0
- package/.claude/skills/genie-base/assets/workspace/ENVIRONMENT.md +18 -0
- package/.claude/skills/genie-base/assets/workspace/HEARTBEAT.md +4 -0
- package/.claude/skills/genie-base/assets/workspace/IDENTITY.md +17 -0
- package/.claude/skills/genie-base/assets/workspace/MEMORY.md +16 -0
- package/.claude/skills/genie-base/assets/workspace/ROLE.md +14 -0
- package/.claude/skills/genie-base/assets/workspace/SOUL.md +36 -0
- package/.claude/skills/genie-base/assets/workspace/TOOLS.md +25 -0
- package/.claude/skills/genie-base/assets/workspace/USER.md +13 -0
- package/.claude/skills/genie-base/assets/workspace/memory/2026-01-30.md +6 -0
- package/.claude/skills/genie-base/assets/workspace/memory/2026-01-31.md +16 -0
- package/.claude/skills/genie-base/assets/workspace/memory/882c22be-9710-41c1-91f8-ed82947ef6ce.txt +1 -0
- package/.claude/skills/genie-base/scripts/install-workspace.sh +107 -0
- package/.claude/skills/genie-base/scripts/sanity-sweep.sh +60 -0
- package/.claude/skills/genie-blank-init/SKILL.md +37 -0
- package/.claude/skills/genie-blank-init/assets/BOOTSTRAP.md +44 -0
- package/.claude/skills/genie-blank-init/assets/IDENTITY.md +9 -0
- package/.claude/skills/genie-blank-init/assets/SOUL.md +10 -0
- package/.claude/skills/genie-blank-init/assets/USER.md +9 -0
- package/.claude/skills/genie-blank-init/scripts/apply-blank-init.sh +117 -0
- package/.claude/skills/genie-forge/SKILL.md +171 -0
- package/.claude/skills/genie-plan-review/CLAUDE.md +11 -0
- package/.claude/skills/genie-plan-review/SKILL.md +53 -0
- package/.claude/skills/genie-review/SKILL.md +171 -0
- package/.claude/skills/genie-wish/SKILL.md +141 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/.genie/.gitkeep +3 -0
- package/.genie/backlog/hooks-v2.md +82 -0
- package/.genie/wishes/upgrade-brainstorm-handoff/wish.md +124 -0
- package/.gitattributes +3 -0
- package/AGENTS.md +75 -0
- package/bun.lock +55 -0
- package/dist/claudio.js +1 -1
- package/dist/genie.js +1 -1
- package/dist/term.js +123 -99
- package/docs/CO-ORCHESTRATION-GUIDE.md +368 -0
- package/package.json +5 -1
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/README.md +120 -0
- package/plugin/agents/implementor.md +92 -0
- package/plugin/agents/quality-reviewer.md +113 -0
- package/plugin/agents/spec-reviewer.md +90 -0
- package/plugin/hooks/hooks.json +3 -0
- package/plugin/references/review-criteria.md +72 -0
- package/plugin/references/wish-template.md +92 -0
- package/plugin/scripts/genie.cjs +141 -0
- package/plugin/scripts/smart-install.js +308 -0
- package/plugin/scripts/src/install-genie-cli.sh +120 -0
- package/plugin/scripts/src/validate-completion.ts +142 -0
- package/plugin/scripts/src/validate-wish.ts +137 -0
- package/plugin/scripts/term.cjs +229 -0
- package/plugin/scripts/validate-completion.cjs +16 -0
- package/plugin/scripts/validate-wish.cjs +17 -0
- package/plugin/scripts/worker-service.cjs +28 -0
- package/plugin/skills/brainstorm/SKILL.md +106 -0
- package/plugin/skills/forge/SKILL.md +171 -0
- package/plugin/skills/genie-base/SKILL.md +99 -0
- package/plugin/skills/genie-base/assets/workspace/AGENTS.md +191 -0
- package/plugin/skills/genie-base/assets/workspace/ENVIRONMENT.md +18 -0
- package/plugin/skills/genie-base/assets/workspace/HEARTBEAT.md +4 -0
- package/plugin/skills/genie-base/assets/workspace/IDENTITY.md +17 -0
- package/plugin/skills/genie-base/assets/workspace/MEMORY.md +16 -0
- package/plugin/skills/genie-base/assets/workspace/ROLE.md +14 -0
- package/plugin/skills/genie-base/assets/workspace/SOUL.md +36 -0
- package/plugin/skills/genie-base/assets/workspace/TOOLS.md +25 -0
- package/plugin/skills/genie-base/assets/workspace/USER.md +13 -0
- package/plugin/skills/genie-base/scripts/install-workspace.sh +107 -0
- package/plugin/skills/genie-base/scripts/sanity-sweep.sh +60 -0
- package/plugin/skills/genie-blank-init/SKILL.md +73 -0
- package/plugin/skills/genie-blank-init/assets/BOOTSTRAP.md +44 -0
- package/plugin/skills/genie-blank-init/assets/IDENTITY.md +9 -0
- package/plugin/skills/genie-blank-init/assets/SOUL.md +10 -0
- package/plugin/skills/genie-blank-init/assets/USER.md +9 -0
- package/plugin/skills/genie-blank-init/scripts/apply-blank-init.sh +117 -0
- package/plugin/skills/genie-cli-dev/CLAUDE.md +19 -0
- package/plugin/skills/genie-cli-dev/SKILL.md +292 -0
- package/plugin/skills/plan-review/SKILL.md +101 -0
- package/plugin/skills/review/SKILL.md +221 -0
- package/plugin/skills/wish/SKILL.md +110 -0
- package/plugin/skills/work-orchestration/SKILL.md +110 -0
- package/scripts/build.js +132 -0
- package/scripts/smart-install.js +308 -0
- package/scripts/sync.js +134 -0
- package/src/lib/beads-registry.ts +595 -0
- package/src/lib/orchestrator/event-monitor.ts +2 -0
- package/src/lib/skill-loader.ts +215 -0
- package/src/lib/tmux.ts +30 -11
- package/src/lib/version.ts +1 -1
- package/src/lib/worker-registry.ts +10 -0
- package/src/services/worker-service.ts +351 -0
- package/src/term-commands/close.ts +48 -3
- package/src/term-commands/create.ts +95 -0
- package/src/term-commands/daemon.ts +176 -0
- package/src/term-commands/kill.ts +56 -2
- package/src/term-commands/orchestrate.ts +3 -2
- package/src/term-commands/send.ts +43 -15
- package/src/term-commands/spawn.ts +446 -0
- package/src/term-commands/split.ts +20 -8
- package/src/term-commands/work.ts +279 -37
- package/src/term-commands/workers.ts +36 -2
- package/src/term.ts +120 -7
|
@@ -15,10 +15,14 @@ import { $ } from 'bun';
|
|
|
15
15
|
import { confirm } from '@inquirer/prompts';
|
|
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 { join } from 'path';
|
|
20
21
|
import { homedir } from 'os';
|
|
21
22
|
|
|
23
|
+
// Use beads registry when enabled
|
|
24
|
+
const useBeads = beadsRegistry.isBeadsRegistryEnabled();
|
|
25
|
+
|
|
22
26
|
// ============================================================================
|
|
23
27
|
// Types
|
|
24
28
|
// ============================================================================
|
|
@@ -35,6 +39,8 @@ export interface CloseOptions {
|
|
|
35
39
|
// ============================================================================
|
|
36
40
|
|
|
37
41
|
const WORKTREE_BASE = join(homedir(), '.local', 'share', 'term', 'worktrees');
|
|
42
|
+
// Worktrees are created inside the project at .genie/worktrees/<taskId>
|
|
43
|
+
const WORKTREE_DIR_NAME = '.genie/worktrees';
|
|
38
44
|
|
|
39
45
|
// ============================================================================
|
|
40
46
|
// Helper Functions
|
|
@@ -101,8 +107,30 @@ async function mergeToMain(
|
|
|
101
107
|
|
|
102
108
|
/**
|
|
103
109
|
* Remove worktree
|
|
110
|
+
* Checks .genie/worktrees first, then bd worktree, then WorktreeManager
|
|
104
111
|
*/
|
|
105
112
|
async function removeWorktree(taskId: string, repoPath: string): Promise<boolean> {
|
|
113
|
+
// First, check .genie/worktrees location (new location)
|
|
114
|
+
const inProjectWorktree = join(repoPath, WORKTREE_DIR_NAME, taskId);
|
|
115
|
+
try {
|
|
116
|
+
await $`git -C ${repoPath} worktree remove ${inProjectWorktree} --force`.quiet();
|
|
117
|
+
return true;
|
|
118
|
+
} catch {
|
|
119
|
+
// Worktree may not exist at this location, continue checking
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Try bd worktree when beads is enabled
|
|
123
|
+
if (useBeads) {
|
|
124
|
+
try {
|
|
125
|
+
const removed = await beadsRegistry.removeWorktree(taskId);
|
|
126
|
+
if (removed) return true;
|
|
127
|
+
// Fall through to WorktreeManager if bd worktree fails
|
|
128
|
+
} catch {
|
|
129
|
+
// Fall through
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Fallback to WorktreeManager (legacy location)
|
|
106
134
|
try {
|
|
107
135
|
const manager = new WorktreeManager({
|
|
108
136
|
baseDir: WORKTREE_BASE,
|
|
@@ -141,8 +169,13 @@ export async function closeCommand(
|
|
|
141
169
|
options: CloseOptions = {}
|
|
142
170
|
): Promise<void> {
|
|
143
171
|
try {
|
|
144
|
-
// Find worker in registry
|
|
145
|
-
|
|
172
|
+
// Find worker in registry (check both during transition)
|
|
173
|
+
let worker = useBeads
|
|
174
|
+
? await beadsRegistry.findByTask(taskId)
|
|
175
|
+
: null;
|
|
176
|
+
if (!worker) {
|
|
177
|
+
worker = await registry.findByTask(taskId);
|
|
178
|
+
}
|
|
146
179
|
|
|
147
180
|
if (!worker) {
|
|
148
181
|
console.log(`ℹ️ No active worker for ${taskId}. Closing issue only.`);
|
|
@@ -207,7 +240,19 @@ export async function closeCommand(
|
|
|
207
240
|
await killWorkerPane(worker.paneId);
|
|
208
241
|
console.log(` ✅ Pane killed`);
|
|
209
242
|
|
|
210
|
-
// 5. Unregister worker
|
|
243
|
+
// 5. Unregister worker from both registries
|
|
244
|
+
if (useBeads) {
|
|
245
|
+
try {
|
|
246
|
+
// Unbind work from agent
|
|
247
|
+
await beadsRegistry.unbindWork(worker.id);
|
|
248
|
+
// Set agent state to done
|
|
249
|
+
await beadsRegistry.setAgentState(worker.id, 'done');
|
|
250
|
+
// Delete agent bead
|
|
251
|
+
await beadsRegistry.deleteAgent(worker.id);
|
|
252
|
+
} catch {
|
|
253
|
+
// Non-fatal if beads cleanup fails
|
|
254
|
+
}
|
|
255
|
+
}
|
|
211
256
|
await registry.unregister(worker.id);
|
|
212
257
|
console.log(` ✅ Worker unregistered`);
|
|
213
258
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create command - Simple bd create wrapper
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* term create "Task title" - Create beads issue
|
|
6
|
+
* term create "Task title" -d "Description" - With description
|
|
7
|
+
* term create "Task title" -p bd-1 - With parent/dependency
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* -d, --description <text> - Issue description
|
|
11
|
+
* -p, --parent <id> - Parent issue ID (creates dependency)
|
|
12
|
+
* --json - Output as JSON
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { $ } from 'bun';
|
|
16
|
+
|
|
17
|
+
export interface CreateOptions {
|
|
18
|
+
description?: string;
|
|
19
|
+
parent?: string;
|
|
20
|
+
json?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Run bd command and return result
|
|
25
|
+
*/
|
|
26
|
+
async function runBd(args: string[]): Promise<{ stdout: string; exitCode: number }> {
|
|
27
|
+
try {
|
|
28
|
+
const result = await $`bd ${args}`.quiet();
|
|
29
|
+
return { stdout: result.stdout.toString().trim(), exitCode: 0 };
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
return {
|
|
32
|
+
stdout: error.stdout?.toString().trim() || error.message,
|
|
33
|
+
exitCode: error.exitCode || 1
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function createCommand(
|
|
39
|
+
title: string,
|
|
40
|
+
options: CreateOptions = {}
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
// Build bd create command
|
|
43
|
+
const args = ['create', title];
|
|
44
|
+
|
|
45
|
+
if (options.description) {
|
|
46
|
+
args.push('--description', options.description);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Run bd create
|
|
50
|
+
const { stdout, exitCode } = await runBd(args);
|
|
51
|
+
|
|
52
|
+
if (exitCode !== 0) {
|
|
53
|
+
console.error(`Failed to create issue: ${stdout}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Extract issue ID from output
|
|
58
|
+
// bd create typically outputs something like "Created bd-123" or just the ID
|
|
59
|
+
const idMatch = stdout.match(/bd-\d+/);
|
|
60
|
+
const issueId = idMatch ? idMatch[0] : null;
|
|
61
|
+
|
|
62
|
+
if (!issueId) {
|
|
63
|
+
console.log(stdout);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// If parent specified, add blockedBy relationship
|
|
68
|
+
if (options.parent) {
|
|
69
|
+
const { exitCode: updateExit } = await runBd([
|
|
70
|
+
'update',
|
|
71
|
+
issueId,
|
|
72
|
+
'--blocked-by',
|
|
73
|
+
options.parent,
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
if (updateExit !== 0) {
|
|
77
|
+
console.log(`Created ${issueId} (failed to set parent dependency)`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (options.json) {
|
|
82
|
+
// Fetch full issue details
|
|
83
|
+
const { stdout: showOutput } = await runBd(['show', issueId, '--json']);
|
|
84
|
+
console.log(showOutput);
|
|
85
|
+
} else {
|
|
86
|
+
console.log(`Created: ${issueId} - "${title}"`);
|
|
87
|
+
if (options.parent) {
|
|
88
|
+
console.log(` Blocked by: ${options.parent}`);
|
|
89
|
+
}
|
|
90
|
+
console.log(`\nNext steps:`);
|
|
91
|
+
console.log(` term work ${issueId} - Start working on it`);
|
|
92
|
+
console.log(` term spawn genie:wish -t ${issueId} - Plan with wish skill`);
|
|
93
|
+
console.log(` bd show ${issueId} - View details`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
* --keep-worktree - Don't remove the worktree
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { $ } from 'bun';
|
|
12
13
|
import { confirm } from '@inquirer/prompts';
|
|
13
14
|
import * as tmux from '../lib/tmux.js';
|
|
14
15
|
import * as registry from '../lib/worker-registry.js';
|
|
16
|
+
import * as beadsRegistry from '../lib/beads-registry.js';
|
|
15
17
|
import { WorktreeManager } from '../lib/worktree.js';
|
|
16
18
|
import { join } from 'path';
|
|
17
19
|
import { homedir } from 'os';
|
|
18
20
|
|
|
21
|
+
// Use beads registry when enabled
|
|
22
|
+
const useBeads = beadsRegistry.isBeadsRegistryEnabled();
|
|
23
|
+
|
|
19
24
|
// ============================================================================
|
|
20
25
|
// Types
|
|
21
26
|
// ============================================================================
|
|
@@ -30,6 +35,8 @@ export interface KillOptions {
|
|
|
30
35
|
// ============================================================================
|
|
31
36
|
|
|
32
37
|
const WORKTREE_BASE = join(homedir(), '.local', 'share', 'term', 'worktrees');
|
|
38
|
+
// Worktrees are created inside the project at .genie/worktrees/<taskId>
|
|
39
|
+
const WORKTREE_DIR_NAME = '.genie/worktrees';
|
|
33
40
|
|
|
34
41
|
// ============================================================================
|
|
35
42
|
// Helper Functions
|
|
@@ -49,8 +56,30 @@ async function killWorkerPane(paneId: string): Promise<boolean> {
|
|
|
49
56
|
|
|
50
57
|
/**
|
|
51
58
|
* Remove worktree
|
|
59
|
+
* Checks .genie/worktrees first, then bd worktree, then WorktreeManager
|
|
52
60
|
*/
|
|
53
61
|
async function removeWorktree(taskId: string, repoPath: string): Promise<boolean> {
|
|
62
|
+
// First, check .genie/worktrees location (new location)
|
|
63
|
+
const inProjectWorktree = join(repoPath, WORKTREE_DIR_NAME, taskId);
|
|
64
|
+
try {
|
|
65
|
+
await $`git -C ${repoPath} worktree remove ${inProjectWorktree} --force`.quiet();
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
// Worktree may not exist at this location, continue checking
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Try bd worktree when beads is enabled
|
|
72
|
+
if (useBeads) {
|
|
73
|
+
try {
|
|
74
|
+
const removed = await beadsRegistry.removeWorktree(taskId);
|
|
75
|
+
if (removed) return true;
|
|
76
|
+
// Fall through to WorktreeManager if bd worktree fails
|
|
77
|
+
} catch {
|
|
78
|
+
// Fall through
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback to WorktreeManager (legacy location)
|
|
54
83
|
try {
|
|
55
84
|
const manager = new WorktreeManager({
|
|
56
85
|
baseDir: WORKTREE_BASE,
|
|
@@ -77,19 +106,32 @@ export async function killCommand(
|
|
|
77
106
|
options: KillOptions = {}
|
|
78
107
|
): Promise<void> {
|
|
79
108
|
try {
|
|
80
|
-
// Find worker by ID or pane
|
|
109
|
+
// Find worker by ID or pane (check both registries during transition)
|
|
81
110
|
let worker = await registry.get(target);
|
|
82
111
|
|
|
112
|
+
if (!worker && useBeads) {
|
|
113
|
+
// Try beads registry
|
|
114
|
+
worker = await beadsRegistry.getWorker(target);
|
|
115
|
+
}
|
|
116
|
+
|
|
83
117
|
if (!worker) {
|
|
84
118
|
// Try finding by pane ID
|
|
85
119
|
worker = await registry.findByPane(target);
|
|
86
120
|
}
|
|
87
121
|
|
|
122
|
+
if (!worker && useBeads) {
|
|
123
|
+
worker = await beadsRegistry.findByPane(target);
|
|
124
|
+
}
|
|
125
|
+
|
|
88
126
|
if (!worker) {
|
|
89
127
|
// Try finding by task ID
|
|
90
128
|
worker = await registry.findByTask(target);
|
|
91
129
|
}
|
|
92
130
|
|
|
131
|
+
if (!worker && useBeads) {
|
|
132
|
+
worker = await beadsRegistry.findByTask(target);
|
|
133
|
+
}
|
|
134
|
+
|
|
93
135
|
if (!worker) {
|
|
94
136
|
console.error(`❌ Worker "${target}" not found.`);
|
|
95
137
|
console.log(` Run \`term workers\` to see active workers.`);
|
|
@@ -127,7 +169,19 @@ export async function killCommand(
|
|
|
127
169
|
}
|
|
128
170
|
}
|
|
129
171
|
|
|
130
|
-
// 3. Unregister worker
|
|
172
|
+
// 3. Unregister worker from both registries
|
|
173
|
+
if (useBeads) {
|
|
174
|
+
try {
|
|
175
|
+
// Unbind work from agent
|
|
176
|
+
await beadsRegistry.unbindWork(worker.id);
|
|
177
|
+
// Set agent state to error (killed, not done)
|
|
178
|
+
await beadsRegistry.setAgentState(worker.id, 'error');
|
|
179
|
+
// Delete agent bead
|
|
180
|
+
await beadsRegistry.deleteAgent(worker.id);
|
|
181
|
+
} catch {
|
|
182
|
+
// Non-fatal if beads cleanup fails
|
|
183
|
+
}
|
|
184
|
+
}
|
|
131
185
|
await registry.unregister(worker.id);
|
|
132
186
|
console.log(` ✅ Worker unregistered`);
|
|
133
187
|
|
|
@@ -243,8 +243,9 @@ export async function sendMessage(
|
|
|
243
243
|
try {
|
|
244
244
|
const { paneId } = await getSessionPane(sessionName, options.pane);
|
|
245
245
|
|
|
246
|
-
// Send the message
|
|
247
|
-
|
|
246
|
+
// Send the message cleanly (no TMUX_MCP markers)
|
|
247
|
+
const escapedMessage = message.replace(/'/g, "'\\''");
|
|
248
|
+
await tmux.executeTmux(`send-keys -t '${paneId}' '${escapedMessage}' Enter`);
|
|
248
249
|
|
|
249
250
|
if (options.noWait) {
|
|
250
251
|
console.log(`✅ Message sent to session "${sessionName}"`);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import * as tmux from '../lib/tmux.js';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export interface SendOptions {
|
|
4
|
+
enter?: boolean;
|
|
5
|
+
pane?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function sendKeysToSession(
|
|
9
|
+
sessionName: string,
|
|
10
|
+
keys: string,
|
|
11
|
+
options: SendOptions = {}
|
|
12
|
+
): Promise<void> {
|
|
4
13
|
try {
|
|
5
14
|
// Find session
|
|
6
15
|
const session = await tmux.findSessionByName(sessionName);
|
|
@@ -9,24 +18,43 @@ export async function sendKeysToSession(sessionName: string, keys: string): Prom
|
|
|
9
18
|
process.exit(1);
|
|
10
19
|
}
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
const windows = await tmux.listWindows(session.id);
|
|
14
|
-
if (!windows || windows.length === 0) {
|
|
15
|
-
console.error(`❌ No windows found in session "${sessionName}"`);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
21
|
+
let paneId: string;
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
// Use specified pane or find first pane
|
|
24
|
+
if (options.pane) {
|
|
25
|
+
paneId = options.pane.startsWith('%') ? options.pane : `%${options.pane}`;
|
|
26
|
+
} else {
|
|
27
|
+
// Get first window and pane
|
|
28
|
+
const windows = await tmux.listWindows(session.id);
|
|
29
|
+
if (!windows || windows.length === 0) {
|
|
30
|
+
console.error(`❌ No windows found in session "${sessionName}"`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const panes = await tmux.listPanes(windows[0].id);
|
|
35
|
+
if (!panes || panes.length === 0) {
|
|
36
|
+
console.error(`❌ No panes found in session "${sessionName}"`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
paneId = panes[0].id;
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
// Default: enter is true (append Enter key)
|
|
44
|
+
const withEnter = options.enter !== false;
|
|
45
|
+
|
|
46
|
+
// Escape single quotes for shell
|
|
47
|
+
const escapedKeys = keys.replace(/'/g, "'\\''");
|
|
48
|
+
|
|
49
|
+
if (withEnter) {
|
|
50
|
+
// Send keys with Enter appended
|
|
51
|
+
await tmux.executeTmux(`send-keys -t '${paneId}' '${escapedKeys}' Enter`);
|
|
52
|
+
} else {
|
|
53
|
+
// Send raw keys without Enter
|
|
54
|
+
await tmux.executeTmux(`send-keys -t '${paneId}' '${escapedKeys}'`);
|
|
55
|
+
}
|
|
26
56
|
|
|
27
|
-
|
|
28
|
-
await tmux.executeCommand(paneId, keys, false, true);
|
|
29
|
-
console.log(`✅ Keys sent to session "${sessionName}"`);
|
|
57
|
+
console.log(`✅ Keys sent to session "${sessionName}"${withEnter ? ' (with Enter)' : ''}`);
|
|
30
58
|
} catch (error: any) {
|
|
31
59
|
console.error(`❌ Error sending keys: ${error.message}`);
|
|
32
60
|
process.exit(1);
|