s9n-devops-agent 1.2.0 → 1.3.1
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/README.md +210 -56
- package/docs/INSTALLATION_GUIDE.md +366 -0
- package/docs/houserules.md +51 -0
- package/package.json +5 -1
- package/src/cs-devops-agent-worker.js +65 -10
- package/src/display-utils.cjs +350 -0
- package/src/file-coordinator.cjs +356 -0
- package/src/file-monitor-enhanced.cjs +338 -0
- package/src/house-rules-manager.js +443 -0
- package/src/session-coordinator.js +289 -15
- package/src/setup-cs-devops-agent.js +5 -3
- package/start-devops-session.sh +139 -0
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
import path from 'path';
|
|
22
|
-
import {
|
|
22
|
+
import { fileURLToPath } from 'url';
|
|
23
|
+
import { dirname } from 'path';
|
|
24
|
+
import { execSync, spawn, fork } from 'child_process';
|
|
23
25
|
import crypto from 'crypto';
|
|
24
26
|
import readline from 'readline';
|
|
25
27
|
import { hasDockerConfiguration } from './docker-utils.js';
|
|
26
|
-
import crypto from 'crypto';
|
|
27
|
-
import readline from 'readline';
|
|
28
28
|
|
|
29
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
30
30
|
const __dirname = dirname(__filename);
|
|
@@ -461,7 +461,13 @@ class SessionCoordinator {
|
|
|
461
461
|
});
|
|
462
462
|
|
|
463
463
|
console.log(`\n${CONFIG.colors.yellow}═══ Auto-merge Configuration ═══${CONFIG.colors.reset}`);
|
|
464
|
-
console.log(`${CONFIG.colors.dim}
|
|
464
|
+
console.log(`${CONFIG.colors.dim}Automatically merge your daily work branches into a target branch.${CONFIG.colors.reset}`);
|
|
465
|
+
console.log();
|
|
466
|
+
console.log(`${CONFIG.colors.bright}How it works:${CONFIG.colors.reset}`);
|
|
467
|
+
console.log(` • The agent creates dated branches (e.g., ${CONFIG.colors.blue}agent_dev_2025-10-01${CONFIG.colors.reset})`);
|
|
468
|
+
console.log(` • At the end of each day, your work is automatically merged`);
|
|
469
|
+
console.log(` • This keeps your target branch (main/develop) up to date`);
|
|
470
|
+
console.log(` • Prevents accumulation of stale feature branches`);
|
|
465
471
|
|
|
466
472
|
// Ask if they want auto-merge
|
|
467
473
|
const autoMerge = await new Promise((resolve) => {
|
|
@@ -784,6 +790,28 @@ INSTRUCTIONS:
|
|
|
784
790
|
- **Worktree Path:** \`${worktreePath}\`
|
|
785
791
|
- **Branch:** \`${branchName}\`
|
|
786
792
|
|
|
793
|
+
## 🚨 CRITICAL: File Coordination Protocol
|
|
794
|
+
|
|
795
|
+
**BEFORE editing any files, you MUST:**
|
|
796
|
+
|
|
797
|
+
1. **Declare your intent** by creating:
|
|
798
|
+
\`\`\`json
|
|
799
|
+
// File: .file-coordination/active-edits/<agent>-${sessionId}.json
|
|
800
|
+
{
|
|
801
|
+
"agent": "<your-name>",
|
|
802
|
+
"session": "${sessionId}",
|
|
803
|
+
"files": ["list", "files", "to", "edit"],
|
|
804
|
+
"operation": "edit",
|
|
805
|
+
"reason": "${task}",
|
|
806
|
+
"declaredAt": "<ISO-8601-timestamp>",
|
|
807
|
+
"estimatedDuration": 300
|
|
808
|
+
}
|
|
809
|
+
\`\`\`
|
|
810
|
+
|
|
811
|
+
2. **Check for conflicts** - read all files in \`.file-coordination/active-edits/\`
|
|
812
|
+
3. **Only proceed if no conflicts** - wait or choose different files if blocked
|
|
813
|
+
4. **Release files when done** - delete your declaration after edits
|
|
814
|
+
|
|
787
815
|
## Instructions for Claude/Cline
|
|
788
816
|
|
|
789
817
|
### Step 1: Navigate to Your Worktree
|
|
@@ -797,18 +825,25 @@ git branch --show-current
|
|
|
797
825
|
# Should output: ${branchName}
|
|
798
826
|
\`\`\`
|
|
799
827
|
|
|
800
|
-
### Step 3:
|
|
828
|
+
### Step 3: Declare Files Before Editing
|
|
829
|
+
Create your declaration in \`.file-coordination/active-edits/\`
|
|
830
|
+
|
|
831
|
+
### Step 4: Work on Your Task
|
|
801
832
|
Make changes for: **${task}**
|
|
802
833
|
|
|
803
|
-
### Step
|
|
834
|
+
### Step 5: Commit Your Changes
|
|
804
835
|
Write your commit message to the session-specific file:
|
|
805
836
|
\`\`\`bash
|
|
806
837
|
echo "feat: your commit message here" > .devops-commit-${sessionId}.msg
|
|
807
838
|
\`\`\`
|
|
808
839
|
|
|
809
|
-
### Step
|
|
840
|
+
### Step 6: Release Your File Locks
|
|
841
|
+
Delete your declaration from \`.file-coordination/active-edits/\`
|
|
842
|
+
|
|
843
|
+
### Step 7: Automatic Processing
|
|
810
844
|
The DevOps agent will automatically:
|
|
811
845
|
- Detect your changes
|
|
846
|
+
- Check for coordination conflicts
|
|
812
847
|
- Read your commit message
|
|
813
848
|
- Commit and push to the remote repository
|
|
814
849
|
- Clear the message file
|
|
@@ -853,11 +888,21 @@ The DevOps agent will automatically:
|
|
|
853
888
|
console.log(`- Working Directory: ${instructions.worktreePath}`);
|
|
854
889
|
console.log(`- Task: ${task || 'development'}`);
|
|
855
890
|
console.log(``);
|
|
856
|
-
console.log(`
|
|
857
|
-
console.log(`
|
|
891
|
+
console.log(`CRITICAL FIRST STEP:`);
|
|
892
|
+
console.log(`1. Read and follow the house rules: cat "${instructions.worktreePath}/houserules.md"`);
|
|
893
|
+
console.log(`2. Switch to the working directory: cd "${instructions.worktreePath}"`);
|
|
894
|
+
console.log(``);
|
|
895
|
+
console.log(`FILE COORDINATION PROTOCOL (from house rules at ${instructions.worktreePath}/houserules.md):`);
|
|
896
|
+
console.log(`Before editing ANY files, you MUST:`);
|
|
897
|
+
console.log(`- Declare your intent in .file-coordination/active-edits/<agent>-${sessionId}.json`);
|
|
898
|
+
console.log(`- Check for conflicts with other agents`);
|
|
899
|
+
console.log(`- Only edit files you've declared`);
|
|
900
|
+
console.log(`- Release files when done`);
|
|
858
901
|
console.log(``);
|
|
859
902
|
console.log(`Write commit messages to: .devops-commit-${sessionId}.msg`);
|
|
860
903
|
console.log(`The DevOps agent will automatically commit and push changes.`);
|
|
904
|
+
console.log(``);
|
|
905
|
+
console.log(`Remember: ALWAYS check house rules first for the latest protocols!`);
|
|
861
906
|
console.log();
|
|
862
907
|
|
|
863
908
|
console.log(`${CONFIG.colors.yellow}══════════════════════════════════════════════════════════════${CONFIG.colors.reset}`);
|
|
@@ -1185,6 +1230,215 @@ The DevOps agent is monitoring this worktree for changes.
|
|
|
1185
1230
|
|
|
1186
1231
|
return session;
|
|
1187
1232
|
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Close a specific session
|
|
1236
|
+
*/
|
|
1237
|
+
async closeSession(sessionId) {
|
|
1238
|
+
const lockFile = path.join(this.locksPath, `${sessionId}.lock`);
|
|
1239
|
+
|
|
1240
|
+
if (!fs.existsSync(lockFile)) {
|
|
1241
|
+
console.error(`${CONFIG.colors.red}Session not found: ${sessionId}${CONFIG.colors.reset}`);
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
const session = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
|
|
1246
|
+
console.log(`\n${CONFIG.colors.yellow}Closing session: ${sessionId}${CONFIG.colors.reset}`);
|
|
1247
|
+
console.log(`${CONFIG.colors.dim}Task: ${session.task}${CONFIG.colors.reset}`);
|
|
1248
|
+
console.log(`${CONFIG.colors.dim}Branch: ${session.branchName}${CONFIG.colors.reset}`);
|
|
1249
|
+
|
|
1250
|
+
// Kill agent if running
|
|
1251
|
+
if (session.agentPid) {
|
|
1252
|
+
try {
|
|
1253
|
+
process.kill(session.agentPid, 'SIGTERM');
|
|
1254
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Agent process stopped`);
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
// Process might already be dead
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// Check for uncommitted changes
|
|
1261
|
+
if (fs.existsSync(session.worktreePath)) {
|
|
1262
|
+
try {
|
|
1263
|
+
const status = execSync(`git -C "${session.worktreePath}" status --porcelain`, { encoding: 'utf8' });
|
|
1264
|
+
if (status.trim()) {
|
|
1265
|
+
console.log(`\n${CONFIG.colors.yellow}Warning: Uncommitted changes found${CONFIG.colors.reset}`);
|
|
1266
|
+
console.log(status);
|
|
1267
|
+
|
|
1268
|
+
const rl = readline.createInterface({
|
|
1269
|
+
input: process.stdin,
|
|
1270
|
+
output: process.stdout
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
const answer = await new Promise(resolve => {
|
|
1274
|
+
rl.question('Commit these changes before closing? (y/N): ', resolve);
|
|
1275
|
+
});
|
|
1276
|
+
rl.close();
|
|
1277
|
+
|
|
1278
|
+
if (answer.toLowerCase() === 'y') {
|
|
1279
|
+
execSync(`git -C "${session.worktreePath}" add -A`, { stdio: 'pipe' });
|
|
1280
|
+
execSync(`git -C "${session.worktreePath}" commit -m "chore: final session cleanup for ${sessionId}"`, { stdio: 'pipe' });
|
|
1281
|
+
execSync(`git -C "${session.worktreePath}" push origin ${session.branchName}`, { stdio: 'pipe' });
|
|
1282
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Changes committed and pushed`);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
console.log(`${CONFIG.colors.dim}Could not check git status${CONFIG.colors.reset}`);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Ask about removing worktree
|
|
1290
|
+
const rl = readline.createInterface({
|
|
1291
|
+
input: process.stdin,
|
|
1292
|
+
output: process.stdout
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
const removeWorktree = await new Promise(resolve => {
|
|
1296
|
+
rl.question(`\nRemove worktree at ${session.worktreePath}? (Y/n): `, resolve);
|
|
1297
|
+
});
|
|
1298
|
+
rl.close();
|
|
1299
|
+
|
|
1300
|
+
if (removeWorktree.toLowerCase() !== 'n') {
|
|
1301
|
+
try {
|
|
1302
|
+
// Remove worktree
|
|
1303
|
+
execSync(`git worktree remove "${session.worktreePath}" --force`, { stdio: 'pipe' });
|
|
1304
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Worktree removed`);
|
|
1305
|
+
|
|
1306
|
+
// Prune worktree list
|
|
1307
|
+
execSync('git worktree prune', { stdio: 'pipe' });
|
|
1308
|
+
} catch (err) {
|
|
1309
|
+
console.error(`${CONFIG.colors.red}Failed to remove worktree: ${err.message}${CONFIG.colors.reset}`);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// Remove lock file
|
|
1315
|
+
fs.unlinkSync(lockFile);
|
|
1316
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Session closed successfully`);
|
|
1317
|
+
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* Interactive session selection and close
|
|
1323
|
+
*/
|
|
1324
|
+
async selectAndCloseSession() {
|
|
1325
|
+
if (!fs.existsSync(this.locksPath)) {
|
|
1326
|
+
console.log(`${CONFIG.colors.yellow}No active sessions${CONFIG.colors.reset}`);
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
const locks = fs.readdirSync(this.locksPath);
|
|
1331
|
+
if (locks.length === 0) {
|
|
1332
|
+
console.log(`${CONFIG.colors.yellow}No active sessions${CONFIG.colors.reset}`);
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
const sessions = [];
|
|
1337
|
+
locks.forEach(lockFile => {
|
|
1338
|
+
const lockPath = path.join(this.locksPath, lockFile);
|
|
1339
|
+
const session = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
1340
|
+
sessions.push(session);
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
console.log(`\n${CONFIG.colors.bright}Select session to close:${CONFIG.colors.reset}\n`);
|
|
1344
|
+
|
|
1345
|
+
sessions.forEach((session, index) => {
|
|
1346
|
+
const status = session.status === 'active' ?
|
|
1347
|
+
`${CONFIG.colors.green}●${CONFIG.colors.reset}` :
|
|
1348
|
+
`${CONFIG.colors.yellow}○${CONFIG.colors.reset}`;
|
|
1349
|
+
|
|
1350
|
+
console.log(`${status} ${CONFIG.colors.bright}${index + 1})${CONFIG.colors.reset} ${session.sessionId}`);
|
|
1351
|
+
console.log(` Task: ${session.task}`);
|
|
1352
|
+
console.log(` Branch: ${session.branchName}`);
|
|
1353
|
+
console.log(` Created: ${session.created}`);
|
|
1354
|
+
console.log();
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1357
|
+
const rl = readline.createInterface({
|
|
1358
|
+
input: process.stdin,
|
|
1359
|
+
output: process.stdout
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
const answer = await new Promise(resolve => {
|
|
1363
|
+
rl.question(`Select session (1-${sessions.length}) or 'q' to quit: `, resolve);
|
|
1364
|
+
});
|
|
1365
|
+
rl.close();
|
|
1366
|
+
|
|
1367
|
+
if (answer.toLowerCase() === 'q') {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const index = parseInt(answer) - 1;
|
|
1372
|
+
if (index >= 0 && index < sessions.length) {
|
|
1373
|
+
await this.closeSession(sessions[index].sessionId);
|
|
1374
|
+
} else {
|
|
1375
|
+
console.log(`${CONFIG.colors.red}Invalid selection${CONFIG.colors.reset}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Clean up all stale sessions and worktrees
|
|
1381
|
+
*/
|
|
1382
|
+
async cleanupAll() {
|
|
1383
|
+
console.log(`\n${CONFIG.colors.yellow}Cleaning up stale sessions and worktrees...${CONFIG.colors.reset}`);
|
|
1384
|
+
|
|
1385
|
+
// Clean up old lock files (older than 24 hours)
|
|
1386
|
+
const oneDayAgo = Date.now() - 86400000;
|
|
1387
|
+
let cleanedLocks = 0;
|
|
1388
|
+
|
|
1389
|
+
if (fs.existsSync(this.locksPath)) {
|
|
1390
|
+
const locks = fs.readdirSync(this.locksPath);
|
|
1391
|
+
locks.forEach(lockFile => {
|
|
1392
|
+
const lockPath = path.join(this.locksPath, lockFile);
|
|
1393
|
+
const stats = fs.statSync(lockPath);
|
|
1394
|
+
if (stats.mtimeMs < oneDayAgo) {
|
|
1395
|
+
fs.unlinkSync(lockPath);
|
|
1396
|
+
cleanedLocks++;
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (cleanedLocks > 0) {
|
|
1402
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Removed ${cleanedLocks} stale lock files`);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// Prune git worktrees
|
|
1406
|
+
try {
|
|
1407
|
+
execSync('git worktree prune', { stdio: 'pipe' });
|
|
1408
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Pruned git worktrees`);
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
console.log(`${CONFIG.colors.dim}Could not prune worktrees${CONFIG.colors.reset}`);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// Clean up orphaned worktree directories
|
|
1414
|
+
if (fs.existsSync(this.worktreesPath)) {
|
|
1415
|
+
const worktrees = fs.readdirSync(this.worktreesPath);
|
|
1416
|
+
let cleanedWorktrees = 0;
|
|
1417
|
+
|
|
1418
|
+
for (const dir of worktrees) {
|
|
1419
|
+
const worktreePath = path.join(this.worktreesPath, dir);
|
|
1420
|
+
|
|
1421
|
+
// Check if this worktree is still valid
|
|
1422
|
+
try {
|
|
1423
|
+
execSync(`git worktree list | grep "${worktreePath}"`, { stdio: 'pipe' });
|
|
1424
|
+
} catch (err) {
|
|
1425
|
+
// Worktree not in git list, it's orphaned
|
|
1426
|
+
try {
|
|
1427
|
+
fs.rmSync(worktreePath, { recursive: true, force: true });
|
|
1428
|
+
cleanedWorktrees++;
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
console.log(`${CONFIG.colors.dim}Could not remove ${dir}${CONFIG.colors.reset}`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
if (cleanedWorktrees > 0) {
|
|
1436
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Removed ${cleanedWorktrees} orphaned worktree directories`);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
console.log(`${CONFIG.colors.green}✓${CONFIG.colors.reset} Cleanup complete`);
|
|
1441
|
+
}
|
|
1188
1442
|
}
|
|
1189
1443
|
|
|
1190
1444
|
// ============================================================================
|
|
@@ -1260,13 +1514,31 @@ async function main() {
|
|
|
1260
1514
|
break;
|
|
1261
1515
|
}
|
|
1262
1516
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1517
|
+
case 'list': {
|
|
1518
|
+
coordinator.listSessions();
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
case 'close': {
|
|
1523
|
+
// Close a session and clean up
|
|
1524
|
+
const sessionId = args[1];
|
|
1525
|
+
if (sessionId) {
|
|
1526
|
+
await coordinator.closeSession(sessionId);
|
|
1527
|
+
} else {
|
|
1528
|
+
// Interactive selection
|
|
1529
|
+
await coordinator.selectAndCloseSession();
|
|
1266
1530
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1531
|
+
break;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
case 'cleanup': {
|
|
1535
|
+
// Clean up stale sessions and worktrees
|
|
1536
|
+
await coordinator.cleanupAll();
|
|
1537
|
+
break;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
case 'help':
|
|
1541
|
+
default: {
|
|
1270
1542
|
console.log(`
|
|
1271
1543
|
${CONFIG.colors.bright}DevOps Session Coordinator${CONFIG.colors.reset}
|
|
1272
1544
|
|
|
@@ -1279,6 +1551,8 @@ ${CONFIG.colors.blue}Commands:${CONFIG.colors.reset}
|
|
|
1279
1551
|
${CONFIG.colors.green}create-and-start${CONFIG.colors.reset} Create session and start agent (all-in-one)
|
|
1280
1552
|
${CONFIG.colors.green}request [agent]${CONFIG.colors.reset} Request a session (for Claude to call)
|
|
1281
1553
|
${CONFIG.colors.green}list${CONFIG.colors.reset} List all active sessions
|
|
1554
|
+
${CONFIG.colors.green}close [id]${CONFIG.colors.reset} Close session and clean up worktree
|
|
1555
|
+
${CONFIG.colors.green}cleanup${CONFIG.colors.reset} Clean up all stale sessions
|
|
1282
1556
|
${CONFIG.colors.green}help${CONFIG.colors.reset} Show this help
|
|
1283
1557
|
|
|
1284
1558
|
${CONFIG.colors.blue}Options:${CONFIG.colors.reset}
|
|
@@ -735,7 +735,8 @@ export AC_MSG_MIN_BYTES="20"
|
|
|
735
735
|
export AC_DEBOUNCE_MS="1500"
|
|
736
736
|
export AC_MSG_DEBOUNCE_MS="3000"
|
|
737
737
|
export AC_CLEAR_MSG_WHEN="push"
|
|
738
|
-
|
|
738
|
+
# Daily rollover is automatic - no prompting needed
|
|
739
|
+
export AC_ROLLOVER_PROMPT="false"
|
|
739
740
|
export AC_DEBUG="false"
|
|
740
741
|
|
|
741
742
|
# Check for debug flag
|
|
@@ -784,7 +785,8 @@ AC_MSG_DEBOUNCE_MS=3000
|
|
|
784
785
|
|
|
785
786
|
# Behavior
|
|
786
787
|
AC_CLEAR_MSG_WHEN=push
|
|
787
|
-
|
|
788
|
+
# Daily rollover is automatic
|
|
789
|
+
AC_ROLLOVER_PROMPT=false
|
|
788
790
|
AC_DEBUG=false
|
|
789
791
|
`;
|
|
790
792
|
|
|
@@ -826,7 +828,7 @@ function printInstructions(initials) {
|
|
|
826
828
|
log.title('🎯 Daily Workflow:');
|
|
827
829
|
console.log('');
|
|
828
830
|
console.log(`• Your daily branches will be: ${colors.bright}dev_${initials}_YYYY-MM-DD${colors.reset}`);
|
|
829
|
-
console.log('• The worker
|
|
831
|
+
console.log('• The worker automatically creates new daily branches at midnight');
|
|
830
832
|
console.log('• Commits require valid conventional format (feat/fix/docs/etc)');
|
|
831
833
|
console.log('• Message file is cleared after successful push');
|
|
832
834
|
console.log('');
|
package/start-devops-session.sh
CHANGED
|
@@ -237,6 +237,142 @@ select_session() {
|
|
|
237
237
|
fi
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
# Function to setup house rules and file coordination
|
|
241
|
+
setup_house_rules() {
|
|
242
|
+
local ROOT="$1"
|
|
243
|
+
local COORD_DIR="$ROOT/.file-coordination"
|
|
244
|
+
|
|
245
|
+
# Check if we need to update existing house rules (even if coordination is set up)
|
|
246
|
+
if [[ -f "$SCRIPT_DIR/src/house-rules-manager.js" ]]; then
|
|
247
|
+
# Check status of house rules
|
|
248
|
+
local STATUS=$(node "$SCRIPT_DIR/src/house-rules-manager.js" status 2>/dev/null || echo '{"needsUpdate": false, "exists": true}')
|
|
249
|
+
local NEEDS_UPDATE=$(echo "$STATUS" | grep -o '"needsUpdate"[[:space:]]*:[[:space:]]*true' || echo "")
|
|
250
|
+
local EXISTS=$(echo "$STATUS" | grep -o '"exists"[[:space:]]*:[[:space:]]*true' || echo "")
|
|
251
|
+
|
|
252
|
+
# Check if house rules were deleted (coordination exists but house rules don't)
|
|
253
|
+
if [[ -d "$COORD_DIR" ]] && [[ -z "$EXISTS" ]]; then
|
|
254
|
+
echo -e "${YELLOW}⚠ House rules file appears to be missing!${NC}"
|
|
255
|
+
echo "The file coordination system is set up, but house rules are gone."
|
|
256
|
+
echo
|
|
257
|
+
echo -n "Recreate house rules? (Y/n): "
|
|
258
|
+
read RECREATE
|
|
259
|
+
if [[ "${RECREATE}" != "n" ]] && [[ "${RECREATE}" != "N" ]]; then
|
|
260
|
+
echo -e "${BLUE}Recreating house rules...${NC}"
|
|
261
|
+
node "$SCRIPT_DIR/src/house-rules-manager.js" update 2>/dev/null
|
|
262
|
+
echo -e "${GREEN}✓ House rules recreated!${NC}"
|
|
263
|
+
echo
|
|
264
|
+
fi
|
|
265
|
+
elif [[ -n "$NEEDS_UPDATE" ]]; then
|
|
266
|
+
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
267
|
+
echo -e "${BOLD}House Rules Update Available${NC}"
|
|
268
|
+
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
269
|
+
echo
|
|
270
|
+
echo "The DevOps Agent has updated house rules sections."
|
|
271
|
+
echo "Your custom rules will be preserved."
|
|
272
|
+
echo
|
|
273
|
+
echo -n "Update house rules now? (Y/n): "
|
|
274
|
+
read UPDATE_CHOICE
|
|
275
|
+
|
|
276
|
+
if [[ "${UPDATE_CHOICE}" != "n" ]] && [[ "${UPDATE_CHOICE}" != "N" ]]; then
|
|
277
|
+
echo -e "${BLUE}Updating house rules...${NC}"
|
|
278
|
+
node "$SCRIPT_DIR/src/house-rules-manager.js" update
|
|
279
|
+
echo -e "${GREEN}✓ House rules updated!${NC}"
|
|
280
|
+
echo
|
|
281
|
+
fi
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# Check if coordination system is already set up
|
|
286
|
+
if [[ -d "$COORD_DIR" ]] && [[ -f "$ROOT/check-file-availability.sh" ]]; then
|
|
287
|
+
return 0 # Already set up
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
|
291
|
+
echo -e "${BOLD}First-time Setup: House Rules & File Coordination${NC}"
|
|
292
|
+
echo -e "${YELLOW}═══════════════════════════════════════════════════════════${NC}"
|
|
293
|
+
echo
|
|
294
|
+
echo "House rules help AI agents understand your project conventions and"
|
|
295
|
+
echo "prevent conflicts when multiple agents work on the same codebase."
|
|
296
|
+
echo
|
|
297
|
+
|
|
298
|
+
# Check for existing house rules
|
|
299
|
+
local HOUSERULES_PATH=""
|
|
300
|
+
local HOUSERULES_FOUND=false
|
|
301
|
+
|
|
302
|
+
if [[ -f "$ROOT/houserules.md" ]]; then
|
|
303
|
+
HOUSERULES_PATH="$ROOT/houserules.md"
|
|
304
|
+
HOUSERULES_FOUND=true
|
|
305
|
+
echo -e "${GREEN}✓${NC} Found existing house rules at: houserules.md"
|
|
306
|
+
elif [[ -f "$ROOT/HOUSERULES.md" ]]; then
|
|
307
|
+
HOUSERULES_PATH="$ROOT/HOUSERULES.md"
|
|
308
|
+
HOUSERULES_FOUND=true
|
|
309
|
+
echo -e "${GREEN}✓${NC} Found existing house rules at: HOUSERULES.md"
|
|
310
|
+
elif [[ -f "$ROOT/.github/HOUSERULES.md" ]]; then
|
|
311
|
+
HOUSERULES_PATH="$ROOT/.github/HOUSERULES.md"
|
|
312
|
+
HOUSERULES_FOUND=true
|
|
313
|
+
echo -e "${GREEN}✓${NC} Found existing house rules at: .github/HOUSERULES.md"
|
|
314
|
+
elif [[ -f "$ROOT/docs/houserules.md" ]]; then
|
|
315
|
+
HOUSERULES_PATH="$ROOT/docs/houserules.md"
|
|
316
|
+
HOUSERULES_FOUND=true
|
|
317
|
+
echo -e "${GREEN}✓${NC} Found existing house rules at: docs/houserules.md"
|
|
318
|
+
else
|
|
319
|
+
echo "No existing house rules found."
|
|
320
|
+
echo
|
|
321
|
+
echo "Would you like to:"
|
|
322
|
+
echo " ${BOLD}1)${NC} Create comprehensive house rules (recommended)"
|
|
323
|
+
echo " ${BOLD}2)${NC} Specify path to existing house rules"
|
|
324
|
+
echo " ${BOLD}3)${NC} Skip for now"
|
|
325
|
+
echo
|
|
326
|
+
echo -n "Your choice [1]: "
|
|
327
|
+
read CHOICE
|
|
328
|
+
|
|
329
|
+
case "${CHOICE:-1}" in
|
|
330
|
+
1)
|
|
331
|
+
HOUSERULES_PATH="$ROOT/houserules.md"
|
|
332
|
+
HOUSERULES_FOUND=false
|
|
333
|
+
echo -e "${GREEN}✓${NC} Will create comprehensive house rules at: houserules.md"
|
|
334
|
+
;;
|
|
335
|
+
2)
|
|
336
|
+
echo -n "Enter path to your house rules (relative to $ROOT): "
|
|
337
|
+
read CUSTOM_PATH
|
|
338
|
+
if [[ -f "$ROOT/$CUSTOM_PATH" ]]; then
|
|
339
|
+
HOUSERULES_PATH="$ROOT/$CUSTOM_PATH"
|
|
340
|
+
HOUSERULES_FOUND=true
|
|
341
|
+
echo -e "${GREEN}✓${NC} Using house rules at: $CUSTOM_PATH"
|
|
342
|
+
else
|
|
343
|
+
echo -e "${YELLOW}File not found. Creating new house rules at: houserules.md${NC}"
|
|
344
|
+
HOUSERULES_PATH="$ROOT/houserules.md"
|
|
345
|
+
HOUSERULES_FOUND=false
|
|
346
|
+
fi
|
|
347
|
+
;;
|
|
348
|
+
3)
|
|
349
|
+
echo -e "${YELLOW}⚠ Skipping house rules setup${NC}"
|
|
350
|
+
echo "You can set them up later by running: ./scripts/setup-file-coordination.sh"
|
|
351
|
+
return 0
|
|
352
|
+
;;
|
|
353
|
+
esac
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
echo
|
|
357
|
+
echo -e "${BLUE}Setting up file coordination system...${NC}"
|
|
358
|
+
|
|
359
|
+
# Run the actual setup inline (simplified version)
|
|
360
|
+
if [[ -f "$SCRIPT_DIR/scripts/setup-file-coordination.sh" ]]; then
|
|
361
|
+
bash "$SCRIPT_DIR/scripts/setup-file-coordination.sh"
|
|
362
|
+
else
|
|
363
|
+
# Inline setup if script doesn't exist
|
|
364
|
+
mkdir -p "$COORD_DIR/active-edits" "$COORD_DIR/completed-edits"
|
|
365
|
+
echo -e "${GREEN}✓${NC} File coordination directories created"
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
echo
|
|
369
|
+
echo -e "${GREEN}✓ Setup complete!${NC} AI agents will now follow house rules and coordinate file edits."
|
|
370
|
+
echo
|
|
371
|
+
echo -e "${DIM}Press Enter to continue...${NC}"
|
|
372
|
+
read -r
|
|
373
|
+
echo
|
|
374
|
+
}
|
|
375
|
+
|
|
240
376
|
# Main function
|
|
241
377
|
main() {
|
|
242
378
|
# Show copyright first
|
|
@@ -256,6 +392,9 @@ main() {
|
|
|
256
392
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
257
393
|
cd "$REPO_ROOT"
|
|
258
394
|
|
|
395
|
+
# Check and setup house rules on first run
|
|
396
|
+
setup_house_rules "$REPO_ROOT"
|
|
397
|
+
|
|
259
398
|
echo -e "${BOLD}Welcome to DevOps Agent Session Manager${NC}"
|
|
260
399
|
echo
|
|
261
400
|
echo "This tool will:"
|