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,372 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ============================================================================
5
+ * RUN WITH AGENT - Helper script to run CS_DevOpsAgent with agent worktrees
6
+ * ============================================================================
7
+ *
8
+ * This script simplifies running CS_DevOpsAgent with automatic worktree creation
9
+ * for different AI agents. It sets up the necessary environment variables
10
+ * and launches the cs-devops-agent worker in the appropriate context.
11
+ *
12
+ * Usage:
13
+ * node run-with-agent.js --agent claude --repo /path/to/repo --task feature-x
14
+ * node run-with-agent.js --agent copilot --repo ../my-project
15
+ * node run-with-agent.js --detect --repo /path/to/repo
16
+ *
17
+ * Options:
18
+ * --agent <name> Specify the agent name (claude, copilot, cursor, aider, custom)
19
+ * --repo <path> Path to the target repository
20
+ * --task <name> Task or feature name (default: development)
21
+ * --detect Auto-detect agent from environment
22
+ * --no-worktree Disable worktree creation (work directly in repo)
23
+ * --list List all active agent worktrees
24
+ * ============================================================================
25
+ */
26
+
27
+ import fs from 'fs';
28
+ import path from 'path';
29
+ import { spawn, execSync } from 'child_process';
30
+ import { fileURLToPath } from 'url';
31
+ import { dirname } from 'path';
32
+
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const __dirname = dirname(__filename);
35
+
36
+ // ============================================================================
37
+ // CONFIGURATION
38
+ // ============================================================================
39
+
40
+ const KNOWN_AGENTS = {
41
+ claude: {
42
+ envVars: ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY'],
43
+ msgFile: '.claude-commit-msg',
44
+ color: '\x1b[35m' // Magenta
45
+ },
46
+ copilot: {
47
+ envVars: ['COPILOT_API_KEY', 'GITHUB_COPILOT_ENABLED'],
48
+ msgFile: '.copilot-commit-msg',
49
+ color: '\x1b[36m' // Cyan
50
+ },
51
+ cursor: {
52
+ envVars: ['CURSOR_API_KEY', 'CURSOR_ENABLED'],
53
+ msgFile: '.cursor-commit-msg',
54
+ color: '\x1b[34m' // Blue
55
+ },
56
+ aider: {
57
+ envVars: ['AIDER_API_KEY', 'OPENAI_API_KEY'],
58
+ msgFile: '.aider-commit-msg',
59
+ color: '\x1b[33m' // Yellow
60
+ },
61
+ warp: {
62
+ envVars: ['WARP_API_KEY', 'WARP_ENABLED'],
63
+ msgFile: '.warp-commit-msg',
64
+ color: '\x1b[32m' // Green
65
+ }
66
+ };
67
+
68
+ const RESET_COLOR = '\x1b[0m';
69
+ const BRIGHT = '\x1b[1m';
70
+
71
+ // ============================================================================
72
+ // UTILITY FUNCTIONS
73
+ // ============================================================================
74
+
75
+ function log(agent, msg) {
76
+ const config = KNOWN_AGENTS[agent] || { color: '\x1b[37m' };
77
+ console.log(`${config.color}${BRIGHT}[${agent.toUpperCase()}]${RESET_COLOR} ${msg}`);
78
+ }
79
+
80
+ function error(msg) {
81
+ console.error(`\x1b[31m[ERROR]${RESET_COLOR} ${msg}`);
82
+ process.exit(1);
83
+ }
84
+
85
+ function detectAgent() {
86
+ // Check environment variables for agent indicators
87
+ for (const [agent, config] of Object.entries(KNOWN_AGENTS)) {
88
+ for (const envVar of config.envVars) {
89
+ if (process.env[envVar]) {
90
+ return agent;
91
+ }
92
+ }
93
+ }
94
+
95
+ // Check for agent-specific tools in PATH
96
+ try {
97
+ execSync('which copilot', { stdio: 'pipe' });
98
+ return 'copilot';
99
+ } catch {}
100
+
101
+ try {
102
+ execSync('which cursor', { stdio: 'pipe' });
103
+ return 'cursor';
104
+ } catch {}
105
+
106
+ try {
107
+ execSync('which aider', { stdio: 'pipe' });
108
+ return 'aider';
109
+ } catch {}
110
+
111
+ // Check terminal environment
112
+ if (process.env.TERM_PROGRAM === 'WarpTerminal') {
113
+ return 'warp';
114
+ }
115
+
116
+ return null;
117
+ }
118
+
119
+ function parseArgs() {
120
+ const args = process.argv.slice(2);
121
+ const options = {
122
+ agent: null,
123
+ repo: null,
124
+ task: 'development',
125
+ detect: false,
126
+ noWorktree: false,
127
+ list: false,
128
+ help: false
129
+ };
130
+
131
+ for (let i = 0; i < args.length; i++) {
132
+ switch (args[i]) {
133
+ case '--agent':
134
+ case '-a':
135
+ options.agent = args[++i];
136
+ break;
137
+ case '--repo':
138
+ case '-r':
139
+ options.repo = args[++i];
140
+ break;
141
+ case '--task':
142
+ case '-t':
143
+ options.task = args[++i];
144
+ break;
145
+ case '--detect':
146
+ case '-d':
147
+ options.detect = true;
148
+ break;
149
+ case '--no-worktree':
150
+ options.noWorktree = true;
151
+ break;
152
+ case '--list':
153
+ case '-l':
154
+ options.list = true;
155
+ break;
156
+ case '--help':
157
+ case '-h':
158
+ options.help = true;
159
+ break;
160
+ }
161
+ }
162
+
163
+ return options;
164
+ }
165
+
166
+ function showHelp() {
167
+ console.log(`
168
+ ${BRIGHT}Run CS_DevOpsAgent with Agent Worktrees${RESET_COLOR}
169
+
170
+ Usage:
171
+ node run-with-agent.js --agent <name> --repo <path> [options]
172
+ node run-with-agent.js --detect --repo <path> [options]
173
+
174
+ Options:
175
+ -a, --agent <name> Specify the agent name (claude, copilot, cursor, aider, warp, custom)
176
+ -r, --repo <path> Path to the target repository
177
+ -t, --task <name> Task or feature name (default: development)
178
+ -d, --detect Auto-detect agent from environment
179
+ --no-worktree Disable worktree creation (work directly in repo)
180
+ -l, --list List all active agent worktrees
181
+ -h, --help Show this help message
182
+
183
+ Examples:
184
+ # Run with specific agent
185
+ node run-with-agent.js --agent claude --repo /path/to/repo --task auth-feature
186
+
187
+ # Auto-detect agent
188
+ node run-with-agent.js --detect --repo ../my-project
189
+
190
+ # List active worktrees
191
+ node run-with-agent.js --list --repo /path/to/repo
192
+
193
+ # Run without worktree
194
+ node run-with-agent.js --agent copilot --repo . --no-worktree
195
+
196
+ Known Agents:
197
+ ${Object.keys(KNOWN_AGENTS).join(', ')}
198
+
199
+ Environment Variables:
200
+ AGENT_NAME Override agent name
201
+ AGENT_TASK Override task name
202
+ AC_USE_WORKTREE Enable/disable worktrees (true/false/auto)
203
+ `);
204
+ }
205
+
206
+ function listWorktrees(repoPath) {
207
+ try {
208
+ process.chdir(repoPath);
209
+ const result = execSync('git worktree list', { encoding: 'utf8' });
210
+
211
+ console.log(`\n${BRIGHT}Active Worktrees in ${repoPath}:${RESET_COLOR}\n`);
212
+
213
+ const lines = result.split('\n').filter(l => l);
214
+ for (const line of lines) {
215
+ const match = line.match(/(.+?)\s+([a-f0-9]+)\s+\[(.+?)\]/);
216
+ if (match) {
217
+ const [, path, commit, branch] = match;
218
+ const isAgent = branch.includes('agent/');
219
+
220
+ if (isAgent) {
221
+ const agentMatch = branch.match(/agent\/([^\/]+)\//);
222
+ const agent = agentMatch ? agentMatch[1] : 'unknown';
223
+ const config = KNOWN_AGENTS[agent] || { color: '\x1b[37m' };
224
+
225
+ console.log(`${config.color}[${agent.toUpperCase()}]${RESET_COLOR}`);
226
+ console.log(` Path: ${path}`);
227
+ console.log(` Branch: ${branch}`);
228
+ console.log(` Commit: ${commit}`);
229
+ console.log('');
230
+ } else if (path === repoPath) {
231
+ console.log(`${BRIGHT}[MAIN]${RESET_COLOR}`);
232
+ console.log(` Path: ${path}`);
233
+ console.log(` Branch: ${branch}`);
234
+ console.log(` Commit: ${commit}`);
235
+ console.log('');
236
+ }
237
+ }
238
+ }
239
+
240
+ // Check for agent configs
241
+ const worktreesDir = path.join(repoPath, '.worktrees');
242
+ if (fs.existsSync(worktreesDir)) {
243
+ const agentsFile = path.join(worktreesDir, 'agents.json');
244
+ if (fs.existsSync(agentsFile)) {
245
+ const agents = JSON.parse(fs.readFileSync(agentsFile, 'utf8'));
246
+ console.log(`${BRIGHT}Registered Agents:${RESET_COLOR}`);
247
+ for (const [agent, data] of Object.entries(agents)) {
248
+ const active = (data.worktrees || []).filter(w => w.status === 'active').length;
249
+ console.log(` ${agent}: ${active} active worktree(s)`);
250
+ }
251
+ }
252
+ }
253
+
254
+ } catch (err) {
255
+ error(`Failed to list worktrees: ${err.message}`);
256
+ }
257
+ }
258
+
259
+ // ============================================================================
260
+ // MAIN EXECUTION
261
+ // ============================================================================
262
+
263
+ async function main() {
264
+ const options = parseArgs();
265
+
266
+ if (options.help) {
267
+ showHelp();
268
+ return;
269
+ }
270
+
271
+ // Validate repository path
272
+ if (!options.repo) {
273
+ error('Repository path is required. Use --repo <path>');
274
+ }
275
+
276
+ const repoPath = path.resolve(options.repo);
277
+ if (!fs.existsSync(repoPath)) {
278
+ error(`Repository path does not exist: ${repoPath}`);
279
+ }
280
+
281
+ // Check if it's a git repository
282
+ try {
283
+ execSync('git rev-parse --git-dir', { cwd: repoPath, stdio: 'pipe' });
284
+ } catch {
285
+ error(`Not a git repository: ${repoPath}`);
286
+ }
287
+
288
+ // Handle list command
289
+ if (options.list) {
290
+ listWorktrees(repoPath);
291
+ return;
292
+ }
293
+
294
+ // Determine agent
295
+ let agent = options.agent;
296
+ if (options.detect && !agent) {
297
+ agent = detectAgent();
298
+ if (!agent) {
299
+ console.log('No agent detected. Using generic agent name.');
300
+ agent = `agent-${Date.now().toString(36)}`;
301
+ } else {
302
+ console.log(`Detected agent: ${agent}`);
303
+ }
304
+ }
305
+
306
+ if (!agent) {
307
+ error('Agent name is required. Use --agent <name> or --detect');
308
+ }
309
+
310
+ // Prepare environment variables
311
+ const env = { ...process.env };
312
+
313
+ env.AGENT_NAME = agent;
314
+ env.AGENT_TASK = options.task;
315
+ env.AI_AGENT = agent; // Alternative env var
316
+ env.AI_TASK = options.task; // Alternative env var
317
+
318
+ if (options.noWorktree) {
319
+ env.AC_USE_WORKTREE = 'false';
320
+ } else {
321
+ env.AC_USE_WORKTREE = 'true';
322
+ }
323
+
324
+ // Set agent-specific message file
325
+ const agentConfig = KNOWN_AGENTS[agent];
326
+ if (agentConfig && agentConfig.msgFile) {
327
+ env.AC_MSG_FILE = agentConfig.msgFile;
328
+ } else {
329
+ env.AC_MSG_FILE = `.${agent}-commit-msg`;
330
+ }
331
+
332
+ // Set branch prefix for agent
333
+ env.AC_BRANCH_PREFIX = `agent_${agent}_`;
334
+
335
+ log(agent, `Starting CS_DevOpsAgent worker`);
336
+ log(agent, `Repository: ${repoPath}`);
337
+ log(agent, `Task: ${options.task}`);
338
+ log(agent, `Worktrees: ${options.noWorktree ? 'disabled' : 'enabled'}`);
339
+ log(agent, `Message file: ${env.AC_MSG_FILE}`);
340
+
341
+ // Launch cs-devops-agent worker
342
+ const workerPath = path.join(__dirname, 'cs-devops-agent-worker.js');
343
+
344
+ const worker = spawn('node', [workerPath], {
345
+ cwd: repoPath,
346
+ env,
347
+ stdio: 'inherit'
348
+ });
349
+
350
+ // Handle graceful shutdown
351
+ process.on('SIGINT', () => {
352
+ log(agent, 'Stopping CS_DevOpsAgent worker...');
353
+ worker.kill('SIGINT');
354
+ setTimeout(() => process.exit(0), 1000);
355
+ });
356
+
357
+ process.on('SIGTERM', () => {
358
+ log(agent, 'Stopping CS_DevOpsAgent worker...');
359
+ worker.kill('SIGTERM');
360
+ setTimeout(() => process.exit(0), 1000);
361
+ });
362
+
363
+ worker.on('exit', (code) => {
364
+ log(agent, `Worker exited with code: ${code}`);
365
+ process.exit(code);
366
+ });
367
+ }
368
+
369
+ // Run the main function
370
+ main().catch(err => {
371
+ error(`Fatal error: ${err.message}`);
372
+ });