@accelerationguy/accel 1.0.1 → 1.0.2
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/bin/install.js +244 -0
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -489,6 +489,234 @@ function deregisterHooks() {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
+
// ─── User Data Paths (preserved during updates) ─────────────
|
|
493
|
+
const USER_DATA_PATTERNS = [
|
|
494
|
+
// Vector user config
|
|
495
|
+
'modules/vector/vector.json',
|
|
496
|
+
'modules/vector/.vector-template/vector.json',
|
|
497
|
+
'modules/vector/sessions/',
|
|
498
|
+
// Momentum workspace data
|
|
499
|
+
'modules/momentum/data/',
|
|
500
|
+
'modules/momentum/workspace.json',
|
|
501
|
+
'modules/momentum/operator.json',
|
|
502
|
+
// Drive project state
|
|
503
|
+
'modules/drive/.drive/',
|
|
504
|
+
// Event bus (user's events and archive)
|
|
505
|
+
'events/',
|
|
506
|
+
];
|
|
507
|
+
|
|
508
|
+
function isUserData(relativePath) {
|
|
509
|
+
return USER_DATA_PATTERNS.some((pattern) => relativePath.startsWith(pattern));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ─── Backup ─────────────────────────────────────────────────
|
|
513
|
+
function createBackup() {
|
|
514
|
+
if (!fs.existsSync(ACCEL_HOME)) return null;
|
|
515
|
+
|
|
516
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
517
|
+
const backupDir = path.join(os.homedir(), `.accel-backup-${timestamp}`);
|
|
518
|
+
|
|
519
|
+
copyRecursive(ACCEL_HOME, backupDir);
|
|
520
|
+
return backupDir;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ─── Smart Update ───────────────────────────────────────────
|
|
524
|
+
function updateModule(moduleId) {
|
|
525
|
+
const mod = MODULES.find((m) => m.id === moduleId);
|
|
526
|
+
if (!mod) throw new Error(`Unknown module: ${moduleId}`);
|
|
527
|
+
|
|
528
|
+
const srcDir = path.join(PACKAGE_DIR, 'modules', moduleId);
|
|
529
|
+
const destDir = path.join(ACCEL_HOME, 'modules', moduleId);
|
|
530
|
+
const commandsDir = path.join(CLAUDE_HOME, 'commands', moduleId);
|
|
531
|
+
|
|
532
|
+
// Collect user data files before update
|
|
533
|
+
const preserved = new Map();
|
|
534
|
+
if (fs.existsSync(destDir)) {
|
|
535
|
+
collectUserData(destDir, ACCEL_HOME, preserved);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Remove old module code (but we have preserved user data in memory)
|
|
539
|
+
if (fs.existsSync(destDir)) {
|
|
540
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
541
|
+
}
|
|
542
|
+
if (fs.existsSync(commandsDir)) {
|
|
543
|
+
fs.rmSync(commandsDir, { recursive: true, force: true });
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Install fresh module code
|
|
547
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
548
|
+
fs.mkdirSync(commandsDir, { recursive: true });
|
|
549
|
+
copyRecursive(srcDir, destDir);
|
|
550
|
+
|
|
551
|
+
// Copy slash commands
|
|
552
|
+
const cmdSrcDirs = [
|
|
553
|
+
'commands', 'tasks', 'src/commands', 'src/tasks',
|
|
554
|
+
'skillsmith/tasks', 'skillsmith',
|
|
555
|
+
'mission-control/tasks', 'mission-control',
|
|
556
|
+
];
|
|
557
|
+
for (const cmdDir of cmdSrcDirs) {
|
|
558
|
+
const cmdSrc = path.join(srcDir, cmdDir);
|
|
559
|
+
if (fs.existsSync(cmdSrc)) {
|
|
560
|
+
const entries = fs.readdirSync(cmdSrc);
|
|
561
|
+
for (const entry of entries) {
|
|
562
|
+
const entryPath = path.join(cmdSrc, entry);
|
|
563
|
+
if (entry.endsWith('.md') && fs.statSync(entryPath).isFile()) {
|
|
564
|
+
fs.copyFileSync(entryPath, path.join(commandsDir, entry));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Restore user data on top of fresh code
|
|
571
|
+
let restoredCount = 0;
|
|
572
|
+
for (const [relativePath, content] of preserved) {
|
|
573
|
+
const fullPath = path.join(ACCEL_HOME, relativePath);
|
|
574
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
575
|
+
fs.writeFileSync(fullPath, content);
|
|
576
|
+
restoredCount++;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Reinstall hooks and MCP if needed
|
|
580
|
+
if (mod.hasHooks) {
|
|
581
|
+
const hooksSrc = path.join(srcDir, 'hooks');
|
|
582
|
+
const srcHooks = path.join(srcDir, 'src', 'hooks');
|
|
583
|
+
const hooksDir = fs.existsSync(hooksSrc) ? hooksSrc : fs.existsSync(srcHooks) ? srcHooks : null;
|
|
584
|
+
if (hooksDir) {
|
|
585
|
+
const hooksDest = path.join(CLAUDE_HOME, 'hooks');
|
|
586
|
+
fs.mkdirSync(hooksDest, { recursive: true });
|
|
587
|
+
copyRecursive(hooksDir, hooksDest);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (mod.hasMcp) {
|
|
592
|
+
const mcpSrc = path.join(srcDir, 'mcp');
|
|
593
|
+
const pkgMcp = path.join(srcDir, 'src', 'packages', `momentum-mcp`);
|
|
594
|
+
const mcpDir = fs.existsSync(mcpSrc) ? mcpSrc : fs.existsSync(pkgMcp) ? pkgMcp : null;
|
|
595
|
+
if (mcpDir) {
|
|
596
|
+
const mcpDest = path.join(ACCEL_HOME, 'mcp', moduleId);
|
|
597
|
+
fs.mkdirSync(mcpDest, { recursive: true });
|
|
598
|
+
copyRecursive(mcpDir, mcpDest);
|
|
599
|
+
if (fs.existsSync(path.join(mcpDest, 'package.json'))) {
|
|
600
|
+
try {
|
|
601
|
+
execFileSync('npm', ['install', '--production', '--silent'], { cwd: mcpDest, stdio: 'pipe' });
|
|
602
|
+
} catch {}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return restoredCount;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function collectUserData(dir, baseDir, preserved) {
|
|
611
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
612
|
+
for (const entry of entries) {
|
|
613
|
+
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
|
614
|
+
const fullPath = path.join(dir, entry.name);
|
|
615
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
616
|
+
|
|
617
|
+
if (entry.isDirectory()) {
|
|
618
|
+
collectUserData(fullPath, baseDir, preserved);
|
|
619
|
+
} else if (isUserData(relativePath)) {
|
|
620
|
+
preserved.set(relativePath, fs.readFileSync(fullPath));
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async function runUpdate() {
|
|
626
|
+
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
627
|
+
console.log(`\n ${c.yellow}Nothing to update. Run \`npx @accelerationguy/accel\` to install first.${c.reset}\n`);
|
|
628
|
+
process.exit(0);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
|
|
632
|
+
const installedVersion = manifest.version;
|
|
633
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(PACKAGE_DIR, 'package.json'), 'utf8'));
|
|
634
|
+
const newVersion = packageJson.version;
|
|
635
|
+
|
|
636
|
+
console.log(`\n ${c.cyan}${c.bold}${BRAND} Update${c.reset}\n`);
|
|
637
|
+
console.log(` Installed: v${installedVersion}`);
|
|
638
|
+
console.log(` Available: v${newVersion}`);
|
|
639
|
+
|
|
640
|
+
if (installedVersion === newVersion) {
|
|
641
|
+
console.log(`\n ${c.green}Already up to date!${c.reset}\n`);
|
|
642
|
+
process.exit(0);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Show what's installed
|
|
646
|
+
const installedModules = Object.keys(manifest.modules);
|
|
647
|
+
console.log(`\n Modules: ${installedModules.map((id) => {
|
|
648
|
+
const mod = MODULES.find((m) => m.id === id);
|
|
649
|
+
return mod ? mod.name : id;
|
|
650
|
+
}).join(', ')}`);
|
|
651
|
+
|
|
652
|
+
// Create backup
|
|
653
|
+
console.log(`\n ${c.dim}Creating backup...${c.reset}`);
|
|
654
|
+
const backupDir = createBackup();
|
|
655
|
+
if (backupDir) {
|
|
656
|
+
console.log(` ${c.green}\u2713${c.reset} Backup saved to ${backupDir}`);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Update each installed module
|
|
660
|
+
console.log(`\n ${c.bold}Updating...${c.reset}\n`);
|
|
661
|
+
|
|
662
|
+
const updated = [];
|
|
663
|
+
const failed = [];
|
|
664
|
+
let totalPreserved = 0;
|
|
665
|
+
|
|
666
|
+
for (const modId of installedModules) {
|
|
667
|
+
const mod = MODULES.find((m) => m.id === modId);
|
|
668
|
+
if (!mod) continue;
|
|
669
|
+
try {
|
|
670
|
+
const restoredCount = updateModule(modId);
|
|
671
|
+
totalPreserved += restoredCount;
|
|
672
|
+
updated.push(modId);
|
|
673
|
+
const preserved = restoredCount > 0 ? ` ${c.dim}(${restoredCount} user files preserved)${c.reset}` : '';
|
|
674
|
+
console.log(` ${c.green}\u2713${c.reset} ${mod.name} updated${preserved}`);
|
|
675
|
+
} catch (e) {
|
|
676
|
+
failed.push({ id: modId, error: e.message });
|
|
677
|
+
console.log(` ${c.red}\u2717${c.reset} ${mod.name} failed: ${e.message}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Update event bus schemas
|
|
682
|
+
setupEventBus();
|
|
683
|
+
|
|
684
|
+
// Update manifest version
|
|
685
|
+
if (updated.length > 0) {
|
|
686
|
+
manifest.version = newVersion;
|
|
687
|
+
manifest.updatedAt = new Date().toISOString();
|
|
688
|
+
manifest.previousVersion = installedVersion;
|
|
689
|
+
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
690
|
+
|
|
691
|
+
// Re-register hooks and MCP (in case format changed)
|
|
692
|
+
registerHooks(updated);
|
|
693
|
+
registerMcpServers(updated);
|
|
694
|
+
installClaudeMdBlock();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Summary
|
|
698
|
+
console.log('');
|
|
699
|
+
if (failed.length > 0) {
|
|
700
|
+
console.log(` ${c.yellow}${updated.length}/${installedModules.length} modules updated.${c.reset}`);
|
|
701
|
+
for (const f of failed) {
|
|
702
|
+
console.log(` ${c.red}\u2717${c.reset} ${f.id}: ${f.error}`);
|
|
703
|
+
}
|
|
704
|
+
if (backupDir) {
|
|
705
|
+
console.log(`\n ${c.dim}Backup at ${backupDir} — restore with: cp -R ${backupDir}/ ~/.accel/${c.reset}`);
|
|
706
|
+
}
|
|
707
|
+
} else {
|
|
708
|
+
console.log(` ${c.green}${c.bold}Updated ${BRAND} v${installedVersion} \u2192 v${newVersion}${c.reset}`);
|
|
709
|
+
if (totalPreserved > 0) {
|
|
710
|
+
console.log(` ${c.dim}${totalPreserved} user data files preserved (rules, workspace, configs)${c.reset}`);
|
|
711
|
+
}
|
|
712
|
+
console.log(`\n Restart Claude Code to pick up changes.`);
|
|
713
|
+
if (backupDir) {
|
|
714
|
+
console.log(` ${c.dim}Backup at ${backupDir} (safe to delete if everything works)${c.reset}`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
console.log('');
|
|
718
|
+
}
|
|
719
|
+
|
|
492
720
|
// ─── Event Bus Setup ────────────────────────────────────────
|
|
493
721
|
function setupEventBus() {
|
|
494
722
|
const eventsDir = path.join(ACCEL_HOME, 'events');
|
|
@@ -549,6 +777,7 @@ async function main() {
|
|
|
549
777
|
const isStatus = args.includes('status');
|
|
550
778
|
const isUninstall = args.includes('uninstall');
|
|
551
779
|
const isUpdate = args.includes('update');
|
|
780
|
+
const isBackup = args.includes('backup');
|
|
552
781
|
|
|
553
782
|
if (isStatus) {
|
|
554
783
|
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
@@ -564,6 +793,21 @@ async function main() {
|
|
|
564
793
|
process.exit(0);
|
|
565
794
|
}
|
|
566
795
|
|
|
796
|
+
if (isUpdate) {
|
|
797
|
+
await runUpdate();
|
|
798
|
+
process.exit(0);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
if (isBackup) {
|
|
802
|
+
if (!fs.existsSync(ACCEL_HOME)) {
|
|
803
|
+
console.log(`\n ${c.yellow}Nothing to back up. No Accelerate installation found.${c.reset}\n`);
|
|
804
|
+
process.exit(0);
|
|
805
|
+
}
|
|
806
|
+
const backupDir = createBackup();
|
|
807
|
+
console.log(`\n ${c.green}\u2713${c.reset} Backup created at: ${backupDir}\n`);
|
|
808
|
+
process.exit(0);
|
|
809
|
+
}
|
|
810
|
+
|
|
567
811
|
if (isUninstall) {
|
|
568
812
|
console.log(`\n ${c.yellow}Uninstalling ${BRAND}...${c.reset}`);
|
|
569
813
|
if (fs.existsSync(ACCEL_HOME)) fs.rmSync(ACCEL_HOME, { recursive: true, force: true });
|