agentvibes 4.5.7 → 4.6.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/src/installer.js CHANGED
@@ -3822,6 +3822,76 @@ async function configureSessionStartHook(targetDir, spinner) {
3822
3822
  }
3823
3823
  }
3824
3824
 
3825
+ /**
3826
+ * Configure BMAD party mode PostToolUse hook in the global ~/.claude/settings.json.
3827
+ * Copies bmad-party-speak script to ~/.claude/hooks/ (or hooks-windows/ on Windows)
3828
+ * and registers the PostToolUse hook so party mode TTS works in any BMAD project.
3829
+ * @param {string} targetDir - Target installation directory (used to locate source scripts)
3830
+ * @param {Object} spinner - Ora spinner instance
3831
+ */
3832
+ async function configurePartyModeHook(targetDir, spinner, homeDirOverride) {
3833
+ spinner.start('Configuring BMAD party mode TTS hook...');
3834
+ const homeDir = homeDirOverride || os.homedir();
3835
+ const globalClaudeDir = path.join(homeDir, '.claude');
3836
+ const globalSettingsPath = path.join(globalClaudeDir, 'settings.json');
3837
+
3838
+ try {
3839
+ // Determine platform-specific paths
3840
+ const hooksSubdir = isNativeWindows() ? 'hooks-windows' : 'hooks';
3841
+ const scriptName = isNativeWindows() ? 'bmad-party-speak.ps1' : 'bmad-party-speak.sh';
3842
+ const globalHooksDir = path.join(globalClaudeDir, hooksSubdir);
3843
+ const srcScript = path.join(__dirname, '..', '.claude', hooksSubdir, scriptName);
3844
+ const destScript = path.join(globalHooksDir, scriptName);
3845
+
3846
+ // Copy script to global hooks dir (create dir if needed)
3847
+ await fs.mkdir(globalHooksDir, { recursive: true });
3848
+ await fs.copyFile(srcScript, destScript);
3849
+ if (!isNativeWindows()) {
3850
+ await fs.chmod(destScript, 0o750);
3851
+ }
3852
+
3853
+ // Build the PostToolUse hook command
3854
+ const hookCommand = isNativeWindows()
3855
+ ? `powershell -NoProfile -ExecutionPolicy Bypass -File "$HOME\\.claude\\hooks-windows\\bmad-party-speak.ps1"`
3856
+ : `bash "$HOME/.claude/hooks/bmad-party-speak.sh"`;
3857
+
3858
+ // Read/create global settings.json
3859
+ let settings = {};
3860
+ try {
3861
+ const content = await fs.readFile(globalSettingsPath, 'utf8');
3862
+ settings = JSON.parse(content);
3863
+ } catch {
3864
+ // File missing or invalid — start fresh
3865
+ }
3866
+
3867
+ if (!settings.hooks) settings.hooks = {};
3868
+
3869
+ // Check if PostToolUse hook already registered
3870
+ const existing = settings.hooks.PostToolUse;
3871
+ const alreadyRegistered = Array.isArray(existing) &&
3872
+ existing.some(entry =>
3873
+ Array.isArray(entry.hooks) &&
3874
+ entry.hooks.some(h => h.command && h.command.includes('bmad-party-speak'))
3875
+ );
3876
+
3877
+ if (!alreadyRegistered) {
3878
+ if (!Array.isArray(settings.hooks.PostToolUse)) {
3879
+ settings.hooks.PostToolUse = [];
3880
+ }
3881
+ settings.hooks.PostToolUse.push({
3882
+ hooks: [{ type: 'command', command: hookCommand }]
3883
+ });
3884
+ await fs.writeFile(globalSettingsPath, JSON.stringify(settings, null, 2));
3885
+ spinner.succeed(chalk.green('BMAD party mode TTS hook configured!\n'));
3886
+ } else {
3887
+ // Script still updated above — just note settings unchanged
3888
+ spinner.succeed(chalk.green('BMAD party mode TTS hook up to date\n'));
3889
+ }
3890
+ } catch (error) {
3891
+ spinner.warn(chalk.yellow(`BMAD party mode hook setup skipped: ${error.message}\n`));
3892
+ }
3893
+ }
3894
+
3825
3895
  /**
3826
3896
  * Ensure target directory is a git repo (required for Claude Code hook context injection)
3827
3897
  * @param {string} targetDir - Target installation directory
@@ -4781,7 +4851,8 @@ async function updateCommandFiles(targetDir, spinner) {
4781
4851
  * These hooks contain bug fixes (e.g. markdown stripping) that must propagate
4782
4852
  * on every `npx agentvibes update` regardless of target directory.
4783
4853
  */
4784
- const CRITICAL_HOOKS = ['stop-tts.sh', 'stop.sh', 'play-tts.sh', 'session-start-tts.sh'];
4854
+ const CRITICAL_HOOKS = ['stop-tts.sh', 'stop.sh', 'play-tts.sh', 'session-start-tts.sh', 'bmad-party-speak.sh'];
4855
+ const CRITICAL_HOOKS_WINDOWS = ['play-tts.ps1', 'session-start-tts.ps1', 'bmad-speak.ps1', 'bmad-party-speak.ps1'];
4785
4856
 
4786
4857
  /**
4787
4858
  * Update critical hooks in the global ~/.claude/hooks/ directory if it exists.
@@ -4811,6 +4882,27 @@ async function updateGlobalHooks(srcHooksDir, homeDirOverride) {
4811
4882
  // file not in global dir or src missing — skip silently
4812
4883
  }
4813
4884
  }
4885
+
4886
+ // Also update Windows global hooks-windows dir if present
4887
+ const globalHooksWindowsDir = path.join(homeDirOverride || os.homedir(), '.claude', 'hooks-windows');
4888
+ const srcHooksWindowsDir = path.join(path.dirname(srcHooksDir), 'hooks-windows');
4889
+ try {
4890
+ await fs.access(globalHooksWindowsDir);
4891
+ for (const hook of CRITICAL_HOOKS_WINDOWS) {
4892
+ const destPath = path.join(globalHooksWindowsDir, hook);
4893
+ const srcPath = path.join(srcHooksWindowsDir, hook);
4894
+ try {
4895
+ await fs.access(destPath); // only update if already installed
4896
+ await fs.copyFile(srcPath, destPath);
4897
+ updated++;
4898
+ } catch {
4899
+ // file not in global dir or src missing — skip silently
4900
+ }
4901
+ }
4902
+ } catch {
4903
+ // hooks-windows dir not present — nothing to do
4904
+ }
4905
+
4814
4906
  return updated;
4815
4907
  }
4816
4908
 
@@ -4873,6 +4965,7 @@ async function performUpdateOperations(targetDir, spinner) {
4873
4965
  // Update settings.json
4874
4966
  spinner.text = 'Updating AgentVibes hook configuration...';
4875
4967
  await configureSessionStartHook(targetDir, silentSpinner);
4968
+ await configurePartyModeHook(targetDir, silentSpinner);
4876
4969
  await ensureGitRepo(targetDir, silentSpinner);
4877
4970
 
4878
4971
  // Detect and migrate old configuration
@@ -5050,6 +5143,7 @@ async function install(options = {}) {
5050
5143
  await copyBackgroundMusicFiles(targetDir, silentSpinner);
5051
5144
  await copyConfigFiles(targetDir, silentSpinner);
5052
5145
  await configureSessionStartHook(targetDir, silentSpinner);
5146
+ await configurePartyModeHook(targetDir, silentSpinner);
5053
5147
  await installPluginManifest(targetDir, silentSpinner);
5054
5148
  await ensureGitRepo(targetDir, silentSpinner);
5055
5149
 
@@ -5943,7 +6037,7 @@ export {
5943
6037
  isTermux, isNativeWindows, detectAndNotifyTermux,
5944
6038
  copyCommandFiles, copyHookFiles, copyPersonalityFiles,
5945
6039
  copyPluginFiles, copyBmadConfigFiles, copyBackgroundMusicFiles,
5946
- copyConfigFiles, configureSessionStartHook, ensureGitRepo,
6040
+ copyConfigFiles, configureSessionStartHook, configurePartyModeHook, ensureGitRepo,
5947
6041
  installPluginManifest, checkAndInstallPiper,
5948
- updateGlobalHooks, CRITICAL_HOOKS,
6042
+ updateGlobalHooks, CRITICAL_HOOKS, CRITICAL_HOOKS_WINDOWS,
5949
6043
  };
@@ -441,7 +441,7 @@ if [[ -n "$BG_FILE" ]] && command -v ffmpeg &>/dev/null; then
441
441
  TOTAL_DUR=$(awk "BEGIN {printf \"%.2f\", $DURATION + 2}")
442
442
  FADE_OUT=$(awk "BEGIN {printf \"%.2f\", $DURATION}")
443
443
  timeout 20 ffmpeg -y -i "$PLAY_FILE" -stream_loop -1 -i "$BG_PATH" \
444
- -filter_complex "[1:a]volume=${BG_VOLUME},afade=t=in:st=0:d=0.3,afade=t=out:st=${FADE_OUT}:d=2[bg];[0:a]adelay=2000|2000[v];[v][bg]amix=inputs=2:duration=longest[out]" \
444
+ -filter_complex "[1:a]volume=${BG_VOLUME},afade=t=in:st=0:d=0.3,afade=t=out:st=${FADE_OUT}:d=2[bg];[0:a]adelay=1000|1000,volume=1.5[v];[v][bg]amix=inputs=2:duration=longest:normalize=0[out]" \
445
445
  -map "[out]" -t "$TOTAL_DUR" "$FINAL_WAV" </dev/null 2>/dev/null && PLAY_FILE="$FINAL_WAV"
446
446
  fi
447
447
  fi