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,985 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ============================================================================
5
+ * DEVOPS-AGENT WORKER SETUP SCRIPT
6
+ * ============================================================================
7
+ *
8
+ * This script sets up the cs-devops-agent worker for a new developer:
9
+ * 1. Prompts for developer's 3-letter initials
10
+ * 2. Configures branch prefix (e.g., dev_sdd_ becomes dev_abc_)
11
+ * 3. Installs required npm packages
12
+ * 4. Creates/updates VS Code settings and tasks
13
+ * 5. Sets up commit message files
14
+ * 6. Creates a personalized run script
15
+ *
16
+ * Usage: node setup-cs-devops-agent.js
17
+ * ============================================================================
18
+ */
19
+
20
+ import fs from 'fs';
21
+ import path from 'path';
22
+ import { execSync } from 'child_process';
23
+ import readline from 'readline';
24
+ import { fileURLToPath } from 'url';
25
+ import { dirname } from 'path';
26
+
27
+ const __filename = fileURLToPath(import.meta.url);
28
+ const __dirname = dirname(__filename);
29
+
30
+ // Colors for console output
31
+ const colors = {
32
+ reset: '\x1b[0m',
33
+ bright: '\x1b[1m',
34
+ green: '\x1b[32m',
35
+ yellow: '\x1b[33m',
36
+ blue: '\x1b[36m',
37
+ red: '\x1b[31m',
38
+ };
39
+
40
+ const log = {
41
+ info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
42
+ success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
43
+ warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
44
+ error: (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`),
45
+ header: (msg) => console.log(`\n${colors.bright}${colors.blue}${'='.repeat(60)}${colors.reset}`),
46
+ title: (msg) => console.log(`${colors.bright}${msg}${colors.reset}`),
47
+ };
48
+
49
+ // ============================================================================
50
+ // HELPER FUNCTIONS
51
+ // ============================================================================
52
+
53
+ function findProjectRoot() {
54
+ let currentDir = process.cwd();
55
+
56
+ // Look for .git directory to find project root
57
+ while (currentDir !== path.dirname(currentDir)) {
58
+ if (fs.existsSync(path.join(currentDir, '.git'))) {
59
+ return currentDir;
60
+ }
61
+ currentDir = path.dirname(currentDir);
62
+ }
63
+
64
+ // Fallback to current directory
65
+ return process.cwd();
66
+ }
67
+
68
+ async function prompt(question) {
69
+ const rl = readline.createInterface({
70
+ input: process.stdin,
71
+ output: process.stdout
72
+ });
73
+
74
+ return new Promise((resolve) => {
75
+ rl.question(question, (answer) => {
76
+ rl.close();
77
+ resolve(answer);
78
+ });
79
+ });
80
+ }
81
+
82
+ function validateInitials(initials) {
83
+ // Remove spaces and convert to lowercase
84
+ const cleaned = initials.replace(/\s/g, '').toLowerCase();
85
+
86
+ // Check if exactly 3 letters
87
+ if (!/^[a-z]{3}$/.test(cleaned)) {
88
+ return null;
89
+ }
90
+
91
+ return cleaned;
92
+ }
93
+
94
+ function ensureDirectoryExists(dirPath) {
95
+ if (!fs.existsSync(dirPath)) {
96
+ fs.mkdirSync(dirPath, { recursive: true });
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+
102
+ function backupFile(filePath) {
103
+ if (fs.existsSync(filePath)) {
104
+ const backupPath = `${filePath}.backup.${Date.now()}`;
105
+ fs.copyFileSync(filePath, backupPath);
106
+ log.info(`Backed up ${path.basename(filePath)} to ${path.basename(backupPath)}`);
107
+ return backupPath;
108
+ }
109
+ return null;
110
+ }
111
+
112
+ // ============================================================================
113
+ // SETUP FUNCTIONS
114
+ // ============================================================================
115
+
116
+ async function setupNpmPackages(projectRoot) {
117
+ log.header();
118
+ log.title('📦 Installing NPM Packages');
119
+
120
+ const packageJsonPath = path.join(projectRoot, 'package.json');
121
+
122
+ // Check if package.json exists, create if not
123
+ if (!fs.existsSync(packageJsonPath)) {
124
+ log.info('Creating package.json...');
125
+ const packageJson = {
126
+ name: path.basename(projectRoot),
127
+ version: '1.0.0',
128
+ type: 'module',
129
+ description: 'SecondBrain Development Project',
130
+ scripts: {},
131
+ devDependencies: {}
132
+ };
133
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
134
+ log.success('Created package.json');
135
+ }
136
+
137
+ // Read current package.json
138
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
139
+
140
+ // Ensure type: module
141
+ if (packageJson.type !== 'module') {
142
+ packageJson.type = 'module';
143
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
144
+ log.success('Set package.json type to "module"');
145
+ }
146
+
147
+ // Install required packages
148
+ log.info('Installing chokidar and execa...');
149
+ try {
150
+ execSync('npm install --save-dev chokidar execa', {
151
+ cwd: projectRoot,
152
+ stdio: 'inherit'
153
+ });
154
+ log.success('Installed required npm packages');
155
+ } catch (error) {
156
+ log.warn('Could not install packages automatically. Please run: npm install --save-dev chokidar execa');
157
+ }
158
+
159
+ return packageJson;
160
+ }
161
+
162
+ function setupVSCodeSettings(projectRoot, initials) {
163
+ log.header();
164
+ log.title('⚙️ Setting up VS Code Configuration');
165
+
166
+ const vscodeDir = path.join(projectRoot, '.vscode');
167
+ ensureDirectoryExists(vscodeDir);
168
+
169
+ // Setup settings.json
170
+ const settingsPath = path.join(vscodeDir, 'settings.json');
171
+ let settings = {};
172
+
173
+ if (fs.existsSync(settingsPath)) {
174
+ backupFile(settingsPath);
175
+ try {
176
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
177
+ } catch (e) {
178
+ log.warn('Could not parse existing settings.json, creating new one');
179
+ }
180
+ }
181
+
182
+ // Add cs-devops-agent settings
183
+ settings['terminal.integrated.env.osx'] = settings['terminal.integrated.env.osx'] || {};
184
+ settings['terminal.integrated.env.linux'] = settings['terminal.integrated.env.linux'] || {};
185
+ settings['terminal.integrated.env.windows'] = settings['terminal.integrated.env.windows'] || {};
186
+
187
+ const envVars = {
188
+ AC_BRANCH_PREFIX: `dev_${initials}_`,
189
+ AC_DAILY_PREFIX: `dev_${initials}_`,
190
+ AC_TZ: 'Asia/Dubai',
191
+ AC_PUSH: 'true',
192
+ AC_REQUIRE_MSG: 'true',
193
+ AC_MSG_MIN_BYTES: '20',
194
+ AC_DEBOUNCE_MS: '1500',
195
+ AC_MSG_DEBOUNCE_MS: '3000',
196
+ AC_CLEAR_MSG_WHEN: 'push',
197
+ AC_ROLLOVER_PROMPT: 'true',
198
+ AC_DEBUG: 'false'
199
+ };
200
+
201
+ // Apply to all platforms
202
+ Object.assign(settings['terminal.integrated.env.osx'], envVars);
203
+ Object.assign(settings['terminal.integrated.env.linux'], envVars);
204
+ Object.assign(settings['terminal.integrated.env.windows'], envVars);
205
+
206
+ // Add file associations for commit messages
207
+ settings['files.associations'] = settings['files.associations'] || {};
208
+ settings['files.associations']['.claude-commit-msg'] = 'markdown';
209
+ settings['files.associations']['CLAUDE_CHANGELOG.md'] = 'markdown';
210
+
211
+ // Add file watchers
212
+ settings['files.watcherExclude'] = settings['files.watcherExclude'] || {};
213
+ settings['files.watcherExclude']['**/Archive/**'] = true;
214
+ settings['files.watcherExclude']['**/__pycache__/**'] = true;
215
+
216
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
217
+ log.success(`Created/Updated VS Code settings with prefix: dev_${initials}_`);
218
+
219
+ return settings;
220
+ }
221
+
222
+ function setupVSCodeTasks(projectRoot, initials) {
223
+ log.title('📋 Setting up VS Code Tasks');
224
+
225
+ const vscodeDir = path.join(projectRoot, '.vscode');
226
+ const tasksPath = path.join(vscodeDir, 'tasks.json');
227
+
228
+ let tasks = {
229
+ version: '2.0.0',
230
+ tasks: []
231
+ };
232
+
233
+ if (fs.existsSync(tasksPath)) {
234
+ backupFile(tasksPath);
235
+ try {
236
+ tasks = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
237
+ } catch (e) {
238
+ log.warn('Could not parse existing tasks.json, creating new one');
239
+ }
240
+ }
241
+
242
+ // Remove old cs-devops-agent tasks if they exist
243
+ tasks.tasks = tasks.tasks.filter(task => !task.label.includes('DevOps Agent'));
244
+
245
+ // Add new cs-devops-agent tasks
246
+ const autoCommitTasks = [
247
+ {
248
+ label: '🚀 Start DevOps Agent Worker',
249
+ type: 'shell',
250
+ command: 'node',
251
+ args: ['ScriptCS_DevOpsAgent/cs-devops-agent-worker.js'],
252
+ options: {
253
+ env: {
254
+ AC_BRANCH_PREFIX: `dev_${initials}_`,
255
+ AC_DEBUG: 'true'
256
+ }
257
+ },
258
+ problemMatcher: [],
259
+ presentation: {
260
+ echo: true,
261
+ reveal: 'always',
262
+ focus: false,
263
+ panel: 'dedicated',
264
+ showReuseMessage: false,
265
+ clear: true
266
+ },
267
+ runOptions: {
268
+ runOn: 'manual'
269
+ }
270
+ },
271
+ {
272
+ label: '🛑 Stop DevOps Agent Worker',
273
+ type: 'shell',
274
+ command: 'pkill -f "node.*cs-devops-agent-worker" || echo "Worker not running"',
275
+ problemMatcher: [],
276
+ presentation: {
277
+ echo: true,
278
+ reveal: 'always',
279
+ focus: false,
280
+ panel: 'shared'
281
+ }
282
+ },
283
+ {
284
+ label: '📝 Create Commit Message',
285
+ type: 'shell',
286
+ command: 'echo "feat(): " > .claude-commit-msg && code .claude-commit-msg',
287
+ problemMatcher: [],
288
+ presentation: {
289
+ echo: false,
290
+ reveal: 'never'
291
+ }
292
+ },
293
+ {
294
+ label: '📊 Show Git Status',
295
+ type: 'shell',
296
+ command: 'git status && echo "" && git branch --show-current',
297
+ problemMatcher: [],
298
+ presentation: {
299
+ echo: true,
300
+ reveal: 'always',
301
+ focus: false,
302
+ panel: 'shared'
303
+ }
304
+ }
305
+ ];
306
+
307
+ tasks.tasks.push(...autoCommitTasks);
308
+
309
+ fs.writeFileSync(tasksPath, JSON.stringify(tasks, null, 2));
310
+ log.success('Created/Updated VS Code tasks.json');
311
+
312
+ return tasks;
313
+ }
314
+
315
+ function setupCommitFiles(projectRoot, initials) {
316
+ log.header();
317
+ log.title('📝 Setting up Commit Message Files');
318
+
319
+ // Setup .claude-commit-msg
320
+ const commitMsgPath = path.join(projectRoot, '.claude-commit-msg');
321
+ if (!fs.existsSync(commitMsgPath)) {
322
+ fs.writeFileSync(commitMsgPath, '');
323
+ log.success('Created .claude-commit-msg file');
324
+ } else {
325
+ log.info('.claude-commit-msg already exists');
326
+ }
327
+
328
+ // Setup CLAUDE_CHANGELOG.md
329
+ const changelogPath = path.join(projectRoot, 'CLAUDE_CHANGELOG.md');
330
+ if (!fs.existsSync(changelogPath)) {
331
+ const initialContent = `# Claude AI Assistant Changelog
332
+
333
+ This file tracks all changes made by Claude AI assistant to this codebase.
334
+ Each entry includes a timestamp, commit type, and detailed description.
335
+
336
+ Developer: ${initials.toUpperCase()}
337
+ Branch Prefix: dev_${initials}_
338
+ Initialized: ${new Date().toISOString()}
339
+
340
+ ---
341
+
342
+ ## Changelog History
343
+
344
+ `;
345
+ fs.writeFileSync(changelogPath, initialContent);
346
+ log.success('Created CLAUDE_CHANGELOG.md');
347
+ } else {
348
+ log.info('CLAUDE_CHANGELOG.md already exists');
349
+ }
350
+
351
+ // Setup Documentation/infrastructure.md
352
+ const docDir = path.join(projectRoot, 'Documentation');
353
+ const infraDocPath = path.join(docDir, 'infrastructure.md');
354
+
355
+ if (!fs.existsSync(docDir)) {
356
+ fs.mkdirSync(docDir, { recursive: true });
357
+ log.success('Created Documentation directory');
358
+ }
359
+
360
+ if (!fs.existsSync(infraDocPath)) {
361
+ const infraTemplate = `# Infrastructure Change Log
362
+
363
+ This document tracks all infrastructure changes made to the project. It is automatically updated when infrastructure-related files are modified.
364
+
365
+ ## Format Guidelines
366
+
367
+ Each entry should follow this format:
368
+ \`\`\`
369
+ ## [Date] - [Agent/Developer Name]
370
+ ### Category: [Config|Dependencies|Build|Architecture|Database|API|Security]
371
+ **Change Type**: [Added|Modified|Removed|Fixed]
372
+ **Component**: [Affected component/service]
373
+ **Description**: Brief description of the change
374
+ **Reason**: Why this change was necessary
375
+ **Impact**: Potential impacts or considerations
376
+ **Files Changed**:
377
+ - file1.js
378
+ - config/settings.json
379
+ \`\`\`
380
+
381
+ ---
382
+
383
+ <!-- New entries will be added above this line -->`;
384
+
385
+ fs.writeFileSync(infraDocPath, infraTemplate);
386
+ log.success('Created infrastructure documentation template');
387
+ } else {
388
+ log.info('Documentation/infrastructure.md already exists');
389
+ }
390
+
391
+ // Setup CLAUDE.md if it doesn't exist
392
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
393
+ if (!fs.existsSync(claudeMdPath)) {
394
+ const claudeRules = `# House Rules for Claude
395
+
396
+ ## Developer Information
397
+ - Developer Initials: ${initials.toUpperCase()}
398
+ - Branch Prefix: dev_${initials}_
399
+ - Default Timezone: Asia/Dubai
400
+ - Project Root: ${projectRoot}
401
+
402
+ ## Commit Policy
403
+ After applying any file edits, you must document changes in two places:
404
+
405
+ ### 1. Single Commit Message File (\`.claude-commit-msg\`)
406
+
407
+ **Location**: \`/.claude-commit-msg\`
408
+ **Action**: APPEND to this file (don't overwrite) - the worker will clean it
409
+ **Format**:
410
+ \`\`\`
411
+ type(scope): subject line describing the change (max 72 characters)
412
+
413
+ - Bullet point 1: Specific file or module changed and what was done
414
+ - Bullet point 2: Behavioral change or feature added (if applicable)
415
+ - Bullet point 3: Any side effects or related updates (if applicable)
416
+ \`\`\`
417
+
418
+ **Commit Types**:
419
+ - \`feat\`: New feature or capability added
420
+ - \`fix\`: Bug fix or error correction
421
+ - \`refactor\`: Code restructuring without changing functionality
422
+ - \`docs\`: Documentation updates (README, comments, etc.)
423
+ - \`test\`: Adding or modifying tests
424
+ - \`chore\`: Maintenance tasks (configs, dependencies, cleanup)
425
+
426
+ **Rules**:
427
+ - Be specific about WHAT changed and WHERE (mention file names)
428
+ - Describe the IMPACT of the change, not just what was done
429
+ - Never include bash commands or command-line syntax
430
+ - Never attempt to run git commands directly
431
+ - Keep the subject line under 72 characters
432
+ - Use present tense ("add" not "added", "fix" not "fixed")
433
+
434
+ ### 2. Changelog Documentation (\`CLAUDE_CHANGELOG.md\`)
435
+ **Location**: \`/CLAUDE_CHANGELOG.md\`
436
+ **Action**: APPEND a new section (don't overwrite)
437
+ **Format**:
438
+ \`\`\`markdown
439
+ ## YYYY-MM-DDTHH:MM:SSZ
440
+ type(scope): exact same subject line from commit message
441
+ - Exact same bullet point 1 from commit message
442
+ - Exact same bullet point 2 from commit message
443
+ - Exact same bullet point 3 from commit message (if used)
444
+ \`\`\`
445
+
446
+ **Timestamp Format**: ISO-8601 with timezone (Z for UTC)
447
+ - Example: \`2025-09-15T14:30:00Z\`
448
+ - Use current time when making the change
449
+
450
+ ### Example of Both Files
451
+
452
+ **.claude-commit-msg** (append new entries):
453
+ \`\`\`
454
+ feat(api): add webhook support for real-time notifications
455
+
456
+ - Created WebhookManager class in services/webhook_manager.py
457
+ - Added POST /api/webhooks endpoint for webhook registration
458
+ - Integrated webhook triggers into event processing pipeline
459
+ \`\`\`
460
+
461
+ **CLAUDE_CHANGELOG.md** (appended):
462
+ \`\`\`markdown
463
+ ## 2025-09-15T14:35:00Z
464
+ feat(api): add webhook support for real-time notifications
465
+ - Created WebhookManager class in services/webhook_manager.py
466
+ - Added POST /api/webhooks endpoint for webhook registration
467
+ - Integrated webhook triggers into event processing pipeline
468
+ \`\`\`
469
+
470
+ ## DevOps Agent Worker
471
+ The cs-devops-agent worker is configured to:
472
+ - Use branch prefix: dev_${initials}_
473
+ - Create daily branches: dev_${initials}_YYYY-MM-DD
474
+ - Auto-commit when .claude-commit-msg changes
475
+ - Handle daily rollover at midnight
476
+ - Automatically stage, commit, and push changes
477
+ - Clear commit message after successful push
478
+
479
+ ## Code Quality & Documentation Standards
480
+
481
+ ### BE A THOUGHTFUL ENGINEER
482
+ Write code that your future self and others can understand easily.
483
+
484
+ ### 1. Module/File Headers
485
+ Every file should start with a comprehensive description:
486
+ \`\`\`javascript
487
+ /**
488
+ * Module Name - Brief Description
489
+ * ================================
490
+ *
491
+ * This module handles [main purpose]. It provides [key functionality].
492
+ *
493
+ * Key Components:
494
+ * - ComponentA: Does X
495
+ * - ComponentB: Handles Y
496
+ *
497
+ * Dependencies:
498
+ * - External: library1, library2
499
+ * - Internal: module.submodule
500
+ *
501
+ * Usage Example:
502
+ * import { MainClass } from './this-module';
503
+ * const instance = new MainClass();
504
+ * const result = instance.process();
505
+ */
506
+ \`\`\`
507
+
508
+ ### 2. Function/Method Documentation
509
+ \`\`\`javascript
510
+ /**
511
+ * Execute a named process with the provided input.
512
+ *
513
+ * This method runs through each step sequentially,
514
+ * passing output from one step as input to the next.
515
+ *
516
+ * @param {string} processName - Name of process to execute
517
+ * @param {string} inputText - Initial input text to process
518
+ * @param {Object} [context] - Optional context for variable substitution
519
+ * @returns {ProcessResult} Object containing success status and outputs
520
+ * @throws {Error} If processName doesn't exist
521
+ * @throws {ConnectionError} If service is unavailable
522
+ *
523
+ * @example
524
+ * const result = await processor.execute("validate", "input data");
525
+ * if (result.success) {
526
+ * console.log(result.output);
527
+ * }
528
+ */
529
+ async function executeProcess(processName, inputText, context) {
530
+ // Step 1: Validate process exists
531
+ if (!processes[processName]) {
532
+ throw new Error(\`Process '\${processName}' not found\`);
533
+ }
534
+
535
+ // Step 2: Initialize execution context
536
+ // This tracks state across all process steps
537
+ const executionContext = initContext(context);
538
+
539
+ // Step 3: Execute each step sequentially
540
+ // Note: Future versions will support parallel execution
541
+ for (const step of process.steps) {
542
+ // Process step with automatic retry on failure
543
+ const stepResult = await executeStep(step, executionContext);
544
+ // ...
545
+ }
546
+ }
547
+ \`\`\`
548
+
549
+ ### 3. Inline Comments
550
+ \`\`\`javascript
551
+ // Good inline comments explain WHY, not WHAT
552
+ const retryDelay = 1000; // Wait 1 second between retries to avoid rate limiting
553
+
554
+ // Document complex logic
555
+ if (mode === 'production') {
556
+ // Force secure connections in production
557
+ // This ensures data privacy for sensitive operations
558
+ options.secure = true;
559
+ } else if (mode === 'development') {
560
+ // Allow insecure connections for local testing
561
+ // Speeds up development workflow
562
+ options.secure = false;
563
+ }
564
+
565
+ // Explain non-obvious code
566
+ // Using exponential backoff to avoid overwhelming the API
567
+ const waitTime = Math.min(2 ** attempt * 0.5, 30); // Cap at 30 seconds
568
+ \`\`\`
569
+
570
+ ### 4. TODO/FIXME Comments
571
+ \`\`\`javascript
572
+ // TODO(${initials}, YYYY-MM-DD): Implement caching for frequent requests
573
+ // This would reduce API calls by ~40% based on usage patterns
574
+
575
+ // FIXME(${initials}, YYYY-MM-DD): Handle edge case when input is null
576
+ // Current behavior: Throws unhandled exception
577
+ // Desired: Return graceful error message
578
+ \`\`\`
579
+
580
+ ### 5. Configuration & Constants
581
+ \`\`\`javascript
582
+ // Configuration constants should explain their purpose and valid ranges
583
+ const MAX_RETRIES = 3; // Maximum retry attempts for failed operations
584
+ const TIMEOUT_MS = 30000; // Request timeout in milliseconds (30 seconds)
585
+ const BATCH_SIZE = 100; // Process items in batches to manage memory
586
+
587
+ // Document configuration structures
588
+ const MODES = {
589
+ 'fast': 'Prioritize speed over accuracy',
590
+ 'balanced': 'Balance between speed and accuracy',
591
+ 'accurate': 'Prioritize accuracy over speed'
592
+ };
593
+ \`\`\`
594
+
595
+ ### 6. Error Handling Comments
596
+ \`\`\`javascript
597
+ try {
598
+ const response = await apiClient.request(endpoint);
599
+ } catch (error) {
600
+ if (error.code === 'ECONNREFUSED') {
601
+ // Service might be starting up or under maintenance
602
+ // Retry with exponential backoff before failing
603
+ logger.warn(\`Service unavailable: \${error.message}\`);
604
+ return await retryWithBackoff(request);
605
+ } else if (error.code === 'INVALID_INPUT') {
606
+ // Invalid input is unrecoverable - notify user
607
+ logger.error(\`Invalid input: \${error.message}\`);
608
+ throw error;
609
+ }
610
+ }
611
+ \`\`\`
612
+
613
+ ## Code Quality Standards
614
+ - Ensure all changes maintain existing code style and conventions
615
+ - Add appropriate error handling for new functionality
616
+ - Update related documentation when changing functionality
617
+ - Write self-documenting code with clear variable and function names
618
+ - Add JSDoc comments for all public functions and classes
619
+ - Comment complex algorithms and business logic
620
+ - Document assumptions and constraints
621
+ - Include examples in function documentation
622
+
623
+ ## File Organization
624
+ - Use descriptive file names that reflect their purpose
625
+ - Group related changes in a single commit when they're part of the same feature
626
+ - If making multiple unrelated changes, document them separately
627
+ - Keep files focused on a single responsibility
628
+ - Create new files rather than making existing files too large
629
+
630
+ ## Testing Guidelines
631
+ - Write tests for new functionality
632
+ - Update tests when modifying existing functionality
633
+ - Ensure all tests pass before committing
634
+ - Document test scenarios and expected outcomes
635
+ - Include edge cases in test coverage
636
+
637
+ ## Communication
638
+ - When asked about changes, reference the CLAUDE_CHANGELOG.md for history
639
+ - Provide context about why changes were made, not just what was changed
640
+ - Alert user to any breaking changes or required migrations
641
+ - Be clear about dependencies and prerequisites
642
+
643
+ ## Image and Asset Creation
644
+ - **NEVER create or generate images without explicit user permission**
645
+ - Always ask before creating any image files (PNG, JPG, SVG, etc.)
646
+ - Do not automatically generate placeholder images or logos
647
+ - Wait for specific user request before creating visual assets
648
+
649
+ ## Version Control Best Practices
650
+ - Make atomic commits - each commit should represent one logical change
651
+ - Write meaningful commit messages following the conventional format
652
+ - Review changes before committing to ensure quality
653
+ - Don't commit commented-out code - remove it or document why it's kept
654
+ - Keep commits focused and avoid mixing unrelated changes
655
+
656
+ ## Security Considerations
657
+ - Never commit sensitive information (passwords, API keys, tokens)
658
+ - Use environment variables for configuration that varies by environment
659
+ - Validate all user input before processing
660
+ - Follow the principle of least privilege
661
+ - Document security considerations in code comments
662
+
663
+ ## Performance Guidelines
664
+ - Comment on performance implications of algorithms
665
+ - Document O(n) complexity for non-trivial algorithms
666
+ - Explain caching strategies where implemented
667
+ - Note potential bottlenecks or scaling concerns
668
+ - Include performance considerations in technical decisions
669
+ `;
670
+ fs.writeFileSync(claudeMdPath, claudeRules);
671
+ log.success('Created CLAUDE.md with house rules');
672
+ } else {
673
+ log.info('CLAUDE.md already exists');
674
+ }
675
+
676
+ // Update .gitignore
677
+ const gitignorePath = path.join(projectRoot, '.gitignore');
678
+ if (fs.existsSync(gitignorePath)) {
679
+ let gitignore = fs.readFileSync(gitignorePath, 'utf8');
680
+
681
+ // Check if entries already exist
682
+ const entriesToAdd = [
683
+ '.claude-commit-msg',
684
+ '**/Archive/',
685
+ '*.backup.*'
686
+ ];
687
+
688
+ let modified = false;
689
+ for (const entry of entriesToAdd) {
690
+ if (!gitignore.includes(entry)) {
691
+ gitignore += `\n${entry}`;
692
+ modified = true;
693
+ }
694
+ }
695
+
696
+ if (modified) {
697
+ gitignore += '\n';
698
+ fs.writeFileSync(gitignorePath, gitignore);
699
+ log.success('Updated .gitignore');
700
+ }
701
+ }
702
+ }
703
+
704
+ function createRunScripts(projectRoot, initials, packageJson) {
705
+ log.header();
706
+ log.title('🎯 Creating Run Scripts');
707
+
708
+ // Update package.json scripts
709
+ packageJson.scripts = packageJson.scripts || {};
710
+ packageJson.scripts['cs-devops-agent'] = 'node ScriptCS_DevOpsAgent/cs-devops-agent-worker.js';
711
+ packageJson.scripts['cs-devops-agent:debug'] = 'AC_DEBUG=true node ScriptCS_DevOpsAgent/cs-devops-agent-worker.js';
712
+ packageJson.scripts['cs-devops-agent:setup'] = 'node ScriptCS_DevOpsAgent/setup-cs-devops-agent.js';
713
+
714
+ const packageJsonPath = path.join(projectRoot, 'package.json');
715
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
716
+ log.success('Updated package.json scripts');
717
+
718
+ // Create a personalized shell script
719
+ const scriptContent = `#!/bin/bash
720
+ # DevOps Agent Worker Runner for ${initials.toUpperCase()}
721
+ # Generated on ${new Date().toISOString()}
722
+
723
+ echo "🚀 Starting DevOps Agent Worker"
724
+ echo "Developer: ${initials.toUpperCase()}"
725
+ echo "Branch Prefix: dev_${initials}_"
726
+ echo ""
727
+
728
+ # Export environment variables
729
+ export AC_BRANCH_PREFIX="dev_${initials}_"
730
+ export AC_DAILY_PREFIX="dev_${initials}_"
731
+ export AC_TZ="Asia/Dubai"
732
+ export AC_PUSH="true"
733
+ export AC_REQUIRE_MSG="true"
734
+ export AC_MSG_MIN_BYTES="20"
735
+ export AC_DEBOUNCE_MS="1500"
736
+ export AC_MSG_DEBOUNCE_MS="3000"
737
+ export AC_CLEAR_MSG_WHEN="push"
738
+ export AC_ROLLOVER_PROMPT="true"
739
+ export AC_DEBUG="false"
740
+
741
+ # Check for debug flag
742
+ if [ "$1" == "--debug" ] || [ "$1" == "-d" ]; then
743
+ export AC_DEBUG="true"
744
+ echo "🐛 Debug mode enabled"
745
+ fi
746
+
747
+ # Check for no-push flag
748
+ if [ "$1" == "--no-push" ] || [ "$1" == "-n" ]; then
749
+ export AC_PUSH="false"
750
+ echo "📦 Push disabled (local commits only)"
751
+ fi
752
+
753
+ # Run the cs-devops-agent worker
754
+ node ScriptCS_DevOpsAgent/cs-devops-agent-worker.js
755
+ `;
756
+
757
+ const scriptPath = path.join(projectRoot, `run-cs-devops-agent-${initials}.sh`);
758
+ fs.writeFileSync(scriptPath, scriptContent);
759
+ fs.chmodSync(scriptPath, '755');
760
+ log.success(`Created personalized run script: run-cs-devops-agent-${initials}.sh`);
761
+
762
+ // Create a .env.example file
763
+ const envExampleContent = `# DevOps Agent Worker Configuration
764
+ # Copy to .env and customize as needed
765
+
766
+ # Developer Settings
767
+ AC_BRANCH_PREFIX=dev_${initials}_
768
+ AC_DAILY_PREFIX=dev_${initials}_
769
+
770
+ # Timezone (for daily branch creation)
771
+ AC_TZ=Asia/Dubai
772
+
773
+ # Git Settings
774
+ AC_PUSH=true
775
+
776
+ # Message Requirements
777
+ AC_REQUIRE_MSG=true
778
+ AC_MSG_MIN_BYTES=20
779
+ AC_MSG_PATTERN=^(feat|fix|refactor|docs|test|chore)(\\([^)]+\\))?:\\s
780
+
781
+ # Timing Settings
782
+ AC_DEBOUNCE_MS=1500
783
+ AC_MSG_DEBOUNCE_MS=3000
784
+
785
+ # Behavior
786
+ AC_CLEAR_MSG_WHEN=push
787
+ AC_ROLLOVER_PROMPT=true
788
+ AC_DEBUG=false
789
+ `;
790
+
791
+ const envExamplePath = path.join(projectRoot, '.env.example');
792
+ fs.writeFileSync(envExamplePath, envExampleContent);
793
+ log.success('Created .env.example file');
794
+ }
795
+
796
+ function printInstructions(initials) {
797
+ log.header();
798
+ log.title('✅ Setup Complete!');
799
+ console.log('');
800
+ log.info(`Developer Initials: ${colors.bright}${initials.toUpperCase()}${colors.reset}`);
801
+ log.info(`Branch Prefix: ${colors.bright}dev_${initials}_${colors.reset}`);
802
+ console.log('');
803
+
804
+ log.title('📚 Quick Start Guide:');
805
+ console.log('');
806
+ console.log('1. Start the cs-devops-agent worker:');
807
+ console.log(` ${colors.green}npm run cs-devops-agent${colors.reset}`);
808
+ console.log(` ${colors.yellow}OR${colors.reset}`);
809
+ console.log(` ${colors.green}./run-cs-devops-agent-${initials}.sh${colors.reset}`);
810
+ console.log(` ${colors.yellow}OR${colors.reset}`);
811
+ console.log(` ${colors.green}Use VS Code: Cmd+Shift+P → Tasks: Run Task → 🚀 Start DevOps Agent Worker${colors.reset}`);
812
+ console.log('');
813
+
814
+ console.log('2. Make your code changes');
815
+ console.log('');
816
+
817
+ console.log('3. Create a commit message:');
818
+ console.log(` ${colors.green}echo "feat(module): description" > .claude-commit-msg${colors.reset}`);
819
+ console.log(` ${colors.yellow}OR${colors.reset}`);
820
+ console.log(` ${colors.green}Use VS Code: Cmd+Shift+P → Tasks: Run Task → 📝 Create Commit Message${colors.reset}`);
821
+ console.log('');
822
+
823
+ console.log('4. The worker will automatically commit and push!');
824
+ console.log('');
825
+
826
+ log.title('🎯 Daily Workflow:');
827
+ console.log('');
828
+ console.log(`• Your daily branches will be: ${colors.bright}dev_${initials}_YYYY-MM-DD${colors.reset}`);
829
+ console.log('• The worker handles day rollover automatically');
830
+ console.log('• Commits require valid conventional format (feat/fix/docs/etc)');
831
+ console.log('• Message file is cleared after successful push');
832
+ console.log('');
833
+
834
+ log.title('📁 Files Created/Updated:');
835
+ console.log('');
836
+ console.log('• .vscode/settings.json - VS Code environment settings');
837
+ console.log('• .vscode/tasks.json - VS Code task shortcuts');
838
+ console.log('• package.json - NPM scripts');
839
+ console.log(`• run-cs-devops-agent-${initials}.sh - Personal run script`);
840
+ console.log('• .claude-commit-msg - Commit message file');
841
+ console.log('• CLAUDE_CHANGELOG.md - Change tracking');
842
+ console.log('• CLAUDE.md - House rules for Claude');
843
+ console.log('• .env.example - Configuration template');
844
+ console.log('');
845
+
846
+ log.title('🔧 Debugging:');
847
+ console.log('');
848
+ console.log('Run with debug output:');
849
+ console.log(` ${colors.green}npm run cs-devops-agent:debug${colors.reset}`);
850
+ console.log(` ${colors.yellow}OR${colors.reset}`);
851
+ console.log(` ${colors.green}./run-cs-devops-agent-${initials}.sh --debug${colors.reset}`);
852
+ console.log('');
853
+
854
+ log.title('📖 Environment Variables:');
855
+ console.log('');
856
+ console.log('See .env.example for all configuration options');
857
+ console.log('');
858
+
859
+ log.header();
860
+ }
861
+
862
+ // ============================================================================
863
+ // CLEANUP FUNCTIONS
864
+ // ============================================================================
865
+
866
+ function cleanupDevOpsAgentFiles(projectRoot) {
867
+ log.header();
868
+ log.title('🧹 Cleaning Up DevOpsAgent Files');
869
+
870
+ const scriptsDir = path.join(projectRoot, 'ScriptCS_DevOpsAgent');
871
+
872
+ // Check if this is a submodule deployment
873
+ const isSubmodule = fs.existsSync(path.join(scriptsDir, '.git'));
874
+ const devOpsAgentRoot = isSubmodule ? scriptsDir : path.join(projectRoot, 'DevOpsAgent');
875
+
876
+ if (!fs.existsSync(devOpsAgentRoot)) {
877
+ log.info('No DevOpsAgent directory to clean up');
878
+ return;
879
+ }
880
+
881
+ // Files to rename/archive in the DevOpsAgent folder
882
+ const filesToRename = [
883
+ { source: 'CLAUDE.md', target: 'CLAUDE.md.template' },
884
+ { source: 'houserules.md', target: 'houserules.md.template' },
885
+ { source: 'package.json', target: 'package.json.template' },
886
+ { source: '.env.example', target: '.env.template' }
887
+ ];
888
+
889
+ for (const file of filesToRename) {
890
+ const sourcePath = path.join(devOpsAgentRoot, file.source);
891
+ const targetPath = path.join(devOpsAgentRoot, file.target);
892
+
893
+ if (fs.existsSync(sourcePath) && !fs.existsSync(targetPath)) {
894
+ try {
895
+ fs.renameSync(sourcePath, targetPath);
896
+ log.success(`Renamed ${file.source} to ${file.target} in DevOpsAgent folder`);
897
+ } catch (error) {
898
+ log.warn(`Could not rename ${file.source}: ${error.message}`);
899
+ }
900
+ }
901
+ }
902
+
903
+ // Create a .gitignore in DevOpsAgent to ignore deployed files
904
+ const devOpsGitignore = path.join(devOpsAgentRoot, '.gitignore');
905
+ if (!fs.existsSync(devOpsGitignore)) {
906
+ const ignoreContent = `# Ignore deployed files to avoid conflicts\n*.deployed\n.env\nnode_modules/\n`;
907
+ fs.writeFileSync(devOpsGitignore, ignoreContent);
908
+ log.success('Created .gitignore in DevOpsAgent folder');
909
+ }
910
+ }
911
+
912
+ // ============================================================================
913
+ // MAIN SETUP FLOW
914
+ // ============================================================================
915
+
916
+ async function main() {
917
+ console.clear();
918
+ log.header();
919
+ console.log(colors.bright + ' DEVOPS-AGENT WORKER SETUP WIZARD' + colors.reset);
920
+ log.header();
921
+ console.log('');
922
+ log.info('This wizard will configure the cs-devops-agent system for you.');
923
+ console.log('');
924
+
925
+ // Find project root
926
+ const projectRoot = findProjectRoot();
927
+ log.info(`Project root: ${projectRoot}`);
928
+
929
+ // Ensure ScriptCS_DevOpsAgent directory exists
930
+ const scriptsDir = path.join(projectRoot, 'ScriptCS_DevOpsAgent');
931
+ if (!fs.existsSync(scriptsDir)) {
932
+ log.error('ScriptCS_DevOpsAgent folder not found! Please copy the folder to your project root.');
933
+ log.info('Run: cp -r ScriptCS_DevOpsAgent /path/to/your/project/');
934
+ process.exit(1);
935
+ }
936
+
937
+ // Get developer initials
938
+ let initials = null;
939
+ while (!initials) {
940
+ const input = await prompt('\n👤 Enter your 3-letter initials (e.g., abc): ');
941
+ initials = validateInitials(input);
942
+
943
+ if (!initials) {
944
+ log.error('Please enter exactly 3 letters (a-z)');
945
+ }
946
+ }
947
+
948
+ log.success(`Using initials: ${initials}`);
949
+
950
+ // Confirm before proceeding
951
+ const proceed = await prompt(`\n📋 This will configure:\n` +
952
+ ` • Branch prefix: dev_${initials}_\n` +
953
+ ` • Daily branches: dev_${initials}_YYYY-MM-DD\n` +
954
+ ` • VS Code settings and tasks\n` +
955
+ ` • NPM packages and scripts\n\n` +
956
+ `Continue? (y/n): `);
957
+
958
+ if (proceed.toLowerCase() !== 'y' && proceed.toLowerCase() !== 'yes') {
959
+ log.warn('Setup cancelled');
960
+ process.exit(0);
961
+ }
962
+
963
+ try {
964
+ // Run setup steps
965
+ const packageJson = await setupNpmPackages(projectRoot);
966
+ setupVSCodeSettings(projectRoot, initials);
967
+ setupVSCodeTasks(projectRoot, initials);
968
+ setupCommitFiles(projectRoot, initials);
969
+ createRunScripts(projectRoot, initials, packageJson);
970
+
971
+ // Clean up DevOpsAgent files to avoid duplicates
972
+ cleanupDevOpsAgentFiles(projectRoot);
973
+
974
+ // Print instructions
975
+ printInstructions(initials);
976
+
977
+ } catch (error) {
978
+ log.error(`Setup failed: ${error.message}`);
979
+ console.error(error);
980
+ process.exit(1);
981
+ }
982
+ }
983
+
984
+ // Run the setup
985
+ main().catch(console.error);