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.
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/bin/cs-devops-agent +151 -0
- package/cleanup-sessions.sh +70 -0
- package/docs/PROJECT_INFO.md +115 -0
- package/docs/RELEASE_NOTES.md +189 -0
- package/docs/SESSION_MANAGEMENT.md +120 -0
- package/docs/TESTING.md +331 -0
- package/docs/houserules.md +267 -0
- package/docs/infrastructure.md +68 -0
- package/docs/testing-guide.md +224 -0
- package/package.json +68 -0
- package/src/agent-commands.js +211 -0
- package/src/claude-session-manager.js +488 -0
- package/src/close-session.js +316 -0
- package/src/cs-devops-agent-worker.js +1660 -0
- package/src/run-with-agent.js +372 -0
- package/src/session-coordinator.js +1207 -0
- package/src/setup-cs-devops-agent.js +985 -0
- package/src/worktree-manager.js +768 -0
- package/start-devops-session.sh +299 -0
|
@@ -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);
|