@a5c-ai/babysitter-omp 5.0.1-staging.71bb37a4 → 5.0.1-staging.75c8fb21
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/cli.cjs +43 -17
- package/bin/install-shared.cjs +219 -0
- package/bin/install.cjs +18 -33
- package/bin/uninstall.cjs +15 -11
- package/commands/call.md +7 -7
- package/commands/contrib.md +31 -31
- package/commands/doctor.md +5 -5
- package/commands/forever.md +6 -6
- package/commands/help.md +3 -2
- package/commands/observe.md +1 -1
- package/commands/plan.md +7 -7
- package/commands/plugins.md +249 -249
- package/commands/project-install.md +10 -10
- package/commands/resume.md +8 -8
- package/commands/retrospect.md +55 -55
- package/commands/user-install.md +10 -10
- package/commands/yolo.md +7 -7
- package/package.json +10 -15
- package/scripts/team-install.cjs +23 -0
- package/skills/contrib/SKILL.md +25 -25
- package/skills/doctor/SKILL.md +5 -5
- package/skills/help/SKILL.md +3 -2
- package/skills/observe/SKILL.md +1 -1
- package/skills/plugins/SKILL.md +243 -243
- package/skills/project-install/SKILL.md +3 -3
- package/skills/resume/SKILL.md +1 -1
- package/skills/retrospect/SKILL.md +48 -48
- package/skills/user-install/SKILL.md +3 -3
- package/versions.json +2 -1
- package/extensions/index.legacy.ts +0 -55
- package/hooks/babysitter-proxied-before-provider-request.js +0 -167
- package/hooks/babysitter-proxied-context.js +0 -165
- package/hooks/babysitter-proxied-session-start.js +0 -227
- package/hooks/babysitter-proxied-stop.js +0 -179
- package/hooks/babysitter-proxied-tool-call.js +0 -166
- package/hooks/hooks.json +0 -46
- package/hooks/proxied-hooks.json +0 -47
- package/scripts/setup.sh +0 -74
package/versions.json
CHANGED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
const COMMANDS = [
|
|
4
|
-
"assimilate",
|
|
5
|
-
"call",
|
|
6
|
-
"cleanup",
|
|
7
|
-
"contrib",
|
|
8
|
-
"doctor",
|
|
9
|
-
"forever",
|
|
10
|
-
"help",
|
|
11
|
-
"observe",
|
|
12
|
-
"plan",
|
|
13
|
-
"plugins",
|
|
14
|
-
"project-install",
|
|
15
|
-
"resume",
|
|
16
|
-
"retrospect",
|
|
17
|
-
"user-install",
|
|
18
|
-
"yolo",
|
|
19
|
-
] as const;
|
|
20
|
-
|
|
21
|
-
function toSkillPrompt(name: string, args: string): string {
|
|
22
|
-
return `/skill:${name}${args ? ` ${args}` : ""}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default function activate(pi: ExtensionAPI): void {
|
|
26
|
-
const forwardBabysit = async (args: unknown) => {
|
|
27
|
-
pi.sendUserMessage(toSkillPrompt("babysit", String(args ?? "").trim()));
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
pi.registerCommand("babysit", {
|
|
31
|
-
description: "Load the Babysitter orchestration skill",
|
|
32
|
-
handler: forwardBabysit,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
pi.registerCommand("babysitter", {
|
|
36
|
-
description: "Alias for /babysit",
|
|
37
|
-
handler: forwardBabysit,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
for (const name of COMMANDS) {
|
|
41
|
-
const forward = async (args: unknown) => {
|
|
42
|
-
pi.sendUserMessage(toSkillPrompt(name, String(args ?? "").trim()));
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
pi.registerCommand(name, {
|
|
46
|
-
description: `Open the Babysitter ${name} skill`,
|
|
47
|
-
handler: forward,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
pi.registerCommand(`babysitter:${name}`, {
|
|
51
|
-
description: `Alias for /${name}`,
|
|
52
|
-
handler: forward,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Unified Before Provider Request Hook for Oh-My-Pi
|
|
4
|
-
* Routes through hooks-proxy for all hook execution.
|
|
5
|
-
*
|
|
6
|
-
* Fires before Oh-My-Pi sends a request to the LLM provider. Delegates to
|
|
7
|
-
* `babysitter hook:run --hook-type pre-tool-use` via hooks-proxy
|
|
8
|
-
* (mapped as a model-level interception point).
|
|
9
|
-
*
|
|
10
|
-
* This hook can be used to:
|
|
11
|
-
* - Log provider requests for observability
|
|
12
|
-
* - Inject babysitter metadata into request context
|
|
13
|
-
* - Track token usage across orchestration iterations
|
|
14
|
-
*
|
|
15
|
-
* Oh-My-Pi plugin protocol (programmatic):
|
|
16
|
-
* - Called from extension via execSync
|
|
17
|
-
* - Receives provider request context as JSON via stdin
|
|
18
|
-
* - Outputs JSON to stdout
|
|
19
|
-
* - Exit 0 = success
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
"use strict";
|
|
23
|
-
|
|
24
|
-
const { execSync } = require("child_process");
|
|
25
|
-
const { readFileSync, mkdirSync, appendFileSync, existsSync, writeFileSync } = require("fs");
|
|
26
|
-
const os = require("os");
|
|
27
|
-
const path = require("path");
|
|
28
|
-
|
|
29
|
-
const PLUGIN_ROOT = process.env.OMP_PLUGIN_ROOT || path.resolve(__dirname, "..");
|
|
30
|
-
const GLOBAL_ROOT = process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(os.homedir(), ".a5c");
|
|
31
|
-
const STATE_DIR = process.env.BABYSITTER_STATE_DIR || path.join(GLOBAL_ROOT, "state");
|
|
32
|
-
const LOG_DIR = process.env.BABYSITTER_LOG_DIR || path.join(GLOBAL_ROOT, "logs");
|
|
33
|
-
const LOG_FILE = path.join(LOG_DIR, "babysitter-omp-before-provider-hook.log");
|
|
34
|
-
const PROXY_MARKER = path.join(PLUGIN_ROOT, ".hooks-proxy-install-attempted");
|
|
35
|
-
|
|
36
|
-
function ensureDir(dir) {
|
|
37
|
-
try { mkdirSync(dir, { recursive: true }); } catch { /* best-effort */ }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function blog(msg) {
|
|
41
|
-
ensureDir(LOG_DIR);
|
|
42
|
-
const ts = new Date().toISOString();
|
|
43
|
-
try {
|
|
44
|
-
appendFileSync(LOG_FILE, `[INFO] ${ts} ${msg}\n`);
|
|
45
|
-
} catch { /* best-effort */ }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function getSdkVersion() {
|
|
49
|
-
try {
|
|
50
|
-
const versions = JSON.parse(readFileSync(path.join(PLUGIN_ROOT, "versions.json"), "utf8"));
|
|
51
|
-
return versions.sdkVersion || "latest";
|
|
52
|
-
} catch {
|
|
53
|
-
return "latest";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function resolveHooksProxy() {
|
|
58
|
-
try {
|
|
59
|
-
execSync("a5c-hooks-proxy --version", { stdio: "pipe", timeout: 5000 });
|
|
60
|
-
return "a5c-hooks-proxy";
|
|
61
|
-
} catch { /* not in PATH */ }
|
|
62
|
-
|
|
63
|
-
const localProxy = path.join(
|
|
64
|
-
process.env.HOME || process.env.USERPROFILE || "~",
|
|
65
|
-
".local", "bin", process.platform === "win32" ? "a5c-hooks-proxy.exe" : "a5c-hooks-proxy"
|
|
66
|
-
);
|
|
67
|
-
if (existsSync(localProxy)) {
|
|
68
|
-
return localProxy;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function installHooksProxy(version) {
|
|
75
|
-
if (existsSync(PROXY_MARKER)) return;
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
execSync(`npm i -g "@a5c-ai/hooks-proxy-cli@${version}" --loglevel=error`, {
|
|
79
|
-
stdio: "pipe",
|
|
80
|
-
timeout: 120000,
|
|
81
|
-
});
|
|
82
|
-
blog(`Installed hooks-proxy globally (${version})`);
|
|
83
|
-
} catch {
|
|
84
|
-
try {
|
|
85
|
-
const prefix = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".local");
|
|
86
|
-
execSync(`npm i -g "@a5c-ai/hooks-proxy-cli@${version}" --prefix "${prefix}" --loglevel=error`, {
|
|
87
|
-
stdio: "pipe",
|
|
88
|
-
timeout: 120000,
|
|
89
|
-
});
|
|
90
|
-
blog(`Installed hooks-proxy to user prefix (${version})`);
|
|
91
|
-
} catch {
|
|
92
|
-
blog("hooks-proxy installation failed");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try { writeFileSync(PROXY_MARKER, version); } catch { /* best-effort */ }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function main() {
|
|
100
|
-
const sessionId = process.env.BABYSITTER_SESSION_ID
|
|
101
|
-
|| process.env.OMP_SESSION_ID
|
|
102
|
-
|| process.env.PI_SESSION_ID
|
|
103
|
-
|| "";
|
|
104
|
-
|
|
105
|
-
if (!sessionId) {
|
|
106
|
-
process.stdout.write("{}\n");
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Read stdin for provider request context
|
|
111
|
-
let inputData = "";
|
|
112
|
-
try {
|
|
113
|
-
inputData = readFileSync(0, "utf8");
|
|
114
|
-
} catch {
|
|
115
|
-
// No stdin available
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
blog(`Unified before-provider-request: session=${sessionId}`);
|
|
119
|
-
|
|
120
|
-
const hookInput = JSON.stringify({
|
|
121
|
-
session_id: sessionId,
|
|
122
|
-
cwd: process.cwd(),
|
|
123
|
-
harness: "oh-my-pi",
|
|
124
|
-
plugin_root: PLUGIN_ROOT,
|
|
125
|
-
provider_request: inputData ? JSON.parse(inputData) : {},
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const sdkVersion = getSdkVersion();
|
|
129
|
-
|
|
130
|
-
// Ensure hooks-proxy is installed
|
|
131
|
-
let proxy = resolveHooksProxy();
|
|
132
|
-
if (!proxy) {
|
|
133
|
-
installHooksProxy(sdkVersion);
|
|
134
|
-
proxy = resolveHooksProxy();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const handler = `babysitter hook:run --harness unified --hook-type pre-tool-use --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
let result;
|
|
141
|
-
if (proxy) {
|
|
142
|
-
blog(`Using hooks-proxy: ${proxy}`);
|
|
143
|
-
result = execSync(`"${proxy}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
144
|
-
input: hookInput,
|
|
145
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
146
|
-
timeout: 10000,
|
|
147
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
148
|
-
});
|
|
149
|
-
} else {
|
|
150
|
-
blog("hooks-proxy not found after install, using npx fallback");
|
|
151
|
-
result = execSync(`npx -y "@a5c-ai/hooks-proxy-cli@${sdkVersion}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
152
|
-
input: hookInput,
|
|
153
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
154
|
-
timeout: 30000,
|
|
155
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
const output = result.toString("utf8").trim();
|
|
159
|
-
blog(`Hook result: ${output}`);
|
|
160
|
-
process.stdout.write((output || "{}") + "\n");
|
|
161
|
-
} catch (err) {
|
|
162
|
-
blog(`Before-provider-request hook failed: ${err.message} -- non-blocking`);
|
|
163
|
-
process.stdout.write("{}\n");
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
main();
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Unified Context Hook for Oh-My-Pi
|
|
4
|
-
* Routes through hooks-proxy for all hook execution.
|
|
5
|
-
*
|
|
6
|
-
* Fires when Oh-My-Pi requests context injection before the agent
|
|
7
|
-
* processes a turn. Delegates to
|
|
8
|
-
* `babysitter hook:run --hook-type user-prompt-submit` via hooks-proxy.
|
|
9
|
-
*
|
|
10
|
-
* Oh-My-Pi supports native additional context and chained context
|
|
11
|
-
* propagation with session-before short-circuit semantics.
|
|
12
|
-
*
|
|
13
|
-
* Oh-My-Pi plugin protocol (programmatic):
|
|
14
|
-
* - Called from extension via execSync
|
|
15
|
-
* - Receives context request as JSON via stdin
|
|
16
|
-
* - Outputs JSON to stdout with context additions
|
|
17
|
-
* - Exit 0 = success
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
"use strict";
|
|
21
|
-
|
|
22
|
-
const { execSync } = require("child_process");
|
|
23
|
-
const { readFileSync, mkdirSync, appendFileSync, existsSync, writeFileSync } = require("fs");
|
|
24
|
-
const os = require("os");
|
|
25
|
-
const path = require("path");
|
|
26
|
-
|
|
27
|
-
const PLUGIN_ROOT = process.env.OMP_PLUGIN_ROOT || path.resolve(__dirname, "..");
|
|
28
|
-
const GLOBAL_ROOT = process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(os.homedir(), ".a5c");
|
|
29
|
-
const STATE_DIR = process.env.BABYSITTER_STATE_DIR || path.join(GLOBAL_ROOT, "state");
|
|
30
|
-
const LOG_DIR = process.env.BABYSITTER_LOG_DIR || path.join(GLOBAL_ROOT, "logs");
|
|
31
|
-
const LOG_FILE = path.join(LOG_DIR, "babysitter-omp-context-hook.log");
|
|
32
|
-
const PROXY_MARKER = path.join(PLUGIN_ROOT, ".hooks-proxy-install-attempted");
|
|
33
|
-
|
|
34
|
-
function ensureDir(dir) {
|
|
35
|
-
try { mkdirSync(dir, { recursive: true }); } catch { /* best-effort */ }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function blog(msg) {
|
|
39
|
-
ensureDir(LOG_DIR);
|
|
40
|
-
const ts = new Date().toISOString();
|
|
41
|
-
try {
|
|
42
|
-
appendFileSync(LOG_FILE, `[INFO] ${ts} ${msg}\n`);
|
|
43
|
-
} catch { /* best-effort */ }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getSdkVersion() {
|
|
47
|
-
try {
|
|
48
|
-
const versions = JSON.parse(readFileSync(path.join(PLUGIN_ROOT, "versions.json"), "utf8"));
|
|
49
|
-
return versions.sdkVersion || "latest";
|
|
50
|
-
} catch {
|
|
51
|
-
return "latest";
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function resolveHooksProxy() {
|
|
56
|
-
try {
|
|
57
|
-
execSync("a5c-hooks-proxy --version", { stdio: "pipe", timeout: 5000 });
|
|
58
|
-
return "a5c-hooks-proxy";
|
|
59
|
-
} catch { /* not in PATH */ }
|
|
60
|
-
|
|
61
|
-
const localProxy = path.join(
|
|
62
|
-
process.env.HOME || process.env.USERPROFILE || "~",
|
|
63
|
-
".local", "bin", process.platform === "win32" ? "a5c-hooks-proxy.exe" : "a5c-hooks-proxy"
|
|
64
|
-
);
|
|
65
|
-
if (existsSync(localProxy)) {
|
|
66
|
-
return localProxy;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function installHooksProxy(version) {
|
|
73
|
-
if (existsSync(PROXY_MARKER)) return;
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
execSync(`npm i -g "@a5c-ai/hooks-proxy-cli@${version}" --loglevel=error`, {
|
|
77
|
-
stdio: "pipe",
|
|
78
|
-
timeout: 120000,
|
|
79
|
-
});
|
|
80
|
-
blog(`Installed hooks-proxy globally (${version})`);
|
|
81
|
-
} catch {
|
|
82
|
-
try {
|
|
83
|
-
const prefix = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".local");
|
|
84
|
-
execSync(`npm i -g "@a5c-ai/hooks-proxy-cli@${version}" --prefix "${prefix}" --loglevel=error`, {
|
|
85
|
-
stdio: "pipe",
|
|
86
|
-
timeout: 120000,
|
|
87
|
-
});
|
|
88
|
-
blog(`Installed hooks-proxy to user prefix (${version})`);
|
|
89
|
-
} catch {
|
|
90
|
-
blog("hooks-proxy installation failed");
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try { writeFileSync(PROXY_MARKER, version); } catch { /* best-effort */ }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function main() {
|
|
98
|
-
const sessionId = process.env.BABYSITTER_SESSION_ID
|
|
99
|
-
|| process.env.OMP_SESSION_ID
|
|
100
|
-
|| process.env.PI_SESSION_ID
|
|
101
|
-
|| "";
|
|
102
|
-
|
|
103
|
-
if (!sessionId) {
|
|
104
|
-
process.stdout.write("{}\n");
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Read stdin for context request
|
|
109
|
-
let inputData = "";
|
|
110
|
-
try {
|
|
111
|
-
inputData = readFileSync(0, "utf8");
|
|
112
|
-
} catch {
|
|
113
|
-
// No stdin available
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
blog(`Unified context: session=${sessionId}`);
|
|
117
|
-
|
|
118
|
-
const hookInput = JSON.stringify({
|
|
119
|
-
session_id: sessionId,
|
|
120
|
-
cwd: process.cwd(),
|
|
121
|
-
harness: "oh-my-pi",
|
|
122
|
-
plugin_root: PLUGIN_ROOT,
|
|
123
|
-
context_request: inputData ? JSON.parse(inputData) : {},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const sdkVersion = getSdkVersion();
|
|
127
|
-
|
|
128
|
-
// Ensure hooks-proxy is installed
|
|
129
|
-
let proxy = resolveHooksProxy();
|
|
130
|
-
if (!proxy) {
|
|
131
|
-
installHooksProxy(sdkVersion);
|
|
132
|
-
proxy = resolveHooksProxy();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const handler = `babysitter hook:run --harness unified --hook-type user-prompt-submit --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
let result;
|
|
139
|
-
if (proxy) {
|
|
140
|
-
blog(`Using hooks-proxy: ${proxy}`);
|
|
141
|
-
result = execSync(`"${proxy}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
142
|
-
input: hookInput,
|
|
143
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
144
|
-
timeout: 10000,
|
|
145
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
146
|
-
});
|
|
147
|
-
} else {
|
|
148
|
-
blog("hooks-proxy not found after install, using npx fallback");
|
|
149
|
-
result = execSync(`npx -y "@a5c-ai/hooks-proxy-cli@${sdkVersion}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
150
|
-
input: hookInput,
|
|
151
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
152
|
-
timeout: 30000,
|
|
153
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
const output = result.toString("utf8").trim();
|
|
157
|
-
blog(`Hook result: ${output}`);
|
|
158
|
-
process.stdout.write((output || "{}") + "\n");
|
|
159
|
-
} catch (err) {
|
|
160
|
-
blog(`Context hook failed: ${err.message} -- returning empty context`);
|
|
161
|
-
process.stdout.write("{}\n");
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
main();
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Unified Session Start Hook for Oh-My-Pi
|
|
4
|
-
* Routes through hooks-proxy for all hook execution.
|
|
5
|
-
*
|
|
6
|
-
* Fires when an Oh-My-Pi session begins. Ensures the babysitter SDK CLI
|
|
7
|
-
* and hooks-proxy are installed, then delegates to
|
|
8
|
-
* `babysitter hook:run --hook-type session-start` via hooks-proxy.
|
|
9
|
-
*
|
|
10
|
-
* Oh-My-Pi plugin protocol (programmatic):
|
|
11
|
-
* - Called from extension activate() via execSync
|
|
12
|
-
* - Receives event context as JSON via stdin
|
|
13
|
-
* - Outputs JSON to stdout
|
|
14
|
-
* - Exit 0 = success
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
"use strict";
|
|
18
|
-
|
|
19
|
-
const { execSync } = require("child_process");
|
|
20
|
-
const { readFileSync, mkdirSync, appendFileSync, existsSync, writeFileSync } = require("fs");
|
|
21
|
-
const os = require("os");
|
|
22
|
-
const path = require("path");
|
|
23
|
-
const crypto = require("crypto");
|
|
24
|
-
|
|
25
|
-
const PLUGIN_ROOT = process.env.OMP_PLUGIN_ROOT || path.resolve(__dirname, "..");
|
|
26
|
-
const GLOBAL_ROOT = process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(os.homedir(), ".a5c");
|
|
27
|
-
const STATE_DIR = process.env.BABYSITTER_STATE_DIR || path.join(GLOBAL_ROOT, "state");
|
|
28
|
-
const LOG_DIR = process.env.BABYSITTER_LOG_DIR || path.join(GLOBAL_ROOT, "logs");
|
|
29
|
-
const LOG_FILE = path.join(LOG_DIR, "babysitter-omp-session-start-hook.log");
|
|
30
|
-
const SDK_MARKER = path.join(PLUGIN_ROOT, ".babysitter-install-attempted");
|
|
31
|
-
const PROXY_MARKER = path.join(PLUGIN_ROOT, ".hooks-proxy-install-attempted");
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Logging
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
function ensureDir(dir) {
|
|
38
|
-
try { mkdirSync(dir, { recursive: true }); } catch { /* best-effort */ }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function blog(msg) {
|
|
42
|
-
ensureDir(LOG_DIR);
|
|
43
|
-
const ts = new Date().toISOString();
|
|
44
|
-
try {
|
|
45
|
-
appendFileSync(LOG_FILE, `[INFO] ${ts} ${msg}\n`);
|
|
46
|
-
} catch { /* best-effort */ }
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// SDK version & install
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
|
|
53
|
-
function getSdkVersion() {
|
|
54
|
-
try {
|
|
55
|
-
const versions = JSON.parse(readFileSync(path.join(PLUGIN_ROOT, "versions.json"), "utf8"));
|
|
56
|
-
return versions.sdkVersion || "latest";
|
|
57
|
-
} catch {
|
|
58
|
-
return "latest";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function getInstalledVersion(cmd) {
|
|
63
|
-
try {
|
|
64
|
-
return execSync(`${cmd} --version`, { stdio: "pipe", timeout: 10000 }).toString().trim();
|
|
65
|
-
} catch {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function installPackage(npmPkg, version, marker) {
|
|
71
|
-
if (existsSync(marker)) return;
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
execSync(`npm i -g "${npmPkg}@${version}" --loglevel=error`, {
|
|
75
|
-
stdio: "pipe",
|
|
76
|
-
timeout: 120000,
|
|
77
|
-
});
|
|
78
|
-
blog(`Installed ${npmPkg} globally (${version})`);
|
|
79
|
-
} catch {
|
|
80
|
-
// Try user-local prefix
|
|
81
|
-
try {
|
|
82
|
-
const prefix = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".local");
|
|
83
|
-
execSync(`npm i -g "${npmPkg}@${version}" --prefix "${prefix}" --loglevel=error`, {
|
|
84
|
-
stdio: "pipe",
|
|
85
|
-
timeout: 120000,
|
|
86
|
-
});
|
|
87
|
-
blog(`Installed ${npmPkg} to user prefix (${version})`);
|
|
88
|
-
} catch {
|
|
89
|
-
blog(`${npmPkg} installation failed`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
try { writeFileSync(marker, version); } catch { /* best-effort */ }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
// Hooks-proxy resolution & install
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
|
|
100
|
-
function resolveHooksProxy() {
|
|
101
|
-
// Check PATH first
|
|
102
|
-
try {
|
|
103
|
-
execSync("a5c-hooks-proxy --version", { stdio: "pipe", timeout: 5000 });
|
|
104
|
-
return "a5c-hooks-proxy";
|
|
105
|
-
} catch { /* not in PATH */ }
|
|
106
|
-
|
|
107
|
-
// Check user-local install
|
|
108
|
-
const localProxy = path.join(
|
|
109
|
-
process.env.HOME || process.env.USERPROFILE || "~",
|
|
110
|
-
".local", "bin", process.platform === "win32" ? "a5c-hooks-proxy.exe" : "a5c-hooks-proxy"
|
|
111
|
-
);
|
|
112
|
-
if (existsSync(localProxy)) {
|
|
113
|
-
return localProxy;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
// CLI execution helpers
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
|
|
123
|
-
function runViaProxy(proxy, hookType, inputJson) {
|
|
124
|
-
const handler = `babysitter hook:run --harness unified --hook-type ${hookType} --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
|
|
125
|
-
const result = execSync(`"${proxy}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
126
|
-
input: inputJson,
|
|
127
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
128
|
-
timeout: 30000,
|
|
129
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
130
|
-
});
|
|
131
|
-
return result.toString("utf8").trim();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function runViaNpxProxy(version, hookType, inputJson) {
|
|
135
|
-
const handler = `babysitter hook:run --harness unified --hook-type ${hookType} --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
|
|
136
|
-
const result = execSync(`npx -y "@a5c-ai/hooks-proxy-cli@${version}" invoke --adapter oh-my-pi --handler "${handler}" --json`, {
|
|
137
|
-
input: inputJson,
|
|
138
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
139
|
-
timeout: 60000,
|
|
140
|
-
env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
|
|
141
|
-
});
|
|
142
|
-
return result.toString("utf8").trim();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
// Main
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
|
|
149
|
-
function main() {
|
|
150
|
-
blog("Unified session-start hook invoked");
|
|
151
|
-
blog(`PLUGIN_ROOT=${PLUGIN_ROOT}`);
|
|
152
|
-
|
|
153
|
-
// Generate a session ID if Oh-My-Pi doesn't provide one
|
|
154
|
-
const sessionId = process.env.OMP_SESSION_ID
|
|
155
|
-
|| process.env.PI_SESSION_ID
|
|
156
|
-
|| process.env.BABYSITTER_SESSION_ID
|
|
157
|
-
|| crypto.randomUUID();
|
|
158
|
-
|
|
159
|
-
// Set env var so downstream hooks can pick it up
|
|
160
|
-
process.env.BABYSITTER_SESSION_ID = sessionId;
|
|
161
|
-
|
|
162
|
-
const sdkVersion = getSdkVersion();
|
|
163
|
-
|
|
164
|
-
// Ensure SDK is installed with version sync
|
|
165
|
-
const currentSdkVersion = getInstalledVersion("babysitter");
|
|
166
|
-
if (!currentSdkVersion || currentSdkVersion !== sdkVersion) {
|
|
167
|
-
blog(`SDK needs install/upgrade: installed=${currentSdkVersion || "none"}, required=${sdkVersion}`);
|
|
168
|
-
installPackage("@a5c-ai/babysitter-sdk", sdkVersion, SDK_MARKER);
|
|
169
|
-
} else {
|
|
170
|
-
blog(`SDK version OK: ${currentSdkVersion}`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Ensure hooks-proxy is installed with version sync
|
|
174
|
-
const currentProxyVersion = getInstalledVersion("a5c-hooks-proxy");
|
|
175
|
-
if (!currentProxyVersion || currentProxyVersion !== sdkVersion) {
|
|
176
|
-
blog(`hooks-proxy needs install/upgrade: installed=${currentProxyVersion || "none"}, required=${sdkVersion}`);
|
|
177
|
-
installPackage("@a5c-ai/hooks-proxy-cli", sdkVersion, PROXY_MARKER);
|
|
178
|
-
} else {
|
|
179
|
-
blog(`hooks-proxy version OK: ${currentProxyVersion}`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Read stdin if available
|
|
183
|
-
let stdinData = "";
|
|
184
|
-
try {
|
|
185
|
-
stdinData = readFileSync(0, "utf8");
|
|
186
|
-
} catch { /* no stdin */ }
|
|
187
|
-
|
|
188
|
-
// Build hook input
|
|
189
|
-
const hookInput = JSON.stringify({
|
|
190
|
-
session_id: sessionId,
|
|
191
|
-
cwd: process.cwd(),
|
|
192
|
-
harness: "oh-my-pi",
|
|
193
|
-
plugin_root: PLUGIN_ROOT,
|
|
194
|
-
...(stdinData ? { event_data: JSON.parse(stdinData) } : {}),
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
blog(`Hook input: ${hookInput}`);
|
|
198
|
-
|
|
199
|
-
// Route through hooks-proxy (install or npx fallback)
|
|
200
|
-
const proxy = resolveHooksProxy();
|
|
201
|
-
let result;
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
if (proxy) {
|
|
205
|
-
blog(`Using hooks-proxy: ${proxy}`);
|
|
206
|
-
result = runViaProxy(proxy, "session-start", hookInput);
|
|
207
|
-
} else {
|
|
208
|
-
blog("hooks-proxy not found after install, using npx fallback");
|
|
209
|
-
result = runViaNpxProxy(sdkVersion, "session-start", hookInput);
|
|
210
|
-
}
|
|
211
|
-
} catch (err) {
|
|
212
|
-
blog(`Hook execution failed: ${err.message}`);
|
|
213
|
-
result = "{}";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
blog(`Hook result: ${result}`);
|
|
217
|
-
|
|
218
|
-
// Output result
|
|
219
|
-
try {
|
|
220
|
-
const parsed = JSON.parse(result);
|
|
221
|
-
process.stdout.write(JSON.stringify(parsed) + "\n");
|
|
222
|
-
} catch {
|
|
223
|
-
process.stdout.write("{}\n");
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
main();
|