@accelerationguy/accel 1.0.0 → 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 +264 -8
- package/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -412,27 +412,38 @@ function registerHooks(installedModules) {
|
|
|
412
412
|
let registered = 0;
|
|
413
413
|
const hooksDir = path.join(CLAUDE_HOME, 'hooks');
|
|
414
414
|
|
|
415
|
+
// Collect all hooks per event type
|
|
416
|
+
const hooksByEvent = {};
|
|
415
417
|
for (const modId of installedModules) {
|
|
416
418
|
const hookDefs = HOOK_DEFINITIONS[modId];
|
|
417
419
|
if (!hookDefs) continue;
|
|
418
420
|
|
|
419
421
|
for (const def of hookDefs) {
|
|
422
|
+
if (!hooksByEvent[def.event]) hooksByEvent[def.event] = [];
|
|
420
423
|
const hookPath = path.join(hooksDir, def.script);
|
|
421
|
-
|
|
424
|
+
hooksByEvent[def.event].push({
|
|
422
425
|
type: 'command',
|
|
423
426
|
command: `python3 ${hookPath}`,
|
|
424
|
-
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
if (!Array.isArray(settings.hooks[def.event])) {
|
|
428
|
-
settings.hooks[def.event] = [];
|
|
429
|
-
}
|
|
430
|
-
settings.hooks[def.event].push(hookEntry);
|
|
427
|
+
});
|
|
431
428
|
registered++;
|
|
432
429
|
console.log(` ${c.green}\u2713${c.reset} Hook registered: ${def.script} (${def.event})`);
|
|
433
430
|
}
|
|
434
431
|
}
|
|
435
432
|
|
|
433
|
+
// Write hooks in the correct Claude Code format:
|
|
434
|
+
// { "hooks": { "EventName": [ { "matcher": "", "hooks": [...] } ] } }
|
|
435
|
+
for (const [eventName, hooks] of Object.entries(hooksByEvent)) {
|
|
436
|
+
if (!Array.isArray(settings.hooks[eventName])) {
|
|
437
|
+
settings.hooks[eventName] = [];
|
|
438
|
+
}
|
|
439
|
+
// Add one entry with all Accelerate hooks grouped together
|
|
440
|
+
settings.hooks[eventName].push({
|
|
441
|
+
matcher: '',
|
|
442
|
+
hooks: hooks,
|
|
443
|
+
[ACCEL_HOOK_MARKER]: true,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
436
447
|
if (registered > 0) {
|
|
437
448
|
fs.mkdirSync(path.dirname(SETTINGS_PATH), { recursive: true });
|
|
438
449
|
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
@@ -457,6 +468,7 @@ function deregisterHooks() {
|
|
|
457
468
|
for (const eventName of Object.keys(settings.hooks)) {
|
|
458
469
|
if (!Array.isArray(settings.hooks[eventName])) continue;
|
|
459
470
|
const before = settings.hooks[eventName].length;
|
|
471
|
+
// Remove entries that have our marker (both old flat format and new grouped format)
|
|
460
472
|
settings.hooks[eventName] = settings.hooks[eventName].filter(
|
|
461
473
|
(h) => !h[ACCEL_HOOK_MARKER]
|
|
462
474
|
);
|
|
@@ -477,6 +489,234 @@ function deregisterHooks() {
|
|
|
477
489
|
}
|
|
478
490
|
}
|
|
479
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
|
+
|
|
480
720
|
// ─── Event Bus Setup ────────────────────────────────────────
|
|
481
721
|
function setupEventBus() {
|
|
482
722
|
const eventsDir = path.join(ACCEL_HOME, 'events');
|
|
@@ -537,6 +777,7 @@ async function main() {
|
|
|
537
777
|
const isStatus = args.includes('status');
|
|
538
778
|
const isUninstall = args.includes('uninstall');
|
|
539
779
|
const isUpdate = args.includes('update');
|
|
780
|
+
const isBackup = args.includes('backup');
|
|
540
781
|
|
|
541
782
|
if (isStatus) {
|
|
542
783
|
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
@@ -552,6 +793,21 @@ async function main() {
|
|
|
552
793
|
process.exit(0);
|
|
553
794
|
}
|
|
554
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
|
+
|
|
555
811
|
if (isUninstall) {
|
|
556
812
|
console.log(`\n ${c.yellow}Uninstalling ${BRAND}...${c.reset}`);
|
|
557
813
|
if (fs.existsSync(ACCEL_HOME)) fs.rmSync(ACCEL_HOME, { recursive: true, force: true });
|