@a5c-ai/babysitter-opencode 0.1.1-staging.0dc03363
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/README.md +169 -0
- package/bin/cli.cjs +194 -0
- package/bin/cli.js +55 -0
- package/bin/install-shared.cjs +406 -0
- package/bin/install.cjs +97 -0
- package/bin/install.js +110 -0
- package/bin/uninstall.cjs +90 -0
- package/bin/uninstall.js +46 -0
- package/commands/assimilate.md +37 -0
- package/commands/call.md +7 -0
- package/commands/cleanup.md +20 -0
- package/commands/contrib.md +33 -0
- package/commands/doctor.md +426 -0
- package/commands/forever.md +7 -0
- package/commands/help.md +244 -0
- package/commands/observe.md +12 -0
- package/commands/plan.md +7 -0
- package/commands/plugins.md +255 -0
- package/commands/project-install.md +17 -0
- package/commands/resume.md +8 -0
- package/commands/retrospect.md +55 -0
- package/commands/status.md +8 -0
- package/commands/user-install.md +17 -0
- package/commands/yolo.md +7 -0
- package/hooks/hooks.json +46 -0
- package/hooks/session-created.js +180 -0
- package/hooks/session-idle.js +122 -0
- package/hooks/shell-env.js +86 -0
- package/hooks/tool-execute-after.js +105 -0
- package/hooks/tool-execute-before.js +107 -0
- package/package.json +46 -0
- package/plugin.json +25 -0
- package/scripts/sync-command-docs.cjs +105 -0
- package/scripts/sync-command-surfaces.js +52 -0
- package/skills/assimilate/SKILL.md +38 -0
- package/skills/babysit/SKILL.md +35 -0
- package/skills/call/SKILL.md +8 -0
- package/skills/cleanup/SKILL.md +21 -0
- package/skills/contrib/SKILL.md +34 -0
- package/skills/doctor/SKILL.md +427 -0
- package/skills/forever/SKILL.md +8 -0
- package/skills/help/SKILL.md +245 -0
- package/skills/observe/SKILL.md +13 -0
- package/skills/plan/SKILL.md +8 -0
- package/skills/plugins/SKILL.md +257 -0
- package/skills/project-install/SKILL.md +18 -0
- package/skills/resume/SKILL.md +9 -0
- package/skills/retrospect/SKILL.md +56 -0
- package/skills/user-install/SKILL.md +18 -0
- package/skills/yolo/SKILL.md +8 -0
- package/versions.json +4 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared installation utilities for babysitter-opencode plugin.
|
|
6
|
+
*
|
|
7
|
+
* Handles plugin bundle copying, OpenCode config registration,
|
|
8
|
+
* marketplace entry management, and process library bootstrapping.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { spawnSync } = require('child_process');
|
|
15
|
+
|
|
16
|
+
const PLUGIN_NAME = 'babysitter';
|
|
17
|
+
|
|
18
|
+
const PLUGIN_BUNDLE_ENTRIES = [
|
|
19
|
+
'plugin.json',
|
|
20
|
+
'versions.json',
|
|
21
|
+
'hooks',
|
|
22
|
+
'skills',
|
|
23
|
+
'commands',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const HOOK_SCRIPT_NAMES = [
|
|
27
|
+
'session-created.js',
|
|
28
|
+
'session-idle.js',
|
|
29
|
+
'shell-env.js',
|
|
30
|
+
'tool-execute-before.js',
|
|
31
|
+
'tool-execute-after.js',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const DEFAULT_MARKETPLACE = {
|
|
35
|
+
name: 'local-plugins',
|
|
36
|
+
interface: {
|
|
37
|
+
displayName: 'Local Plugins',
|
|
38
|
+
},
|
|
39
|
+
plugins: [],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Path helpers
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
function getUserHome() {
|
|
47
|
+
if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
|
|
48
|
+
if (process.env.HOME) return path.resolve(process.env.HOME);
|
|
49
|
+
return os.homedir();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getGlobalStateDir() {
|
|
53
|
+
if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
|
|
54
|
+
return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
|
|
55
|
+
}
|
|
56
|
+
return path.join(getUserHome(), '.a5c');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the OpenCode config root.
|
|
61
|
+
* OpenCode uses `.opencode/` in the workspace directory by default.
|
|
62
|
+
* Respect OPENCODE_HOME env var if set.
|
|
63
|
+
*/
|
|
64
|
+
function getOpenCodeHome(workspace) {
|
|
65
|
+
if (process.env.OPENCODE_HOME) return path.resolve(process.env.OPENCODE_HOME);
|
|
66
|
+
return path.join(workspace || process.cwd(), '.opencode');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getHomePluginRoot(workspace) {
|
|
70
|
+
if (process.env.BABYSITTER_OPENCODE_PLUGIN_DIR) {
|
|
71
|
+
return path.resolve(process.env.BABYSITTER_OPENCODE_PLUGIN_DIR, PLUGIN_NAME);
|
|
72
|
+
}
|
|
73
|
+
return path.join(getOpenCodeHome(workspace), 'plugins', PLUGIN_NAME);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getHomeMarketplacePath(workspace) {
|
|
77
|
+
if (process.env.BABYSITTER_OPENCODE_MARKETPLACE_PATH) {
|
|
78
|
+
return path.resolve(process.env.BABYSITTER_OPENCODE_MARKETPLACE_PATH);
|
|
79
|
+
}
|
|
80
|
+
return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// File utilities
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
function writeFileIfChanged(filePath, contents) {
|
|
88
|
+
if (fs.existsSync(filePath)) {
|
|
89
|
+
const current = fs.readFileSync(filePath, 'utf8');
|
|
90
|
+
if (current === contents) return false;
|
|
91
|
+
}
|
|
92
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
93
|
+
fs.writeFileSync(filePath, contents, 'utf8');
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readJson(filePath) {
|
|
98
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function writeJson(filePath, value) {
|
|
102
|
+
writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function copyRecursive(src, dest) {
|
|
106
|
+
const stat = fs.statSync(src);
|
|
107
|
+
if (stat.isDirectory()) {
|
|
108
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
109
|
+
for (const entry of fs.readdirSync(src)) {
|
|
110
|
+
if (['node_modules', '.git', 'test', '.a5c'].includes(entry)) continue;
|
|
111
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Strip BOM from SKILL.md files
|
|
117
|
+
if (path.basename(src) === 'SKILL.md') {
|
|
118
|
+
const file = fs.readFileSync(src);
|
|
119
|
+
const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
|
|
120
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
121
|
+
fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
126
|
+
fs.copyFileSync(src, dest);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Plugin bundle
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
function copyPluginBundle(packageRoot, pluginRoot) {
|
|
134
|
+
if (path.resolve(packageRoot) === path.resolve(pluginRoot)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
138
|
+
fs.mkdirSync(pluginRoot, { recursive: true });
|
|
139
|
+
for (const entry of PLUGIN_BUNDLE_ENTRIES) {
|
|
140
|
+
const src = path.join(packageRoot, entry);
|
|
141
|
+
if (fs.existsSync(src)) {
|
|
142
|
+
copyRecursive(src, path.join(pluginRoot, entry));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// OpenCode index.js entry point generation
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
function writeIndexJs(pluginRoot) {
|
|
152
|
+
const indexContent = `#!/usr/bin/env node
|
|
153
|
+
/**
|
|
154
|
+
* Babysitter plugin entry point for OpenCode.
|
|
155
|
+
*
|
|
156
|
+
* OpenCode discovers plugins by looking for JS/TS modules in
|
|
157
|
+
* .opencode/plugins/. This file registers the babysitter hooks
|
|
158
|
+
* with the OpenCode plugin system.
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
"use strict";
|
|
162
|
+
|
|
163
|
+
const path = require("path");
|
|
164
|
+
|
|
165
|
+
const PLUGIN_DIR = __dirname;
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
name: "babysitter",
|
|
169
|
+
version: require(path.join(PLUGIN_DIR, "plugin.json")).version,
|
|
170
|
+
|
|
171
|
+
hooks: {
|
|
172
|
+
"session.created": require(path.join(PLUGIN_DIR, "hooks", "session-created.js")),
|
|
173
|
+
"session.idle": require(path.join(PLUGIN_DIR, "hooks", "session-idle.js")),
|
|
174
|
+
"shell.env": require(path.join(PLUGIN_DIR, "hooks", "shell-env.js")),
|
|
175
|
+
"tool.execute.before": require(path.join(PLUGIN_DIR, "hooks", "tool-execute-before.js")),
|
|
176
|
+
"tool.execute.after": require(path.join(PLUGIN_DIR, "hooks", "tool-execute-after.js")),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
`;
|
|
180
|
+
fs.writeFileSync(path.join(pluginRoot, 'index.js'), indexContent, 'utf8');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// OpenCode hooks.json config registration
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function mergeHooksConfig(packageRoot, openCodeHome) {
|
|
188
|
+
const hooksJsonPath = path.join(packageRoot, 'hooks', 'hooks.json');
|
|
189
|
+
if (!fs.existsSync(hooksJsonPath)) return;
|
|
190
|
+
const managedConfig = readJson(hooksJsonPath);
|
|
191
|
+
const managedHooks = managedConfig.hooks || {};
|
|
192
|
+
const hooksConfigPath = path.join(openCodeHome, 'hooks.json');
|
|
193
|
+
const existing = fs.existsSync(hooksConfigPath)
|
|
194
|
+
? readJson(hooksConfigPath)
|
|
195
|
+
: { version: 1, hooks: {} };
|
|
196
|
+
existing.version = existing.version || 1;
|
|
197
|
+
if (!existing.hooks || typeof existing.hooks !== 'object') {
|
|
198
|
+
existing.hooks = {};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for (const [eventName, entries] of Object.entries(managedHooks)) {
|
|
202
|
+
const existingEntries = Array.isArray(existing.hooks[eventName]) ? existing.hooks[eventName] : [];
|
|
203
|
+
const filteredEntries = existingEntries.filter((entry) => {
|
|
204
|
+
const script = String(entry.script || entry.command || entry.bash || '');
|
|
205
|
+
return !HOOK_SCRIPT_NAMES.some((name) => script.includes(name));
|
|
206
|
+
});
|
|
207
|
+
existing.hooks[eventName] = [...filteredEntries, ...entries];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
writeJson(hooksConfigPath, existing);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function removeManagedHooks(openCodeHome) {
|
|
214
|
+
const hooksConfigPath = path.join(openCodeHome, 'hooks.json');
|
|
215
|
+
if (!fs.existsSync(hooksConfigPath)) return;
|
|
216
|
+
|
|
217
|
+
let hooksConfig;
|
|
218
|
+
try {
|
|
219
|
+
hooksConfig = readJson(hooksConfigPath);
|
|
220
|
+
} catch {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (!hooksConfig.hooks || typeof hooksConfig.hooks !== 'object') return;
|
|
224
|
+
|
|
225
|
+
for (const eventName of Object.keys(hooksConfig.hooks)) {
|
|
226
|
+
const eventHooks = Array.isArray(hooksConfig.hooks[eventName]) ? hooksConfig.hooks[eventName] : [];
|
|
227
|
+
const filtered = eventHooks.filter((entry) => {
|
|
228
|
+
const script = String(entry.script || entry.command || entry.bash || '');
|
|
229
|
+
return !HOOK_SCRIPT_NAMES.some((name) => script.includes(name));
|
|
230
|
+
});
|
|
231
|
+
if (filtered.length > 0) {
|
|
232
|
+
hooksConfig.hooks[eventName] = filtered;
|
|
233
|
+
} else {
|
|
234
|
+
delete hooksConfig.hooks[eventName];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (Object.keys(hooksConfig.hooks).length === 0) {
|
|
238
|
+
fs.rmSync(hooksConfigPath, { force: true });
|
|
239
|
+
} else {
|
|
240
|
+
writeJson(hooksConfigPath, hooksConfig);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Marketplace
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
function normalizeMarketplaceName(name) {
|
|
249
|
+
const raw = String(name || '').trim();
|
|
250
|
+
const sanitized = raw
|
|
251
|
+
.replace(/[^A-Za-z0-9_-]+/g, '-')
|
|
252
|
+
.replace(/^-+|-+$/g, '');
|
|
253
|
+
return sanitized || DEFAULT_MARKETPLACE.name;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
|
|
257
|
+
let next = pluginSourcePath;
|
|
258
|
+
if (path.isAbsolute(next)) {
|
|
259
|
+
next = path.relative(path.dirname(marketplacePath), next);
|
|
260
|
+
}
|
|
261
|
+
next = String(next || '').replace(/\\/g, '/');
|
|
262
|
+
if (!next.startsWith('./') && !next.startsWith('../')) {
|
|
263
|
+
next = `./${next}`;
|
|
264
|
+
}
|
|
265
|
+
return next;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function ensureMarketplaceEntry(marketplacePath, pluginSourcePath) {
|
|
269
|
+
const marketplace = fs.existsSync(marketplacePath)
|
|
270
|
+
? readJson(marketplacePath)
|
|
271
|
+
: { ...DEFAULT_MARKETPLACE, plugins: [] };
|
|
272
|
+
marketplace.name = normalizeMarketplaceName(marketplace.name);
|
|
273
|
+
marketplace.interface = marketplace.interface || {};
|
|
274
|
+
marketplace.interface.displayName =
|
|
275
|
+
marketplace.interface.displayName || DEFAULT_MARKETPLACE.interface.displayName;
|
|
276
|
+
const nextEntry = {
|
|
277
|
+
name: PLUGIN_NAME,
|
|
278
|
+
source: {
|
|
279
|
+
source: 'local',
|
|
280
|
+
path: normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath),
|
|
281
|
+
},
|
|
282
|
+
policy: {
|
|
283
|
+
installation: 'AVAILABLE',
|
|
284
|
+
authentication: 'ON_INSTALL',
|
|
285
|
+
},
|
|
286
|
+
category: 'Coding',
|
|
287
|
+
};
|
|
288
|
+
const existingIndex = Array.isArray(marketplace.plugins)
|
|
289
|
+
? marketplace.plugins.findIndex((entry) => entry && entry.name === PLUGIN_NAME)
|
|
290
|
+
: -1;
|
|
291
|
+
if (!Array.isArray(marketplace.plugins)) {
|
|
292
|
+
marketplace.plugins = [nextEntry];
|
|
293
|
+
} else if (existingIndex >= 0) {
|
|
294
|
+
marketplace.plugins[existingIndex] = nextEntry;
|
|
295
|
+
} else {
|
|
296
|
+
marketplace.plugins.push(nextEntry);
|
|
297
|
+
}
|
|
298
|
+
writeJson(marketplacePath, marketplace);
|
|
299
|
+
return nextEntry;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function removeMarketplaceEntry(marketplacePath) {
|
|
303
|
+
if (!fs.existsSync(marketplacePath)) return;
|
|
304
|
+
const marketplace = readJson(marketplacePath);
|
|
305
|
+
if (!Array.isArray(marketplace.plugins)) return;
|
|
306
|
+
marketplace.plugins = marketplace.plugins.filter((entry) => entry && entry.name !== PLUGIN_NAME);
|
|
307
|
+
writeJson(marketplacePath, marketplace);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// Babysitter SDK CLI helpers
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
function resolveBabysitterCommand(packageRoot) {
|
|
315
|
+
if (process.env.BABYSITTER_SDK_CLI) {
|
|
316
|
+
return {
|
|
317
|
+
command: process.execPath,
|
|
318
|
+
argsPrefix: [path.resolve(process.env.BABYSITTER_SDK_CLI)],
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
return {
|
|
323
|
+
command: process.execPath,
|
|
324
|
+
argsPrefix: [
|
|
325
|
+
require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
|
|
326
|
+
paths: [packageRoot],
|
|
327
|
+
}),
|
|
328
|
+
],
|
|
329
|
+
};
|
|
330
|
+
} catch {
|
|
331
|
+
return {
|
|
332
|
+
command: 'babysitter',
|
|
333
|
+
argsPrefix: [],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function runBabysitterCli(packageRoot, cliArgs, options = {}) {
|
|
339
|
+
const resolved = resolveBabysitterCommand(packageRoot);
|
|
340
|
+
const result = spawnSync(resolved.command, [...resolved.argsPrefix, ...cliArgs], {
|
|
341
|
+
cwd: options.cwd || process.cwd(),
|
|
342
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
343
|
+
encoding: 'utf8',
|
|
344
|
+
env: {
|
|
345
|
+
...process.env,
|
|
346
|
+
...(options.env || {}),
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
if (result.status !== 0) {
|
|
350
|
+
const stderr = (result.stderr || '').trim();
|
|
351
|
+
const stdout = (result.stdout || '').trim();
|
|
352
|
+
throw new Error(
|
|
353
|
+
`babysitter ${cliArgs.join(' ')} failed` +
|
|
354
|
+
(stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return result.stdout;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function ensureGlobalProcessLibrary(packageRoot) {
|
|
361
|
+
return JSON.parse(
|
|
362
|
+
runBabysitterCli(
|
|
363
|
+
packageRoot,
|
|
364
|
+
['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
|
|
365
|
+
{ cwd: packageRoot },
|
|
366
|
+
),
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
// OpenCode surface installation
|
|
372
|
+
// ---------------------------------------------------------------------------
|
|
373
|
+
|
|
374
|
+
function installOpenCodeSurface(packageRoot, openCodeHome) {
|
|
375
|
+
// Copy skills into .opencode/skills/
|
|
376
|
+
const sourceSkills = path.join(packageRoot, 'skills');
|
|
377
|
+
if (fs.existsSync(sourceSkills)) {
|
|
378
|
+
const targetSkills = path.join(openCodeHome, 'skills');
|
|
379
|
+
fs.mkdirSync(targetSkills, { recursive: true });
|
|
380
|
+
for (const entry of fs.readdirSync(sourceSkills, { withFileTypes: true })) {
|
|
381
|
+
if (!entry.isDirectory()) continue;
|
|
382
|
+
copyRecursive(
|
|
383
|
+
path.join(sourceSkills, entry.name),
|
|
384
|
+
path.join(targetSkills, entry.name),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Merge hooks config
|
|
390
|
+
mergeHooksConfig(packageRoot, openCodeHome);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
module.exports = {
|
|
394
|
+
copyPluginBundle,
|
|
395
|
+
copyRecursive,
|
|
396
|
+
ensureGlobalProcessLibrary,
|
|
397
|
+
ensureMarketplaceEntry,
|
|
398
|
+
getHomeMarketplacePath,
|
|
399
|
+
getHomePluginRoot,
|
|
400
|
+
getOpenCodeHome,
|
|
401
|
+
installOpenCodeSurface,
|
|
402
|
+
removeManagedHooks,
|
|
403
|
+
removeMarketplaceEntry,
|
|
404
|
+
writeIndexJs,
|
|
405
|
+
writeJson,
|
|
406
|
+
};
|
package/bin/install.cjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Babysitter OpenCode Plugin Installer
|
|
6
|
+
*
|
|
7
|
+
* Copies the babysitter plugin bundle into the OpenCode plugins directory:
|
|
8
|
+
* <workspace>/.opencode/plugins/babysitter/
|
|
9
|
+
*
|
|
10
|
+
* Registers hooks in OpenCode config, creates the index.js entry point
|
|
11
|
+
* for plugin discovery, and bootstraps the global process library.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node install.cjs # Install into cwd workspace
|
|
15
|
+
* node install.cjs --workspace /path # Install into specified workspace
|
|
16
|
+
* node install.cjs --global # Global install (user home)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const {
|
|
21
|
+
copyPluginBundle,
|
|
22
|
+
ensureGlobalProcessLibrary,
|
|
23
|
+
ensureMarketplaceEntry,
|
|
24
|
+
getHomeMarketplacePath,
|
|
25
|
+
getHomePluginRoot,
|
|
26
|
+
getOpenCodeHome,
|
|
27
|
+
installOpenCodeSurface,
|
|
28
|
+
writeIndexJs,
|
|
29
|
+
} = require('./install-shared.cjs');
|
|
30
|
+
|
|
31
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
32
|
+
|
|
33
|
+
function parseArgs(argv) {
|
|
34
|
+
let workspace = process.env.OPENCODE_WORKSPACE || process.cwd();
|
|
35
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
36
|
+
const arg = argv[i];
|
|
37
|
+
if (arg === '--workspace') {
|
|
38
|
+
const next = argv[i + 1];
|
|
39
|
+
workspace = next && !next.startsWith('-') ? path.resolve(argv[++i]) : process.cwd();
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (arg === '--global') {
|
|
43
|
+
workspace = null;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
47
|
+
}
|
|
48
|
+
return { workspace };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function main() {
|
|
52
|
+
const { workspace } = parseArgs(process.argv);
|
|
53
|
+
const openCodeHome = getOpenCodeHome(workspace);
|
|
54
|
+
const pluginRoot = getHomePluginRoot(workspace);
|
|
55
|
+
const marketplacePath = getHomeMarketplacePath(workspace);
|
|
56
|
+
|
|
57
|
+
console.log(`[babysitter] Installing OpenCode plugin to ${pluginRoot}`);
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// 1. Copy plugin bundle
|
|
61
|
+
copyPluginBundle(PACKAGE_ROOT, pluginRoot);
|
|
62
|
+
console.log('[babysitter] Copied plugin bundle');
|
|
63
|
+
|
|
64
|
+
// 2. Write index.js entry point for OpenCode plugin discovery
|
|
65
|
+
writeIndexJs(pluginRoot);
|
|
66
|
+
console.log('[babysitter] Created index.js entry point');
|
|
67
|
+
|
|
68
|
+
// 3. Register in marketplace
|
|
69
|
+
ensureMarketplaceEntry(marketplacePath, pluginRoot);
|
|
70
|
+
console.log(`[babysitter] Marketplace: ${marketplacePath}`);
|
|
71
|
+
|
|
72
|
+
// 4. Install OpenCode surfaces (skills, hooks config)
|
|
73
|
+
installOpenCodeSurface(PACKAGE_ROOT, openCodeHome);
|
|
74
|
+
console.log('[babysitter] Installed hooks and skills');
|
|
75
|
+
|
|
76
|
+
// 5. Bootstrap global process library
|
|
77
|
+
try {
|
|
78
|
+
const active = ensureGlobalProcessLibrary(PACKAGE_ROOT);
|
|
79
|
+
console.log(`[babysitter] Process library: ${active.binding?.dir || '(default)'}`);
|
|
80
|
+
if (active.defaultSpec?.cloneDir) {
|
|
81
|
+
console.log(`[babysitter] Process library clone: ${active.defaultSpec.cloneDir}`);
|
|
82
|
+
}
|
|
83
|
+
console.log(`[babysitter] Process library state: ${active.stateFile}`);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.warn(`[babysitter] Warning: Could not bootstrap process library: ${err.message}`);
|
|
86
|
+
console.warn('[babysitter] Run "babysitter process-library:clone" manually if needed.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('[babysitter] Installation complete!');
|
|
90
|
+
console.log('[babysitter] Restart OpenCode to pick up the installed plugin.');
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error(`[babysitter] Failed to install plugin: ${err.message}`);
|
|
93
|
+
process.exitCode = 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
main();
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Babysitter OpenCode Plugin Installer
|
|
4
|
+
*
|
|
5
|
+
* Copies plugin files into the OpenCode plugins directory:
|
|
6
|
+
* .opencode/plugins/babysitter/
|
|
7
|
+
*
|
|
8
|
+
* OpenCode discovers plugins as JS/TS modules in .opencode/plugins/.
|
|
9
|
+
* This installer creates the necessary directory structure and copies
|
|
10
|
+
* hook scripts, skills, and an index.js entry point.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
|
|
18
|
+
const PLUGIN_ROOT = path.resolve(__dirname, "..");
|
|
19
|
+
const WORKSPACE = process.env.OPENCODE_WORKSPACE || process.cwd();
|
|
20
|
+
const TARGET_DIR = path.join(WORKSPACE, ".opencode", "plugins", "babysitter");
|
|
21
|
+
|
|
22
|
+
function copyRecursive(src, dest) {
|
|
23
|
+
const stat = fs.statSync(src);
|
|
24
|
+
if (stat.isDirectory()) {
|
|
25
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
26
|
+
for (const entry of fs.readdirSync(src)) {
|
|
27
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
31
|
+
fs.copyFileSync(src, dest);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function main() {
|
|
36
|
+
console.log(`Installing babysitter plugin for OpenCode...`);
|
|
37
|
+
console.log(` Source: ${PLUGIN_ROOT}`);
|
|
38
|
+
console.log(` Target: ${TARGET_DIR}`);
|
|
39
|
+
|
|
40
|
+
// Create target directory
|
|
41
|
+
fs.mkdirSync(TARGET_DIR, { recursive: true });
|
|
42
|
+
|
|
43
|
+
// Copy hooks
|
|
44
|
+
const hooksDir = path.join(PLUGIN_ROOT, "hooks");
|
|
45
|
+
if (fs.existsSync(hooksDir)) {
|
|
46
|
+
copyRecursive(hooksDir, path.join(TARGET_DIR, "hooks"));
|
|
47
|
+
console.log(" Copied hooks/");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Copy skills
|
|
51
|
+
const skillsDir = path.join(PLUGIN_ROOT, "skills");
|
|
52
|
+
if (fs.existsSync(skillsDir)) {
|
|
53
|
+
copyRecursive(skillsDir, path.join(TARGET_DIR, "skills"));
|
|
54
|
+
console.log(" Copied skills/");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Copy commands
|
|
58
|
+
const commandsDir = path.join(PLUGIN_ROOT, "commands");
|
|
59
|
+
if (fs.existsSync(commandsDir)) {
|
|
60
|
+
copyRecursive(commandsDir, path.join(TARGET_DIR, "commands"));
|
|
61
|
+
console.log(" Copied commands/");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Copy plugin.json and versions.json
|
|
65
|
+
for (const file of ["plugin.json", "versions.json"]) {
|
|
66
|
+
const src = path.join(PLUGIN_ROOT, file);
|
|
67
|
+
if (fs.existsSync(src)) {
|
|
68
|
+
fs.copyFileSync(src, path.join(TARGET_DIR, file));
|
|
69
|
+
console.log(` Copied ${file}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create index.js entry point for OpenCode plugin discovery
|
|
74
|
+
const indexContent = `#!/usr/bin/env node
|
|
75
|
+
/**
|
|
76
|
+
* Babysitter plugin entry point for OpenCode.
|
|
77
|
+
*
|
|
78
|
+
* OpenCode discovers plugins by looking for JS/TS modules in
|
|
79
|
+
* .opencode/plugins/. This file registers the babysitter hooks
|
|
80
|
+
* with the OpenCode plugin system.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
"use strict";
|
|
84
|
+
|
|
85
|
+
const path = require("path");
|
|
86
|
+
|
|
87
|
+
const PLUGIN_DIR = __dirname;
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
name: "babysitter",
|
|
91
|
+
version: require(path.join(PLUGIN_DIR, "plugin.json")).version,
|
|
92
|
+
|
|
93
|
+
hooks: {
|
|
94
|
+
"session.created": require(path.join(PLUGIN_DIR, "hooks", "session-created.js")),
|
|
95
|
+
"session.idle": require(path.join(PLUGIN_DIR, "hooks", "session-idle.js")),
|
|
96
|
+
"shell.env": require(path.join(PLUGIN_DIR, "hooks", "shell-env.js")),
|
|
97
|
+
"tool.execute.before": require(path.join(PLUGIN_DIR, "hooks", "tool-execute-before.js")),
|
|
98
|
+
"tool.execute.after": require(path.join(PLUGIN_DIR, "hooks", "tool-execute-after.js")),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
fs.writeFileSync(path.join(TARGET_DIR, "index.js"), indexContent);
|
|
104
|
+
console.log(" Created index.js");
|
|
105
|
+
|
|
106
|
+
console.log(`\nBabysitter plugin installed to ${TARGET_DIR}`);
|
|
107
|
+
console.log("Restart OpenCode to activate the plugin.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Babysitter OpenCode Plugin Uninstaller
|
|
6
|
+
*
|
|
7
|
+
* Removes the babysitter plugin from the OpenCode plugins directory
|
|
8
|
+
* and cleans up hooks config and marketplace entries.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node uninstall.cjs # Uninstall from cwd workspace
|
|
12
|
+
* node uninstall.cjs --workspace /path # Uninstall from specified workspace
|
|
13
|
+
* node uninstall.cjs --global # Global uninstall
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const {
|
|
19
|
+
getHomeMarketplacePath,
|
|
20
|
+
getHomePluginRoot,
|
|
21
|
+
getOpenCodeHome,
|
|
22
|
+
removeManagedHooks,
|
|
23
|
+
removeMarketplaceEntry,
|
|
24
|
+
} = require('./install-shared.cjs');
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
let workspace = process.env.OPENCODE_WORKSPACE || process.cwd();
|
|
28
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
29
|
+
const arg = argv[i];
|
|
30
|
+
if (arg === '--workspace') {
|
|
31
|
+
const next = argv[i + 1];
|
|
32
|
+
workspace = next && !next.startsWith('-') ? path.resolve(argv[++i]) : process.cwd();
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (arg === '--global') {
|
|
36
|
+
workspace = null;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
40
|
+
}
|
|
41
|
+
return { workspace };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function main() {
|
|
45
|
+
const { workspace } = parseArgs(process.argv);
|
|
46
|
+
const openCodeHome = getOpenCodeHome(workspace);
|
|
47
|
+
const pluginRoot = getHomePluginRoot(workspace);
|
|
48
|
+
const marketplacePath = getHomeMarketplacePath(workspace);
|
|
49
|
+
let removedPlugin = false;
|
|
50
|
+
|
|
51
|
+
console.log(`[babysitter] Uninstalling OpenCode plugin from ${pluginRoot}`);
|
|
52
|
+
|
|
53
|
+
// 1. Remove plugin directory
|
|
54
|
+
if (fs.existsSync(pluginRoot)) {
|
|
55
|
+
try {
|
|
56
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
57
|
+
console.log(`[babysitter] Removed ${pluginRoot}`);
|
|
58
|
+
removedPlugin = true;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.warn(`[babysitter] Warning: Could not remove plugin directory: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 2. Remove marketplace entry
|
|
65
|
+
removeMarketplaceEntry(marketplacePath);
|
|
66
|
+
console.log('[babysitter] Cleaned marketplace entry');
|
|
67
|
+
|
|
68
|
+
// 3. Remove managed hooks from OpenCode config
|
|
69
|
+
removeManagedHooks(openCodeHome);
|
|
70
|
+
console.log('[babysitter] Cleaned hooks config');
|
|
71
|
+
|
|
72
|
+
// 4. Clean up empty parent directories
|
|
73
|
+
const pluginsDir = path.dirname(pluginRoot);
|
|
74
|
+
try {
|
|
75
|
+
const remaining = fs.readdirSync(pluginsDir);
|
|
76
|
+
if (remaining.length === 0) {
|
|
77
|
+
fs.rmdirSync(pluginsDir);
|
|
78
|
+
console.log('[babysitter] Removed empty plugins/ directory');
|
|
79
|
+
}
|
|
80
|
+
} catch { /* best-effort */ }
|
|
81
|
+
|
|
82
|
+
if (!removedPlugin) {
|
|
83
|
+
console.log('[babysitter] Plugin directory not found; config and hooks cleaned if present.');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('[babysitter] Uninstallation complete. Restart OpenCode to finish removal.');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
main();
|