@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.
Files changed (2) hide show
  1. package/bin/install.js +264 -8
  2. 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
- const hookEntry = {
424
+ hooksByEvent[def.event].push({
422
425
  type: 'command',
423
426
  command: `python3 ${hookPath}`,
424
- [ACCEL_HOOK_MARKER]: true,
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accelerationguy/accel",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Accelerate — Unified Claude Code Agent Framework by Acceleration Guy",
5
5
  "bin": {
6
6
  "accel": "./bin/install.js"