helloagents 3.0.23 → 3.0.26

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.
@@ -38,6 +38,7 @@ function routeExplicitCommand({
38
38
  appendReplayEvent,
39
39
  buildRouteInstruction,
40
40
  suppress,
41
+ recordReplayEvents,
41
42
  }) {
42
43
  const cmdMatch = prompt.match(/^~(\w+)/)
43
44
  if (!cmdMatch) return false
@@ -50,14 +51,16 @@ function routeExplicitCommand({
50
51
  sourceSkillName: skillName,
51
52
  payload,
52
53
  })
53
- appendReplayEvent(cwd, {
54
- host,
55
- event: 'command_route_selected',
56
- source: 'route',
57
- skillName: canonicalSkillName,
58
- sourceSkillName: skillName,
59
- payload,
60
- })
54
+ if (recordReplayEvents !== false) {
55
+ appendReplayEvent(cwd, {
56
+ host,
57
+ event: 'command_route_selected',
58
+ source: 'route',
59
+ skillName: canonicalSkillName,
60
+ sourceSkillName: skillName,
61
+ payload,
62
+ })
63
+ }
61
64
  suppress(buildRouteInstruction({
62
65
  skillName,
63
66
  extraRules: buildHelpExtraRules(skillName),
@@ -84,6 +87,7 @@ export function handleRouteCommand({
84
87
  getWorkflowRecommendation,
85
88
  suppress,
86
89
  emptySuppress,
90
+ recordReplayEvents = true,
87
91
  }) {
88
92
  const prompt = (payload.prompt || '').trim()
89
93
  const cwd = payload.cwd || process.cwd()
@@ -105,6 +109,7 @@ export function handleRouteCommand({
105
109
  appendReplayEvent,
106
110
  buildRouteInstruction,
107
111
  suppress,
112
+ recordReplayEvents,
108
113
  })) {
109
114
  return
110
115
  }
@@ -112,13 +117,15 @@ export function handleRouteCommand({
112
117
  const bootstrapFile = resolveBootstrapFile(cwd, settings, host)
113
118
  if (bootstrapFile === 'bootstrap.md') {
114
119
  clearRouteContext({ cwd, payload })
115
- appendReplayEvent(cwd, {
116
- host,
117
- event: 'semantic_route_prompted',
118
- source: 'route',
119
- recommendation: getWorkflowRecommendation(cwd, { payload }),
120
- payload,
121
- })
120
+ if (recordReplayEvents !== false) {
121
+ appendReplayEvent(cwd, {
122
+ host,
123
+ event: 'semantic_route_prompted',
124
+ source: 'route',
125
+ recommendation: getWorkflowRecommendation(cwd, { payload }),
126
+ payload,
127
+ })
128
+ }
122
129
  suppress(buildSemanticRouteInstruction(cwd, payload))
123
130
  return
124
131
  }
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process'
3
+ import { existsSync } from 'node:fs'
4
+ import { platform } from 'node:os'
5
+ import { join, dirname } from 'node:path'
6
+ import { fileURLToPath } from 'node:url'
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+ const PKG_ROOT = join(__dirname, '..')
11
+ const PLAT = platform()
12
+
13
+ function shellQuote(value = '') {
14
+ return `'${String(value).replace(/'/g, `'\\''`)}'`
15
+ }
16
+
17
+ export function resolveSoundPath(event = '', pkgRoot = PKG_ROOT) {
18
+ const filePath = join(pkgRoot, 'assets', 'sounds', `${event}.wav`)
19
+ return existsSync(filePath) ? filePath : ''
20
+ }
21
+
22
+ export function buildWindowsSoundCommand(filePath = '') {
23
+ return `(New-Object System.Media.SoundPlayer '${String(filePath || '').replace(/'/g, "''")}').PlaySync()`
24
+ }
25
+
26
+ function playWindows(filePath) {
27
+ const result = spawnSync('powershell', [
28
+ '-NoProfile',
29
+ '-c',
30
+ buildWindowsSoundCommand(filePath),
31
+ ], {
32
+ encoding: 'utf-8',
33
+ windowsHide: true,
34
+ })
35
+
36
+ if (result.error) throw result.error
37
+ if (result.status !== 0) {
38
+ throw new Error((result.stderr || result.stdout || 'PowerShell sound playback failed').trim())
39
+ }
40
+ }
41
+
42
+ function playMac(filePath) {
43
+ const result = spawnSync('afplay', [filePath], {
44
+ encoding: 'utf-8',
45
+ })
46
+ if (result.error) throw result.error
47
+ if (result.status !== 0) {
48
+ throw new Error((result.stderr || result.stdout || 'afplay failed').trim())
49
+ }
50
+ }
51
+
52
+ function playLinux(filePath) {
53
+ const script = `if command -v aplay >/dev/null 2>&1; then aplay -q ${shellQuote(filePath)}; elif command -v paplay >/dev/null 2>&1; then paplay ${shellQuote(filePath)}; else printf '\\a'; fi`
54
+ const result = spawnSync('sh', ['-c', script], {
55
+ encoding: 'utf-8',
56
+ })
57
+ if (result.error) throw result.error
58
+ if (result.status !== 0) {
59
+ throw new Error((result.stderr || result.stdout || 'Linux sound playback failed').trim())
60
+ }
61
+ }
62
+
63
+ export function playSoundEvent(event = '', pkgRoot = PKG_ROOT) {
64
+ const soundPath = resolveSoundPath(event, pkgRoot)
65
+ if (!soundPath) {
66
+ process.stderr.write('\x07')
67
+ return false
68
+ }
69
+
70
+ if (PLAT === 'win32') {
71
+ playWindows(soundPath)
72
+ return true
73
+ }
74
+ if (PLAT === 'darwin') {
75
+ playMac(soundPath)
76
+ return true
77
+ }
78
+
79
+ playLinux(soundPath)
80
+ return true
81
+ }
82
+
83
+ function main() {
84
+ const event = process.argv[2] || 'complete'
85
+ try {
86
+ playSoundEvent(event)
87
+ } catch {
88
+ process.stderr.write('\x07')
89
+ }
90
+ }
91
+
92
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
93
+ main()
94
+ }
@@ -5,7 +5,7 @@
5
5
  import { platform } from 'node:os';
6
6
  import { join } from 'node:path';
7
7
  import { existsSync } from 'node:fs';
8
- import { spawnSync } from 'node:child_process';
8
+ import { execFileSync, spawn } from 'node:child_process';
9
9
 
10
10
  const PLAT = platform();
11
11
 
@@ -55,33 +55,65 @@ function resolveWav(pkgRoot, event) {
55
55
  return existsSync(p) ? p : null;
56
56
  }
57
57
 
58
- function runSync(command, args) {
58
+ function resolveSoundHelper(pkgRoot) {
59
+ const helperPath = join(pkgRoot, 'scripts', 'notify-sound.mjs');
60
+ return existsSync(helperPath) ? helperPath : '';
61
+ }
62
+
63
+ function runDetached(command, args) {
59
64
  try {
60
- const result = spawnSync(command, args, {
65
+ const child = spawn(command, args, {
61
66
  stdio: 'ignore',
67
+ detached: true,
62
68
  windowsHide: true,
63
69
  });
64
- return !result.error && result.status === 0;
70
+ child.on('error', () => {});
71
+ child.unref();
72
+ return true;
65
73
  } catch {
66
74
  return false;
67
75
  }
68
76
  }
69
77
 
70
- export function playSound(pkgRoot, event) {
78
+ function shellQuote(value = '') {
79
+ return `'${String(value).replace(/'/g, `'\\''`)}'`
80
+ }
81
+
82
+ function runSoundHelper(pkgRoot, event, mode = 'background') {
83
+ const helperPath = resolveSoundHelper(pkgRoot);
84
+ if (!helperPath) return false;
85
+
86
+ if (mode === 'blocking') {
87
+ try {
88
+ execFileSync(process.execPath, [helperPath, event], {
89
+ stdio: 'ignore',
90
+ windowsHide: true,
91
+ });
92
+ return true;
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+
98
+ return runDetached(process.execPath, [helperPath, event]);
99
+ }
100
+
101
+ export function playSound(pkgRoot, event, options = {}) {
71
102
  if (DISABLE_OS_NOTIFICATIONS) return;
72
103
  const wav = resolveWav(pkgRoot, event);
73
104
  if (!wav) { process.stderr.write('\x07'); return; }
105
+ if (runSoundHelper(pkgRoot, event, options.mode === 'blocking' ? 'blocking' : 'background')) return;
74
106
  try {
75
107
  if (PLAT === 'win32') {
76
- runSync('powershell', [
108
+ runDetached('powershell', [
77
109
  '-NoProfile',
78
110
  '-c',
79
111
  `(New-Object Media.SoundPlayer '${wav.replace(/'/g, "''")}').PlaySync()`,
80
112
  ]);
81
113
  } else if (PLAT === 'darwin') {
82
- runSync('afplay', [wav]);
114
+ runDetached('afplay', [wav]);
83
115
  } else {
84
- if (!runSync('aplay', ['-q', wav]) && !runSync('paplay', [wav])) process.stderr.write('\x07');
116
+ runDetached('sh', ['-c', `if command -v aplay >/dev/null 2>&1; then aplay -q ${shellQuote(wav)}; elif command -v paplay >/dev/null 2>&1; then paplay ${shellQuote(wav)}; else printf '\\a'; fi`]);
85
117
  }
86
118
  } catch { process.stderr.write('\x07'); }
87
119
  }
@@ -124,16 +156,16 @@ export function desktopNotify(pkgRoot, event, extra) {
124
156
  try {
125
157
  if (PLAT === 'win32') {
126
158
  const iconPath = join(pkgRoot, 'assets', 'icons', 'icon.png').replace(/\//g, '\\');
127
- runSync('powershell', ['-NoProfile', '-c', buildWindowsToastScript(notification, iconPath)]);
159
+ runDetached('powershell', ['-NoProfile', '-c', buildWindowsToastScript(notification, iconPath)]);
128
160
  } else if (PLAT === 'darwin') {
129
161
  const subtitle = notification.sourceLabel
130
162
  ? ` subtitle "${escapeAppleScriptText(notification.sourceLabel)}"`
131
163
  : '';
132
- runSync('osascript', ['-e',
164
+ runDetached('osascript', ['-e',
133
165
  `display notification "${escapeAppleScriptText(notification.message)}" with title "${escapeAppleScriptText(notification.title)}"${subtitle}`],
134
166
  );
135
167
  } else {
136
- if (!runSync('notify-send', [notification.title, notification.body])) process.stderr.write('\x07');
168
+ runDetached('sh', ['-c', `if command -v notify-send >/dev/null 2>&1; then notify-send ${shellQuote(notification.title)} ${shellQuote(notification.body)}; else printf '\\a'; fi`]);
137
169
  }
138
170
  } catch { process.stderr.write('\x07'); }
139
171
  }