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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +18 -10
- package/README_CN.md +18 -10
- package/bootstrap-lite.md +3 -3
- package/bootstrap.md +3 -3
- package/gemini-extension.json +1 -1
- package/install.ps1 +21 -15
- package/install.sh +37 -12
- package/package.json +1 -1
- package/scripts/cli-codex-config.mjs +50 -3
- package/scripts/cli-codex-hooks-state.mjs +271 -0
- package/scripts/cli-codex.mjs +21 -14
- package/scripts/cli-doctor-codex.mjs +26 -3
- package/scripts/cli-host-detect.mjs +3 -3
- package/scripts/cli-messages.mjs +2 -2
- package/scripts/cli-utils.mjs +4 -3
- package/scripts/delivery-gate.mjs +20 -11
- package/scripts/notify-closeout.mjs +22 -2
- package/scripts/notify-route.mjs +22 -15
- package/scripts/notify-sound.mjs +94 -0
- package/scripts/notify-ui.mjs +43 -11
- package/scripts/notify.mjs +241 -66
- package/scripts/project-session-cleanup.mjs +27 -1
- package/scripts/ralph-loop.mjs +76 -81
- package/scripts/runtime-scope.mjs +45 -17
- package/scripts/session-capsule.mjs +1 -0
- package/scripts/turn-state-cli.mjs +24 -2
- package/scripts/turn-stop-gate.mjs +7 -5
- package/skills/commands/help/SKILL.md +1 -1
package/scripts/notify-route.mjs
CHANGED
|
@@ -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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
}
|
package/scripts/notify-ui.mjs
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
65
|
+
const child = spawn(command, args, {
|
|
61
66
|
stdio: 'ignore',
|
|
67
|
+
detached: true,
|
|
62
68
|
windowsHide: true,
|
|
63
69
|
});
|
|
64
|
-
|
|
70
|
+
child.on('error', () => {});
|
|
71
|
+
child.unref();
|
|
72
|
+
return true;
|
|
65
73
|
} catch {
|
|
66
74
|
return false;
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
+
runDetached('afplay', [wav]);
|
|
83
115
|
} else {
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
164
|
+
runDetached('osascript', ['-e',
|
|
133
165
|
`display notification "${escapeAppleScriptText(notification.message)}" with title "${escapeAppleScriptText(notification.title)}"${subtitle}`],
|
|
134
166
|
);
|
|
135
167
|
} else {
|
|
136
|
-
|
|
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
|
}
|