@a5c-ai/babysitter-opencode 5.0.1-staging.e4f17eff → 5.0.1-staging.e920fef118ef

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 (55) hide show
  1. package/README.md +19 -21
  2. package/bin/cli.cjs +1 -191
  3. package/bin/cli.js +90 -49
  4. package/bin/install-shared.cjs +1 -478
  5. package/bin/install-shared.js +615 -0
  6. package/bin/install.cjs +1 -143
  7. package/bin/install.js +18 -98
  8. package/bin/uninstall.cjs +1 -87
  9. package/bin/uninstall.js +12 -34
  10. package/commands/call.md +5 -1
  11. package/commands/cleanup.md +30 -8
  12. package/commands/doctor.md +7 -8
  13. package/commands/help.md +246 -244
  14. package/commands/observe.md +17 -12
  15. package/commands/yolo.md +11 -7
  16. package/hooks/babysitter-proxied-session-created.js +18 -212
  17. package/hooks/babysitter-proxied-session-created.sh +11 -0
  18. package/hooks/babysitter-proxied-shell-env.js +21 -145
  19. package/hooks/babysitter-proxied-shell-env.sh +3 -0
  20. package/hooks/babysitter-proxied-stop-hook.sh +3 -0
  21. package/hooks/babysitter-proxied-tool-execute-after.js +20 -160
  22. package/hooks/babysitter-proxied-tool-execute-after.sh +3 -0
  23. package/hooks/babysitter-proxied-tool-execute-before.js +20 -162
  24. package/hooks/babysitter-proxied-tool-execute-before.sh +3 -0
  25. package/hooks/hooks.json +14 -22
  26. package/package.json +21 -19
  27. package/plugin.json +6 -4
  28. package/scripts/create-release-tag.mjs +18 -0
  29. package/scripts/publish-from-tag.mjs +41 -0
  30. package/scripts/team-install.js +23 -0
  31. package/skills/accomplish-status/SKILL.md +1 -1
  32. package/skills/babysit/SKILL.md +4 -3
  33. package/skills/call/SKILL.md +5 -1
  34. package/skills/cleanup/SKILL.md +30 -8
  35. package/skills/contrib/SKILL.md +25 -25
  36. package/skills/doctor/SKILL.md +7 -8
  37. package/skills/help/SKILL.md +4 -2
  38. package/skills/observe/SKILL.md +7 -2
  39. package/skills/plugins/SKILL.md +243 -243
  40. package/skills/project-install/SKILL.md +3 -3
  41. package/skills/resume/SKILL.md +1 -1
  42. package/skills/retrospect/SKILL.md +48 -48
  43. package/skills/user-install/SKILL.md +3 -3
  44. package/skills/yolo/SKILL.md +5 -1
  45. package/versions.json +2 -2
  46. package/hooks/babysitter-proxied-session-idle.js +0 -173
  47. package/hooks/hooks.json.legacy +0 -46
  48. package/hooks/proxied-hooks.json +0 -47
  49. package/hooks/session-created.js +0 -182
  50. package/hooks/session-idle.js +0 -124
  51. package/hooks/shell-env.js +0 -88
  52. package/hooks/tool-execute-after.js +0 -107
  53. package/hooks/tool-execute-before.js +0 -109
  54. package/scripts/sync-command-docs.cjs +0 -107
  55. package/scripts/sync-command-surfaces.js +0 -52
@@ -1,218 +1,24 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Unified Session Created Hook for OpenCode
4
- * Routes through hooks-proxy for all hook execution.
5
- *
6
- * Fires when an OpenCode session is created. 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
- * OpenCode plugin protocol:
11
- * - Receives event context as JSON via process.argv or stdin
12
- * - Outputs JSON to stdout
13
- * - Exit 0 = success
14
- */
15
-
16
2
  "use strict";
17
-
18
- const { execSync, execFileSync } = require("child_process");
19
- const { readFileSync, mkdirSync, appendFileSync, existsSync, writeFileSync } = require("fs");
20
- const os = require("os");
21
- const path = require("path");
22
- const crypto = require("crypto");
23
-
24
- const PLUGIN_ROOT = process.env.OPENCODE_PLUGIN_ROOT || path.resolve(__dirname, "..");
25
- const GLOBAL_ROOT = process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(os.homedir(), ".a5c");
26
- const STATE_DIR = process.env.BABYSITTER_STATE_DIR || path.join(GLOBAL_ROOT, "state");
27
- const LOG_DIR = process.env.BABYSITTER_LOG_DIR || path.join(GLOBAL_ROOT, "logs");
28
- const LOG_FILE = path.join(LOG_DIR, "babysitter-session-created-hook.log");
29
- const SDK_MARKER = path.join(PLUGIN_ROOT, ".babysitter-install-attempted");
30
- const PROXY_MARKER = path.join(PLUGIN_ROOT, ".hooks-proxy-install-attempted");
31
-
32
- // ---------------------------------------------------------------------------
33
- // Logging
34
- // ---------------------------------------------------------------------------
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
- // ---------------------------------------------------------------------------
49
- // SDK version & install
50
- // ---------------------------------------------------------------------------
51
-
52
- function getSdkVersion() {
53
- try {
54
- const versions = JSON.parse(readFileSync(path.join(PLUGIN_ROOT, "versions.json"), "utf8"));
55
- return versions.sdkVersion || "latest";
56
- } catch {
57
- return "latest";
58
- }
59
- }
60
-
61
- function getInstalledVersion(cmd) {
62
- try {
63
- return execSync(`${cmd} --version`, { stdio: "pipe", timeout: 10000 }).toString().trim();
64
- } catch {
65
- return null;
66
- }
67
- }
68
-
69
- function installPackage(npmPkg, version, marker) {
70
- if (existsSync(marker)) return;
71
-
72
- try {
73
- execSync(`npm i -g "${npmPkg}@${version}" --loglevel=error`, {
74
- stdio: "pipe",
75
- timeout: 120000,
76
- });
77
- blog(`Installed ${npmPkg} globally (${version})`);
78
- } catch {
79
- // Try user-local prefix
80
- try {
81
- const prefix = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".local");
82
- execSync(`npm i -g "${npmPkg}@${version}" --prefix "${prefix}" --loglevel=error`, {
83
- stdio: "pipe",
84
- timeout: 120000,
85
- });
86
- blog(`Installed ${npmPkg} to user prefix (${version})`);
87
- } catch {
88
- blog(`${npmPkg} installation failed`);
89
- }
90
- }
91
-
92
- try { writeFileSync(marker, version); } catch { /* best-effort */ }
93
- }
94
-
95
- // ---------------------------------------------------------------------------
96
- // Hooks-proxy resolution & install
97
- // ---------------------------------------------------------------------------
98
-
99
- function resolveHooksProxy() {
100
- // Check PATH first
101
- try {
102
- execSync("a5c-hooks-proxy --version", { stdio: "pipe", timeout: 5000 });
103
- return "a5c-hooks-proxy";
104
- } catch { /* not in PATH */ }
105
-
106
- // Check user-local install
107
- const localProxy = path.join(
108
- process.env.HOME || process.env.USERPROFILE || "~",
109
- ".local", "bin", process.platform === "win32" ? "a5c-hooks-proxy.exe" : "a5c-hooks-proxy"
110
- );
111
- if (existsSync(localProxy)) {
112
- return localProxy;
113
- }
114
-
115
- return null;
116
- }
117
-
118
- // ---------------------------------------------------------------------------
119
- // CLI execution helpers
120
- // ---------------------------------------------------------------------------
121
-
122
- function runViaProxy(proxy, hookType, inputJson) {
123
- const handler = `babysitter hook:run --harness unified --hook-type ${hookType} --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
124
- const result = execSync(`"${proxy}" invoke --adapter opencode --handler "${handler}" --json`, {
125
- input: inputJson,
3
+ var execSync = require("child_process").execSync;
4
+ var path = require("path");
5
+ var readFileSync = require("fs").readFileSync;
6
+
7
+ var PLUGIN_ROOT = process.env.PLUGIN_ROOT || process.env.PLUGIN_ROOT || path.resolve(__dirname, "..");
8
+ var stdin = "";
9
+ try { stdin = readFileSync(0, "utf8"); } catch {}
10
+ try {
11
+ var result = execSync("bash " + JSON.stringify(path.join(PLUGIN_ROOT, "hooks/session-start.sh")), {
12
+ input: stdin,
126
13
  stdio: ["pipe", "pipe", "pipe"],
127
14
  timeout: 30000,
128
- env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
129
- });
130
- return result.toString("utf8").trim();
131
- }
132
-
133
- function runViaNpxProxy(version, hookType, inputJson) {
134
- const handler = `babysitter hook:run --harness unified --hook-type ${hookType} --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
135
- const result = execSync(`npx -y "@a5c-ai/hooks-proxy-cli@${version}" invoke --adapter opencode --handler "${handler}" --json`, {
136
- input: inputJson,
137
- stdio: ["pipe", "pipe", "pipe"],
138
- timeout: 60000,
139
- env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
140
- });
141
- return result.toString("utf8").trim();
142
- }
143
-
144
- // ---------------------------------------------------------------------------
145
- // Main
146
- // ---------------------------------------------------------------------------
147
-
148
- function main() {
149
- blog("Unified session-created hook invoked");
150
- blog(`PLUGIN_ROOT=${PLUGIN_ROOT}`);
151
-
152
- // Generate a session ID if OpenCode doesn't provide one
153
- const sessionId = process.env.OPENCODE_SESSION_ID
154
- || process.env.BABYSITTER_SESSION_ID
155
- || crypto.randomUUID();
156
-
157
- // Set env var so downstream hooks can pick it up
158
- process.env.BABYSITTER_SESSION_ID = sessionId;
159
-
160
- const sdkVersion = getSdkVersion();
161
-
162
- // Ensure SDK is installed with version sync
163
- const currentSdkVersion = getInstalledVersion("babysitter");
164
- if (!currentSdkVersion || currentSdkVersion !== sdkVersion) {
165
- blog(`SDK needs install/upgrade: installed=${currentSdkVersion || "none"}, required=${sdkVersion}`);
166
- installPackage("@a5c-ai/babysitter-sdk", sdkVersion, SDK_MARKER);
167
- } else {
168
- blog(`SDK version OK: ${currentSdkVersion}`);
169
- }
170
-
171
- // Ensure hooks-proxy is installed with version sync
172
- const currentProxyVersion = getInstalledVersion("a5c-hooks-proxy");
173
- if (!currentProxyVersion || currentProxyVersion !== sdkVersion) {
174
- blog(`hooks-proxy needs install/upgrade: installed=${currentProxyVersion || "none"}, required=${sdkVersion}`);
175
- installPackage("@a5c-ai/hooks-proxy-cli", sdkVersion, PROXY_MARKER);
176
- } else {
177
- blog(`hooks-proxy version OK: ${currentProxyVersion}`);
178
- }
179
-
180
- // Build hook input
181
- const hookInput = JSON.stringify({
182
- session_id: sessionId,
183
- cwd: process.cwd(),
184
- harness: "opencode",
185
- plugin_root: PLUGIN_ROOT,
15
+ env: Object.assign({}, process.env, {
16
+ HOOK_TYPE: process.env.HOOK_TYPE || "",
17
+ ADAPTER_NAME: process.env.ADAPTER_NAME || "opencode",
18
+ PLUGIN_ROOT: PLUGIN_ROOT
19
+ })
186
20
  });
187
-
188
- blog(`Hook input: ${hookInput}`);
189
-
190
- // Route through hooks-proxy (install or npx fallback)
191
- const proxy = resolveHooksProxy();
192
- let result;
193
-
194
- try {
195
- if (proxy) {
196
- blog(`Using hooks-proxy: ${proxy}`);
197
- result = runViaProxy(proxy, "session-start", hookInput);
198
- } else {
199
- blog("hooks-proxy not found after install, using npx fallback");
200
- result = runViaNpxProxy(sdkVersion, "session-start", hookInput);
201
- }
202
- } catch (err) {
203
- blog(`Hook execution failed: ${err.message}`);
204
- result = "{}";
205
- }
206
-
207
- blog(`Hook result: ${result}`);
208
-
209
- // Output result
210
- try {
211
- const parsed = JSON.parse(result);
212
- process.stdout.write(JSON.stringify(parsed) + "\n");
213
- } catch {
214
- process.stdout.write("{}\n");
215
- }
21
+ process.stdout.write(result);
22
+ } catch (e) {
23
+ process.stdout.write("{}\n");
216
24
  }
217
-
218
- main();
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ # Session Start — installs SDK if needed, then runs hook handler.
3
+ set -euo pipefail
4
+ PLUGIN_ROOT="${PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
5
+ SDK_VERSION=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync('${PLUGIN_ROOT}/versions.json','utf8')).sdkVersion||'latest')}catch{console.log('latest')}" 2>/dev/null || echo "latest")
6
+ if ! command -v babysitter &>/dev/null; then
7
+ npm i -g "@a5c-ai/babysitter-sdk@${SDK_VERSION}" --loglevel=error 2>/dev/null || \
8
+ npm i -g "@a5c-ai/babysitter-sdk@${SDK_VERSION}" --prefix "$HOME/.local" --loglevel=error 2>/dev/null || true
9
+ [ -d "$HOME/.local/bin" ] && export PATH="$HOME/.local/bin:$PATH"
10
+ fi
11
+ babysitter hook:run --harness unified --hook-type session-start --json
@@ -1,148 +1,24 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Unified Shell Environment Hook for OpenCode
4
- * Routes through hooks-proxy when available for consistency,
5
- * but primarily performs direct env var injection.
6
- *
7
- * Fires when OpenCode initializes a shell environment. Injects babysitter
8
- * environment variables (BABYSITTER_SESSION_ID, BABYSITTER_STATE_DIR, etc.)
9
- * so that subprocesses and other hooks can discover the active session.
10
- *
11
- * This is critical for OpenCode because it does NOT natively inject
12
- * distinctive env vars into plugins -- the babysitter plugin must self-inject
13
- * them via this hook.
14
- *
15
- * OpenCode plugin protocol:
16
- * - Outputs env var assignments as JSON: { "env": { "KEY": "VALUE" } }
17
- * - Exit 0 = success
18
- */
19
-
20
2
  "use strict";
21
-
22
- const { readFileSync, mkdirSync, appendFileSync, existsSync, writeFileSync } = require("fs");
23
- const { execSync } = require("child_process");
24
- const os = require("os");
25
- const path = require("path");
26
- const crypto = require("crypto");
27
-
28
- const PLUGIN_ROOT = process.env.OPENCODE_PLUGIN_ROOT || path.resolve(__dirname, "..");
29
- const GLOBAL_ROOT = process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(os.homedir(), ".a5c");
30
- const STATE_DIR = process.env.BABYSITTER_STATE_DIR || path.join(GLOBAL_ROOT, "state");
31
- const RUNS_DIR = process.env.BABYSITTER_RUNS_DIR || path.join(GLOBAL_ROOT, "runs");
32
- const LOG_DIR = process.env.BABYSITTER_LOG_DIR || path.join(GLOBAL_ROOT, "logs");
33
- const LOG_FILE = path.join(LOG_DIR, "babysitter-shell-env-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
- blog("Unified shell-env hook invoked");
101
-
102
- // Resolve or generate session ID
103
- const sessionId = process.env.BABYSITTER_SESSION_ID
104
- || process.env.OPENCODE_SESSION_ID
105
- || crypto.randomUUID();
106
-
107
- const sdkVersion = getSdkVersion();
108
-
109
- // Ensure hooks-proxy is installed (for other hooks to use)
110
- const proxy = resolveHooksProxy();
111
- if (!proxy) {
112
- installHooksProxy(sdkVersion);
113
- }
114
-
115
- // Build env vars to inject
116
- const env = {
117
- BABYSITTER_SESSION_ID: sessionId,
118
- OPENCODE_SESSION_ID: sessionId,
119
- BABYSITTER_STATE_DIR: STATE_DIR,
120
- BABYSITTER_RUNS_DIR: RUNS_DIR,
121
- OPENCODE_PLUGIN_ROOT: PLUGIN_ROOT,
122
- };
123
-
124
- // Add SDK version for downstream hooks
125
- if (sdkVersion && sdkVersion !== "latest") {
126
- env.BABYSITTER_SDK_VERSION = sdkVersion;
127
- }
128
-
129
- // Add global state dir if defined
130
- const globalStateDir = process.env.BABYSITTER_GLOBAL_STATE_DIR;
131
- if (globalStateDir) {
132
- env.BABYSITTER_GLOBAL_STATE_DIR = globalStateDir;
133
- }
134
-
135
- // Note: shell-env is purely env injection — hooks-proxy routing is logged
136
- // but the output is always the same env vars object. The proxy can
137
- // potentially enrich env vars in the future.
138
- const resolvedProxy = resolveHooksProxy();
139
- if (resolvedProxy) {
140
- blog(`hooks-proxy available: ${resolvedProxy} (env injection is direct)`);
141
- }
142
-
143
- blog(`Injecting env: ${JSON.stringify(env)}`);
144
-
145
- process.stdout.write(JSON.stringify({ env }) + "\n");
3
+ var execSync = require("child_process").execSync;
4
+ var path = require("path");
5
+ var readFileSync = require("fs").readFileSync;
6
+
7
+ var PLUGIN_ROOT = process.env.PLUGIN_ROOT || process.env.PLUGIN_ROOT || path.resolve(__dirname, "..");
8
+ var stdin = "";
9
+ try { stdin = readFileSync(0, "utf8"); } catch {}
10
+ try {
11
+ var result = execSync("bash " + JSON.stringify(path.join(PLUGIN_ROOT, "hooks/shell-env.sh")), {
12
+ input: stdin,
13
+ stdio: ["pipe", "pipe", "pipe"],
14
+ timeout: 30000,
15
+ env: Object.assign({}, process.env, {
16
+ HOOK_TYPE: process.env.HOOK_TYPE || "",
17
+ ADAPTER_NAME: process.env.ADAPTER_NAME || "opencode",
18
+ PLUGIN_ROOT: PLUGIN_ROOT
19
+ })
20
+ });
21
+ process.stdout.write(result);
22
+ } catch (e) {
23
+ process.stdout.write("{}\n");
146
24
  }
147
-
148
- main();
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ babysitter hook:run --harness unified --hook-type shell-env --json
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ babysitter hook:run --harness unified --hook-type stop --json
@@ -1,164 +1,24 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * Unified Tool Execute After Hook for OpenCode
4
- * Routes through hooks-proxy for all hook execution.
5
- *
6
- * Fires after a tool execution in OpenCode. Delegates to
7
- * `babysitter hook:run --hook-type post-tool-use` via hooks-proxy.
8
- *
9
- * This hook can be used to:
10
- * - Log tool execution results for babysitter run observability
11
- * - Trigger babysitter effects based on tool outputs
12
- * - Update session state after tool executions
13
- *
14
- * OpenCode plugin protocol:
15
- * - Receives tool result context as JSON via stdin
16
- * - Outputs JSON to stdout
17
- * - Exit 0 = success
18
- */
19
-
20
2
  "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.OPENCODE_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-tool-after-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.OPENCODE_SESSION_ID
100
- || "";
101
-
102
- if (!sessionId) {
103
- process.stdout.write("{}\n");
104
- return;
105
- }
106
-
107
- // Read stdin for tool result context
108
- let inputData = "";
109
- try {
110
- inputData = require("fs").readFileSync(0, "utf8");
111
- } catch {
112
- // No stdin available
113
- }
114
-
115
- blog(`Unified tool-execute-after: session=${sessionId}`);
116
-
117
- const hookInput = JSON.stringify({
118
- session_id: sessionId,
119
- cwd: process.cwd(),
120
- harness: "opencode",
121
- plugin_root: PLUGIN_ROOT,
122
- tool_result: inputData ? JSON.parse(inputData) : {},
3
+ var execSync = require("child_process").execSync;
4
+ var path = require("path");
5
+ var readFileSync = require("fs").readFileSync;
6
+
7
+ var PLUGIN_ROOT = process.env.PLUGIN_ROOT || process.env.PLUGIN_ROOT || path.resolve(__dirname, "..");
8
+ var stdin = "";
9
+ try { stdin = readFileSync(0, "utf8"); } catch {}
10
+ try {
11
+ var result = execSync("bash " + JSON.stringify(path.join(PLUGIN_ROOT, "hooks/post-tool-use.sh")), {
12
+ input: stdin,
13
+ stdio: ["pipe", "pipe", "pipe"],
14
+ timeout: 30000,
15
+ env: Object.assign({}, process.env, {
16
+ HOOK_TYPE: process.env.HOOK_TYPE || "",
17
+ ADAPTER_NAME: process.env.ADAPTER_NAME || "opencode",
18
+ PLUGIN_ROOT: PLUGIN_ROOT
19
+ })
123
20
  });
124
-
125
- const sdkVersion = getSdkVersion();
126
-
127
- // Ensure hooks-proxy is installed
128
- let proxy = resolveHooksProxy();
129
- if (!proxy) {
130
- installHooksProxy(sdkVersion);
131
- proxy = resolveHooksProxy();
132
- }
133
-
134
- const handler = `babysitter hook:run --harness unified --hook-type post-tool-use --plugin-root ${PLUGIN_ROOT} --state-dir ${STATE_DIR} --json`;
135
-
136
- try {
137
- let result;
138
- if (proxy) {
139
- blog(`Using hooks-proxy: ${proxy}`);
140
- result = execSync(`"${proxy}" invoke --adapter opencode --handler "${handler}" --json`, {
141
- input: hookInput,
142
- stdio: ["pipe", "pipe", "pipe"],
143
- timeout: 10000,
144
- env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
145
- });
146
- } else {
147
- blog("hooks-proxy not found after install, using npx fallback");
148
- result = execSync(`npx -y "@a5c-ai/hooks-proxy-cli@${sdkVersion}" invoke --adapter opencode --handler "${handler}" --json`, {
149
- input: hookInput,
150
- stdio: ["pipe", "pipe", "pipe"],
151
- timeout: 30000,
152
- env: { ...process.env, BABYSITTER_STATE_DIR: STATE_DIR },
153
- });
154
- }
155
- const output = result.toString("utf8").trim();
156
- blog(`Hook result: ${output}`);
157
- process.stdout.write((output || "{}") + "\n");
158
- } catch (err) {
159
- blog(`Post-tool-use hook failed: ${err.message} -- non-blocking`);
160
- process.stdout.write("{}\n");
161
- }
21
+ process.stdout.write(result);
22
+ } catch (e) {
23
+ process.stdout.write("{}\n");
162
24
  }
163
-
164
- main();
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ babysitter hook:run --harness unified --hook-type post-tool-use --json