s9n-devops-agent 1.0.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.
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Agent Command Monitor
3
+ * Watches for special command files to trigger agent actions
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { execSync } = require('child_process');
9
+
10
+ class AgentCommandMonitor {
11
+ constructor(sessionId, worktreePath) {
12
+ this.sessionId = sessionId;
13
+ this.worktreePath = worktreePath;
14
+ this.commandFile = path.join(worktreePath, `.devops-command-${sessionId}`);
15
+ this.isClosing = false;
16
+ }
17
+
18
+ /**
19
+ * Start monitoring for commands
20
+ */
21
+ startMonitoring() {
22
+ // Check for command file every 5 seconds
23
+ this.interval = setInterval(() => {
24
+ this.checkForCommands();
25
+ }, 5000);
26
+
27
+ console.log('[agent-commands] Monitoring for special commands...');
28
+ console.log(`[agent-commands] To close session, create: ${path.basename(this.commandFile)} with "CLOSE_SESSION"`);
29
+ }
30
+
31
+ /**
32
+ * Check if command file exists and process it
33
+ */
34
+ checkForCommands() {
35
+ if (this.isClosing) return;
36
+
37
+ if (fs.existsSync(this.commandFile)) {
38
+ try {
39
+ const command = fs.readFileSync(this.commandFile, 'utf8').trim();
40
+
41
+ // Remove the command file immediately
42
+ fs.unlinkSync(this.commandFile);
43
+
44
+ // Process the command
45
+ this.processCommand(command);
46
+ } catch (error) {
47
+ console.error('[agent-commands] Error reading command file:', error.message);
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Process a command
54
+ */
55
+ processCommand(command) {
56
+ console.log(`[agent-commands] Received command: ${command}`);
57
+
58
+ switch (command.toUpperCase()) {
59
+ case 'CLOSE_SESSION':
60
+ case 'EXIT':
61
+ case 'QUIT':
62
+ this.handleCloseSession();
63
+ break;
64
+
65
+ case 'STATUS':
66
+ this.handleStatus();
67
+ break;
68
+
69
+ case 'PUSH':
70
+ this.handlePush();
71
+ break;
72
+
73
+ default:
74
+ console.log(`[agent-commands] Unknown command: ${command}`);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Handle close session command
80
+ */
81
+ handleCloseSession() {
82
+ if (this.isClosing) return;
83
+ this.isClosing = true;
84
+
85
+ console.log('[agent-commands] Initiating session cleanup...');
86
+
87
+ // Stop monitoring
88
+ if (this.interval) {
89
+ clearInterval(this.interval);
90
+ }
91
+
92
+ try {
93
+ // 1. Check for uncommitted changes
94
+ const status = execSync(`git status --porcelain`, {
95
+ cwd: this.worktreePath,
96
+ encoding: 'utf8'
97
+ }).trim();
98
+
99
+ if (status) {
100
+ console.log('[agent-commands] Committing final changes...');
101
+ execSync(`git add -A`, { cwd: this.worktreePath });
102
+ execSync(`git commit -m "chore: session cleanup - final commit"`, {
103
+ cwd: this.worktreePath,
104
+ stdio: 'pipe'
105
+ });
106
+ }
107
+
108
+ // 2. Push any unpushed commits
109
+ console.log('[agent-commands] Pushing changes...');
110
+ const branch = execSync(`git rev-parse --abbrev-ref HEAD`, {
111
+ cwd: this.worktreePath,
112
+ encoding: 'utf8'
113
+ }).trim();
114
+
115
+ try {
116
+ execSync(`git push origin ${branch}`, {
117
+ cwd: this.worktreePath,
118
+ stdio: 'pipe'
119
+ });
120
+ console.log('[agent-commands] Changes pushed successfully');
121
+ } catch (error) {
122
+ console.log('[agent-commands] Push failed or no changes to push');
123
+ }
124
+
125
+ // 3. Create a cleanup marker file
126
+ const cleanupMarker = path.join(this.worktreePath, '.session-cleanup-requested');
127
+ fs.writeFileSync(cleanupMarker, JSON.stringify({
128
+ sessionId: this.sessionId,
129
+ timestamp: new Date().toISOString(),
130
+ worktree: this.worktreePath
131
+ }, null, 2));
132
+
133
+ console.log('[agent-commands] ✓ Session cleanup complete');
134
+ console.log('[agent-commands] The worktree has been prepared for removal.');
135
+ console.log('[agent-commands] Run "npm run devops:close" from the main repo to remove the worktree.');
136
+ console.log('[agent-commands] Stopping agent...');
137
+
138
+ // Exit the agent process
139
+ setTimeout(() => {
140
+ process.exit(0);
141
+ }, 2000);
142
+
143
+ } catch (error) {
144
+ console.error('[agent-commands] Error during cleanup:', error.message);
145
+ this.isClosing = false;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Handle status command
151
+ */
152
+ handleStatus() {
153
+ try {
154
+ const branch = execSync(`git rev-parse --abbrev-ref HEAD`, {
155
+ cwd: this.worktreePath,
156
+ encoding: 'utf8'
157
+ }).trim();
158
+
159
+ const status = execSync(`git status --short`, {
160
+ cwd: this.worktreePath,
161
+ encoding: 'utf8'
162
+ });
163
+
164
+ console.log('[agent-commands] Session Status:');
165
+ console.log(` Session ID: ${this.sessionId}`);
166
+ console.log(` Branch: ${branch}`);
167
+ console.log(` Working directory: ${this.worktreePath}`);
168
+
169
+ if (status.trim()) {
170
+ console.log(' Uncommitted changes:');
171
+ console.log(status);
172
+ } else {
173
+ console.log(' No uncommitted changes');
174
+ }
175
+ } catch (error) {
176
+ console.error('[agent-commands] Error getting status:', error.message);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Handle push command
182
+ */
183
+ handlePush() {
184
+ try {
185
+ const branch = execSync(`git rev-parse --abbrev-ref HEAD`, {
186
+ cwd: this.worktreePath,
187
+ encoding: 'utf8'
188
+ }).trim();
189
+
190
+ console.log(`[agent-commands] Pushing branch ${branch}...`);
191
+ execSync(`git push origin ${branch}`, {
192
+ cwd: this.worktreePath,
193
+ stdio: 'inherit'
194
+ });
195
+ console.log('[agent-commands] Push complete');
196
+ } catch (error) {
197
+ console.error('[agent-commands] Error pushing:', error.message);
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Stop monitoring
203
+ */
204
+ stop() {
205
+ if (this.interval) {
206
+ clearInterval(this.interval);
207
+ }
208
+ }
209
+ }
210
+
211
+ module.exports = AgentCommandMonitor;
@@ -0,0 +1,488 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ============================================================================
5
+ * CLAUDE SESSION MANAGER
6
+ * ============================================================================
7
+ *
8
+ * Manages multiple Claude/Cline sessions with automatic worktree assignment.
9
+ * Each Claude session gets its own worktree to prevent conflicts.
10
+ *
11
+ * Usage:
12
+ * # Start a new Claude session
13
+ * node claude-session-manager.js start --task "feature-auth"
14
+ *
15
+ * # Get current session info
16
+ * node claude-session-manager.js current
17
+ *
18
+ * # List all active sessions
19
+ * node claude-session-manager.js list
20
+ *
21
+ * # End a session
22
+ * node claude-session-manager.js end --session <id>
23
+ *
24
+ * ============================================================================
25
+ */
26
+
27
+ import fs from 'fs';
28
+ import path from 'path';
29
+ import { execSync } from 'child_process';
30
+ import { fileURLToPath } from 'url';
31
+ import { dirname } from 'path';
32
+ import crypto from 'crypto';
33
+
34
+ const __filename = fileURLToPath(import.meta.url);
35
+ const __dirname = dirname(__filename);
36
+
37
+ // ============================================================================
38
+ // CONFIGURATION
39
+ // ============================================================================
40
+
41
+ const CONFIG = {
42
+ sessionsFile: 'local_deploy/claude-sessions.json',
43
+ worktreesDir: 'local_deploy/worktrees',
44
+ sessionPrefix: 'claude-session',
45
+ colors: {
46
+ reset: '\x1b[0m',
47
+ bright: '\x1b[1m',
48
+ green: '\x1b[32m',
49
+ yellow: '\x1b[33m',
50
+ blue: '\x1b[36m',
51
+ red: '\x1b[31m',
52
+ magenta: '\x1b[35m',
53
+ }
54
+ };
55
+
56
+ // ============================================================================
57
+ // SESSION MANAGER CLASS
58
+ // ============================================================================
59
+
60
+ class ClaudeSessionManager {
61
+ constructor() {
62
+ this.repoRoot = this.getRepoRoot();
63
+ this.sessionsPath = path.join(this.repoRoot, CONFIG.sessionsFile);
64
+ this.worktreesPath = path.join(this.repoRoot, CONFIG.worktreesDir);
65
+ this.loadSessions();
66
+ }
67
+
68
+ getRepoRoot() {
69
+ try {
70
+ return execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
71
+ } catch (error) {
72
+ console.error('Error: Not in a git repository');
73
+ process.exit(1);
74
+ }
75
+ }
76
+
77
+ loadSessions() {
78
+ if (fs.existsSync(this.sessionsPath)) {
79
+ const data = fs.readFileSync(this.sessionsPath, 'utf8');
80
+ this.sessions = JSON.parse(data);
81
+ } else {
82
+ this.sessions = {};
83
+ }
84
+ }
85
+
86
+ saveSessions() {
87
+ fs.writeFileSync(this.sessionsPath, JSON.stringify(this.sessions, null, 2));
88
+ }
89
+
90
+ generateSessionId() {
91
+ // Generate a short, unique session ID
92
+ const timestamp = Date.now().toString(36);
93
+ const random = crypto.randomBytes(2).toString('hex');
94
+ return `${timestamp}-${random}`;
95
+ }
96
+
97
+ /**
98
+ * Start a new Claude session with its own worktree
99
+ */
100
+ startSession(task, options = {}) {
101
+ const sessionId = options.sessionId || this.generateSessionId();
102
+ const sessionName = `${CONFIG.sessionPrefix}-${sessionId}`;
103
+ const worktreeName = `${sessionName}-${task.replace(/\s+/g, '-').toLowerCase()}`;
104
+ const worktreePath = path.join(this.worktreesPath, worktreeName);
105
+ const branchName = `claude/${sessionId}/${task.replace(/\s+/g, '-').toLowerCase()}`;
106
+
107
+ console.log(`${CONFIG.colors.blue}Starting new Claude session...${CONFIG.colors.reset}`);
108
+ console.log(`Session ID: ${CONFIG.colors.bright}${sessionId}${CONFIG.colors.reset}`);
109
+ console.log(`Task: ${task}`);
110
+
111
+ // Check if session already exists
112
+ if (this.sessions[sessionId]) {
113
+ console.log(`${CONFIG.colors.yellow}Session ${sessionId} already exists${CONFIG.colors.reset}`);
114
+ return this.sessions[sessionId];
115
+ }
116
+
117
+ // Create worktree
118
+ try {
119
+ if (!fs.existsSync(this.worktreesPath)) {
120
+ fs.mkdirSync(this.worktreesPath, { recursive: true });
121
+ }
122
+
123
+ // Create new branch and worktree
124
+ console.log(`Creating worktree at: ${worktreePath}`);
125
+ execSync(`git worktree add -b ${branchName} "${worktreePath}" HEAD`, { stdio: 'inherit' });
126
+
127
+ // Create session configuration
128
+ const session = {
129
+ id: sessionId,
130
+ name: sessionName,
131
+ task: task,
132
+ worktree: {
133
+ path: worktreePath,
134
+ branch: branchName,
135
+ name: worktreeName
136
+ },
137
+ created: new Date().toISOString(),
138
+ status: 'active',
139
+ pid: process.pid,
140
+ commitMsgFile: `.${sessionName}-commit-msg`,
141
+ agentConfig: {
142
+ AC_MSG_FILE: `.${sessionName}-commit-msg`,
143
+ AC_BRANCH_PREFIX: `claude_${sessionId}_`,
144
+ AGENT_NAME: sessionName
145
+ }
146
+ };
147
+
148
+ // Save session
149
+ this.sessions[sessionId] = session;
150
+ this.saveSessions();
151
+
152
+ // Create session config file in worktree
153
+ this.createWorktreeConfig(session);
154
+
155
+ // Create VS Code workspace file
156
+ this.createVSCodeWorkspace(session);
157
+
158
+ console.log(`${CONFIG.colors.green}✓ Session created successfully!${CONFIG.colors.reset}`);
159
+ console.log(`\n${CONFIG.colors.bright}To use this session:${CONFIG.colors.reset}`);
160
+ console.log(`1. Open VS Code: ${CONFIG.colors.blue}code "${worktreePath}"${CONFIG.colors.reset}`);
161
+ console.log(`2. Or navigate: ${CONFIG.colors.blue}cd "${worktreePath}"${CONFIG.colors.reset}`);
162
+ console.log(`3. Start agent: ${CONFIG.colors.blue}npm run agent:session ${sessionId}${CONFIG.colors.reset}`);
163
+
164
+ // Output for Claude to read
165
+ console.log(`\n${CONFIG.colors.magenta}[CLAUDE_SESSION_INFO]${CONFIG.colors.reset}`);
166
+ console.log(JSON.stringify({
167
+ sessionId: sessionId,
168
+ worktreePath: worktreePath,
169
+ branchName: branchName,
170
+ commitMsgFile: session.commitMsgFile
171
+ }, null, 2));
172
+
173
+ return session;
174
+
175
+ } catch (error) {
176
+ console.error(`${CONFIG.colors.red}Failed to create session: ${error.message}${CONFIG.colors.reset}`);
177
+ process.exit(1);
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Create configuration files in the worktree
183
+ */
184
+ createWorktreeConfig(session) {
185
+ const configPath = path.join(session.worktree.path, '.claude-session.json');
186
+ fs.writeFileSync(configPath, JSON.stringify(session, null, 2));
187
+
188
+ // Create commit message file
189
+ const msgFilePath = path.join(session.worktree.path, session.commitMsgFile);
190
+ fs.writeFileSync(msgFilePath, '');
191
+
192
+ // Create .env.claude file for environment variables
193
+ const envContent = Object.entries(session.agentConfig)
194
+ .map(([key, value]) => `${key}="${value}"`)
195
+ .join('\n');
196
+
197
+ const envPath = path.join(session.worktree.path, '.env.claude');
198
+ fs.writeFileSync(envPath, envContent);
199
+ }
200
+
201
+ /**
202
+ * Create VS Code workspace configuration
203
+ */
204
+ createVSCodeWorkspace(session) {
205
+ const vscodeDir = path.join(session.worktree.path, '.vscode');
206
+
207
+ if (!fs.existsSync(vscodeDir)) {
208
+ fs.mkdirSync(vscodeDir, { recursive: true });
209
+ }
210
+
211
+ // Settings for this workspace
212
+ const settings = {
213
+ 'window.title': `Claude ${session.id} - ${session.task}`,
214
+ 'terminal.integrated.env.osx': session.agentConfig,
215
+ 'terminal.integrated.env.linux': session.agentConfig,
216
+ 'terminal.integrated.env.windows': session.agentConfig,
217
+ 'files.exclude': {
218
+ '.claude-session.json': false,
219
+ [session.commitMsgFile]: false
220
+ }
221
+ };
222
+
223
+ fs.writeFileSync(
224
+ path.join(vscodeDir, 'settings.json'),
225
+ JSON.stringify(settings, null, 2)
226
+ );
227
+
228
+ // Create workspace file
229
+ const workspaceFile = {
230
+ folders: [
231
+ {
232
+ path: '.',
233
+ name: `Claude: ${session.task}`
234
+ }
235
+ ],
236
+ settings: settings
237
+ };
238
+
239
+ fs.writeFileSync(
240
+ path.join(session.worktree.path, `${session.name}.code-workspace`),
241
+ JSON.stringify(workspaceFile, null, 2)
242
+ );
243
+ }
244
+
245
+ /**
246
+ * Get current session based on current directory
247
+ */
248
+ getCurrentSession() {
249
+ const cwd = process.cwd();
250
+
251
+ // Check if we're in a worktree
252
+ for (const [sessionId, session] of Object.entries(this.sessions)) {
253
+ if (session.worktree.path === cwd || cwd.startsWith(session.worktree.path + '/')) {
254
+ return session;
255
+ }
256
+ }
257
+
258
+ // Check for session file in current directory
259
+ const sessionFilePath = path.join(cwd, '.claude-session.json');
260
+ if (fs.existsSync(sessionFilePath)) {
261
+ const sessionData = JSON.parse(fs.readFileSync(sessionFilePath, 'utf8'));
262
+ return sessionData;
263
+ }
264
+
265
+ return null;
266
+ }
267
+
268
+ /**
269
+ * List all active sessions
270
+ */
271
+ listSessions() {
272
+ console.log(`\n${CONFIG.colors.bright}Active Claude Sessions:${CONFIG.colors.reset}`);
273
+
274
+ const activeSessions = Object.values(this.sessions).filter(s => s.status === 'active');
275
+
276
+ if (activeSessions.length === 0) {
277
+ console.log('No active sessions');
278
+ return;
279
+ }
280
+
281
+ for (const session of activeSessions) {
282
+ const exists = fs.existsSync(session.worktree.path);
283
+ const status = exists ? CONFIG.colors.green + '✓' : CONFIG.colors.red + '✗';
284
+
285
+ console.log(`\n${status} ${CONFIG.colors.bright}${session.id}${CONFIG.colors.reset}`);
286
+ console.log(` Task: ${session.task}`);
287
+ console.log(` Branch: ${session.worktree.branch}`);
288
+ console.log(` Path: ${session.worktree.path}`);
289
+ console.log(` Created: ${new Date(session.created).toLocaleString()}`);
290
+
291
+ if (!exists) {
292
+ console.log(` ${CONFIG.colors.yellow}(Worktree missing)${CONFIG.colors.reset}`);
293
+ }
294
+ }
295
+
296
+ console.log(`\nTotal active sessions: ${activeSessions.length}`);
297
+ }
298
+
299
+ /**
300
+ * End a session and optionally clean up
301
+ */
302
+ endSession(sessionId, options = {}) {
303
+ const session = this.sessions[sessionId];
304
+
305
+ if (!session) {
306
+ console.error(`Session not found: ${sessionId}`);
307
+ return;
308
+ }
309
+
310
+ console.log(`${CONFIG.colors.yellow}Ending session: ${sessionId}${CONFIG.colors.reset}`);
311
+
312
+ // Mark as inactive
313
+ session.status = 'inactive';
314
+ session.ended = new Date().toISOString();
315
+
316
+ if (options.cleanup) {
317
+ // Remove worktree
318
+ if (fs.existsSync(session.worktree.path)) {
319
+ console.log(`Removing worktree: ${session.worktree.path}`);
320
+ try {
321
+ execSync(`git worktree remove "${session.worktree.path}" --force`, { stdio: 'inherit' });
322
+ } catch (error) {
323
+ console.error(`Failed to remove worktree: ${error.message}`);
324
+ }
325
+ }
326
+
327
+ // Delete branch if requested
328
+ if (options.deleteBranch) {
329
+ console.log(`Deleting branch: ${session.worktree.branch}`);
330
+ try {
331
+ execSync(`git branch -D ${session.worktree.branch}`, { stdio: 'inherit' });
332
+ } catch (error) {
333
+ console.error(`Failed to delete branch: ${error.message}`);
334
+ }
335
+ }
336
+
337
+ // Remove from sessions
338
+ delete this.sessions[sessionId];
339
+ }
340
+
341
+ this.saveSessions();
342
+ console.log(`${CONFIG.colors.green}Session ended${CONFIG.colors.reset}`);
343
+ }
344
+
345
+ /**
346
+ * Start the DevOps agent for a specific session
347
+ */
348
+ startAgent(sessionId) {
349
+ const session = this.sessions[sessionId];
350
+
351
+ if (!session) {
352
+ console.error(`Session not found: ${sessionId}`);
353
+ return;
354
+ }
355
+
356
+ console.log(`${CONFIG.colors.blue}Starting DevOps agent for session: ${sessionId}${CONFIG.colors.reset}`);
357
+
358
+ // Build environment variables
359
+ const env = Object.entries(session.agentConfig)
360
+ .map(([key, value]) => `${key}="${value}"`)
361
+ .join(' ');
362
+
363
+ // Command to start the agent
364
+ const agentScript = path.join(__dirname, 'cs-devops-agent-worker.js');
365
+ const command = `cd "${session.worktree.path}" && ${env} node "${agentScript}"`;
366
+
367
+ console.log(`\nRun this command to start the agent:`);
368
+ console.log(`${CONFIG.colors.blue}${command}${CONFIG.colors.reset}`);
369
+
370
+ return command;
371
+ }
372
+ }
373
+
374
+ // ============================================================================
375
+ // CLI INTERFACE
376
+ // ============================================================================
377
+
378
+ function showHelp() {
379
+ console.log(`
380
+ ${CONFIG.colors.bright}Claude Session Manager${CONFIG.colors.reset}
381
+
382
+ Usage:
383
+ node claude-session-manager.js <command> [options]
384
+
385
+ Commands:
386
+ start --task <name> Start a new Claude session with a task
387
+ current Show current session info
388
+ list List all active sessions
389
+ end --session <id> End a session
390
+ agent --session <id> Get command to start agent for session
391
+ help Show this help message
392
+
393
+ Options:
394
+ --task <name> Task or feature name
395
+ --session <id> Session ID
396
+ --cleanup Remove worktree when ending session
397
+ --delete-branch Delete branch when ending session
398
+
399
+ Examples:
400
+ # Start a new session for authentication feature
401
+ node claude-session-manager.js start --task "authentication-feature"
402
+
403
+ # List all active sessions
404
+ node claude-session-manager.js list
405
+
406
+ # End a session and clean up
407
+ node claude-session-manager.js end --session abc123 --cleanup --delete-branch
408
+
409
+ # Get current session info (run from within a worktree)
410
+ node claude-session-manager.js current
411
+ `);
412
+ }
413
+
414
+ async function main() {
415
+ const args = process.argv.slice(2);
416
+ const command = args[0];
417
+
418
+ if (!command || command === 'help') {
419
+ showHelp();
420
+ return;
421
+ }
422
+
423
+ const manager = new ClaudeSessionManager();
424
+
425
+ switch (command) {
426
+ case 'start': {
427
+ const taskIdx = args.indexOf('--task');
428
+ if (taskIdx === -1 || !args[taskIdx + 1]) {
429
+ console.error('Error: --task is required for start command');
430
+ process.exit(1);
431
+ }
432
+ const task = args[taskIdx + 1];
433
+ manager.startSession(task);
434
+ break;
435
+ }
436
+
437
+ case 'current': {
438
+ const session = manager.getCurrentSession();
439
+ if (session) {
440
+ console.log(`${CONFIG.colors.bright}Current Session:${CONFIG.colors.reset}`);
441
+ console.log(JSON.stringify(session, null, 2));
442
+ } else {
443
+ console.log('Not in a Claude session worktree');
444
+ }
445
+ break;
446
+ }
447
+
448
+ case 'list': {
449
+ manager.listSessions();
450
+ break;
451
+ }
452
+
453
+ case 'end': {
454
+ const sessionIdx = args.indexOf('--session');
455
+ if (sessionIdx === -1 || !args[sessionIdx + 1]) {
456
+ console.error('Error: --session is required for end command');
457
+ process.exit(1);
458
+ }
459
+ const sessionId = args[sessionIdx + 1];
460
+ const cleanup = args.includes('--cleanup');
461
+ const deleteBranch = args.includes('--delete-branch');
462
+ manager.endSession(sessionId, { cleanup, deleteBranch });
463
+ break;
464
+ }
465
+
466
+ case 'agent': {
467
+ const sessionIdx = args.indexOf('--session');
468
+ if (sessionIdx === -1 || !args[sessionIdx + 1]) {
469
+ console.error('Error: --session is required for agent command');
470
+ process.exit(1);
471
+ }
472
+ const sessionId = args[sessionIdx + 1];
473
+ manager.startAgent(sessionId);
474
+ break;
475
+ }
476
+
477
+ default:
478
+ console.error(`Unknown command: ${command}`);
479
+ showHelp();
480
+ process.exit(1);
481
+ }
482
+ }
483
+
484
+ // Run the CLI
485
+ main().catch(err => {
486
+ console.error(`Error: ${err.message}`);
487
+ process.exit(1);
488
+ });