@a5c-ai/babysitter-github 0.1.1-staging.0825aadb

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 (44) hide show
  1. package/.github/plugin.json +25 -0
  2. package/AGENTS.md +41 -0
  3. package/README.md +545 -0
  4. package/bin/cli.js +104 -0
  5. package/bin/install-shared.js +450 -0
  6. package/bin/install.js +81 -0
  7. package/bin/uninstall.js +76 -0
  8. package/commands/assimilate.md +37 -0
  9. package/commands/call.md +7 -0
  10. package/commands/cleanup.md +20 -0
  11. package/commands/contrib.md +33 -0
  12. package/commands/doctor.md +426 -0
  13. package/commands/forever.md +7 -0
  14. package/commands/help.md +244 -0
  15. package/commands/observe.md +12 -0
  16. package/commands/plan.md +7 -0
  17. package/commands/plugins.md +255 -0
  18. package/commands/project-install.md +17 -0
  19. package/commands/resume.md +8 -0
  20. package/commands/retrospect.md +55 -0
  21. package/commands/user-install.md +17 -0
  22. package/commands/yolo.md +7 -0
  23. package/hooks/session-end.ps1 +68 -0
  24. package/hooks/session-end.sh +65 -0
  25. package/hooks/session-start.ps1 +110 -0
  26. package/hooks/session-start.sh +100 -0
  27. package/hooks/user-prompt-submitted.ps1 +51 -0
  28. package/hooks/user-prompt-submitted.sh +41 -0
  29. package/hooks.json +29 -0
  30. package/package.json +50 -0
  31. package/plugin.json +25 -0
  32. package/scripts/sync-command-surfaces.js +62 -0
  33. package/scripts/team-install.js +86 -0
  34. package/skills/assimilate/SKILL.md +38 -0
  35. package/skills/babysit/SKILL.md +77 -0
  36. package/skills/call/SKILL.md +8 -0
  37. package/skills/doctor/SKILL.md +427 -0
  38. package/skills/help/SKILL.md +245 -0
  39. package/skills/observe/SKILL.md +13 -0
  40. package/skills/plan/SKILL.md +8 -0
  41. package/skills/resume/SKILL.md +9 -0
  42. package/skills/retrospect/SKILL.md +56 -0
  43. package/skills/user-install/SKILL.md +18 -0
  44. package/versions.json +3 -0
@@ -0,0 +1,450 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+
8
+ const PLUGIN_NAME = 'babysitter';
9
+ const PLUGIN_CATEGORY = 'Coding';
10
+ const LEGACY_HOOK_SCRIPT_NAMES = [
11
+ 'session-start.sh',
12
+ 'stop-hook.sh',
13
+ 'user-prompt-submit.sh',
14
+ ];
15
+ const HOOK_SCRIPT_NAMES = [
16
+ 'session-start.sh',
17
+ 'session-start.ps1',
18
+ 'session-end.sh',
19
+ 'session-end.ps1',
20
+ 'user-prompt-submitted.sh',
21
+ 'user-prompt-submitted.ps1',
22
+ ];
23
+ const DEFAULT_MARKETPLACE = {
24
+ name: 'local-plugins',
25
+ interface: {
26
+ displayName: 'Local Plugins',
27
+ },
28
+ plugins: [],
29
+ };
30
+ const PLUGIN_BUNDLE_ENTRIES = [
31
+ 'plugin.json',
32
+ 'hooks.json',
33
+ 'hooks',
34
+ 'skills',
35
+ 'versions.json',
36
+ 'AGENTS.md',
37
+ ];
38
+
39
+ function getCopilotHome() {
40
+ if (process.env.COPILOT_HOME) return path.resolve(process.env.COPILOT_HOME);
41
+ return path.join(os.homedir(), '.copilot');
42
+ }
43
+
44
+ function getUserHome() {
45
+ if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
46
+ if (process.env.HOME) return path.resolve(process.env.HOME);
47
+ return os.homedir();
48
+ }
49
+
50
+ function getGlobalStateDir() {
51
+ if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
52
+ return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
53
+ }
54
+ return path.join(getUserHome(), '.a5c');
55
+ }
56
+
57
+ function getHomePluginRoot() {
58
+ if (process.env.BABYSITTER_GITHUB_PLUGIN_DIR) {
59
+ return path.resolve(process.env.BABYSITTER_GITHUB_PLUGIN_DIR, PLUGIN_NAME);
60
+ }
61
+ return path.join(getCopilotHome(), 'plugins', PLUGIN_NAME);
62
+ }
63
+
64
+ function getHomeMarketplacePath() {
65
+ if (process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH) {
66
+ return path.resolve(process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH);
67
+ }
68
+ return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
69
+ }
70
+
71
+ function writeFileIfChanged(filePath, contents) {
72
+ if (fs.existsSync(filePath)) {
73
+ const current = fs.readFileSync(filePath, 'utf8');
74
+ if (current === contents) {
75
+ return false;
76
+ }
77
+ }
78
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
79
+ fs.writeFileSync(filePath, contents, 'utf8');
80
+ return true;
81
+ }
82
+
83
+ function copyRecursive(src, dest) {
84
+ const stat = fs.statSync(src);
85
+ if (stat.isDirectory()) {
86
+ fs.mkdirSync(dest, { recursive: true });
87
+ for (const entry of fs.readdirSync(src)) {
88
+ if (['node_modules', '.git', 'test', '.a5c'].includes(entry)) continue;
89
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
90
+ }
91
+ return;
92
+ }
93
+
94
+ if (path.basename(src) === 'SKILL.md') {
95
+ const file = fs.readFileSync(src);
96
+ const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
97
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
98
+ fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
99
+ return;
100
+ }
101
+
102
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
103
+ fs.copyFileSync(src, dest);
104
+ }
105
+
106
+ function copyPluginBundle(packageRoot, pluginRoot) {
107
+ if (path.resolve(packageRoot) === path.resolve(pluginRoot)) {
108
+ return;
109
+ }
110
+ fs.rmSync(pluginRoot, { recursive: true, force: true });
111
+ fs.mkdirSync(pluginRoot, { recursive: true });
112
+ for (const entry of PLUGIN_BUNDLE_ENTRIES) {
113
+ const src = path.join(packageRoot, entry);
114
+ if (fs.existsSync(src)) {
115
+ copyRecursive(src, path.join(pluginRoot, entry));
116
+ }
117
+ }
118
+ }
119
+
120
+ function ensureExecutable(filePath) {
121
+ try {
122
+ fs.chmodSync(filePath, 0o755);
123
+ } catch {
124
+ // Best-effort only. Windows and some filesystems may ignore mode changes.
125
+ }
126
+ }
127
+
128
+ function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
129
+ let next = pluginSourcePath;
130
+ if (path.isAbsolute(next)) {
131
+ next = path.relative(path.dirname(marketplacePath), next);
132
+ }
133
+ next = String(next || '').replace(/\\/g, '/');
134
+ if (!next.startsWith('./') && !next.startsWith('../')) {
135
+ next = `./${next}`;
136
+ }
137
+ return next;
138
+ }
139
+
140
+ function readJson(filePath) {
141
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
142
+ }
143
+
144
+ function writeJson(filePath, value) {
145
+ writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
146
+ }
147
+
148
+ function ensureMarketplaceEntry(marketplacePath, pluginSourcePath) {
149
+ const marketplace = fs.existsSync(marketplacePath)
150
+ ? readJson(marketplacePath)
151
+ : { ...DEFAULT_MARKETPLACE, plugins: [] };
152
+ marketplace.name = marketplace.name || DEFAULT_MARKETPLACE.name;
153
+ marketplace.interface = marketplace.interface || {};
154
+ marketplace.interface.displayName =
155
+ marketplace.interface.displayName || DEFAULT_MARKETPLACE.interface.displayName;
156
+ const nextEntry = {
157
+ name: PLUGIN_NAME,
158
+ source: {
159
+ source: 'local',
160
+ path: normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath),
161
+ },
162
+ policy: {
163
+ installation: 'AVAILABLE',
164
+ authentication: 'ON_INSTALL',
165
+ },
166
+ category: PLUGIN_CATEGORY,
167
+ };
168
+ const existingIndex = Array.isArray(marketplace.plugins)
169
+ ? marketplace.plugins.findIndex((entry) => entry && entry.name === PLUGIN_NAME)
170
+ : -1;
171
+ if (!Array.isArray(marketplace.plugins)) {
172
+ marketplace.plugins = [nextEntry];
173
+ } else if (existingIndex >= 0) {
174
+ marketplace.plugins[existingIndex] = nextEntry;
175
+ } else {
176
+ marketplace.plugins.push(nextEntry);
177
+ }
178
+ writeJson(marketplacePath, marketplace);
179
+ return nextEntry;
180
+ }
181
+
182
+ function removeMarketplaceEntry(marketplacePath) {
183
+ if (!fs.existsSync(marketplacePath)) {
184
+ return;
185
+ }
186
+ const marketplace = readJson(marketplacePath);
187
+ if (!Array.isArray(marketplace.plugins)) {
188
+ return;
189
+ }
190
+ marketplace.plugins = marketplace.plugins.filter((entry) => entry && entry.name !== PLUGIN_NAME);
191
+ writeJson(marketplacePath, marketplace);
192
+ }
193
+
194
+ /**
195
+ * Registers the plugin in ~/.copilot/config.json.
196
+ */
197
+ function registerCopilotPlugin(pluginRoot) {
198
+ const copilotHome = getCopilotHome();
199
+ const configPath = path.join(copilotHome, 'config.json');
200
+
201
+ fs.mkdirSync(copilotHome, { recursive: true });
202
+
203
+ let config = {};
204
+ if (fs.existsSync(configPath)) {
205
+ try {
206
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
207
+ } catch {
208
+ config = {};
209
+ }
210
+ }
211
+
212
+ if (!config.plugins) {
213
+ config.plugins = [];
214
+ }
215
+
216
+ const existing = config.plugins.findIndex(
217
+ (p) => (typeof p === 'string' ? p : p.path) === pluginRoot
218
+ );
219
+
220
+ if (existing === -1) {
221
+ config.plugins.push({
222
+ path: pluginRoot,
223
+ enabled: true,
224
+ });
225
+ } else {
226
+ config.plugins[existing] = {
227
+ path: pluginRoot,
228
+ enabled: true,
229
+ };
230
+ }
231
+
232
+ writeFileIfChanged(configPath, `${JSON.stringify(config, null, 2)}\n`);
233
+ }
234
+
235
+ /**
236
+ * Removes the plugin entry from ~/.copilot/config.json.
237
+ */
238
+ function deregisterCopilotPlugin(pluginRoot) {
239
+ const configPath = path.join(getCopilotHome(), 'config.json');
240
+ if (!fs.existsSync(configPath)) return;
241
+
242
+ try {
243
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
244
+ if (Array.isArray(config.plugins)) {
245
+ config.plugins = config.plugins.filter(
246
+ (p) => (typeof p === 'string' ? p : p.path) !== pluginRoot
247
+ );
248
+ writeFileIfChanged(configPath, `${JSON.stringify(config, null, 2)}\n`);
249
+ console.log(`[${PLUGIN_NAME}] Removed plugin entry from config.json`);
250
+ }
251
+ } catch (err) {
252
+ console.warn(`[${PLUGIN_NAME}] Warning: Could not update config.json: ${err.message}`);
253
+ }
254
+ }
255
+
256
+ function installManagedSkills(packageRoot, copilotHome) {
257
+ const sourceRoot = path.join(packageRoot, 'skills');
258
+ if (!fs.existsSync(sourceRoot)) return;
259
+ const targetRoot = path.join(copilotHome, 'skills');
260
+ fs.mkdirSync(targetRoot, { recursive: true });
261
+
262
+ for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
263
+ if (!entry.isDirectory()) continue;
264
+ copyRecursive(
265
+ path.join(sourceRoot, entry.name),
266
+ path.join(targetRoot, entry.name),
267
+ );
268
+ }
269
+ }
270
+
271
+ function mergeManagedHooksConfig(packageRoot, copilotHome) {
272
+ const hooksJsonPath = path.join(packageRoot, 'hooks.json');
273
+ if (!fs.existsSync(hooksJsonPath)) return;
274
+ const managedConfig = readJson(hooksJsonPath);
275
+ const managedHooks = managedConfig.hooks || {};
276
+ const hooksConfigPath = path.join(copilotHome, 'hooks.json');
277
+ const existing = fs.existsSync(hooksConfigPath)
278
+ ? readJson(hooksConfigPath)
279
+ : { version: 1, hooks: {} };
280
+ // Ensure version field is present per Copilot CLI spec
281
+ existing.version = existing.version || 1;
282
+ if (!existing.hooks || typeof existing.hooks !== 'object') {
283
+ existing.hooks = {};
284
+ }
285
+
286
+ // Remove legacy PascalCase event entries that are no longer valid
287
+ for (const legacyEvent of ['SessionStart', 'UserPromptSubmit', 'Stop']) {
288
+ delete existing.hooks[legacyEvent];
289
+ }
290
+
291
+ const allScriptNames = [...LEGACY_HOOK_SCRIPT_NAMES, ...HOOK_SCRIPT_NAMES];
292
+ for (const [eventName, entries] of Object.entries(managedHooks)) {
293
+ const existingEntries = Array.isArray(existing.hooks[eventName]) ? existing.hooks[eventName] : [];
294
+ const filteredEntries = existingEntries
295
+ .filter((entry) => {
296
+ const bash = String(entry.bash || entry.command || '');
297
+ const ps = String(entry.powershell || '');
298
+ return !allScriptNames.some((name) => bash.includes(name) || ps.includes(name));
299
+ });
300
+ existing.hooks[eventName] = [...filteredEntries, ...entries];
301
+ }
302
+
303
+ writeJson(hooksConfigPath, existing);
304
+ }
305
+
306
+ function installManagedHooks(packageRoot, copilotHome) {
307
+ const sourceRoot = path.join(packageRoot, 'hooks');
308
+ if (!fs.existsSync(sourceRoot)) return;
309
+ const targetRoot = path.join(copilotHome, 'hooks');
310
+ fs.mkdirSync(targetRoot, { recursive: true });
311
+
312
+ for (const scriptName of HOOK_SCRIPT_NAMES) {
313
+ const sourcePath = path.join(sourceRoot, scriptName);
314
+ if (!fs.existsSync(sourcePath)) continue;
315
+ const targetPath = path.join(targetRoot, scriptName);
316
+ copyRecursive(sourcePath, targetPath);
317
+ ensureExecutable(targetPath);
318
+ }
319
+
320
+ mergeManagedHooksConfig(packageRoot, copilotHome);
321
+ }
322
+
323
+ function removeLegacyHooks(copilotHome) {
324
+ for (const hookName of LEGACY_HOOK_SCRIPT_NAMES) {
325
+ fs.rmSync(path.join(copilotHome, 'hooks', hookName), { force: true });
326
+ }
327
+
328
+ const hooksConfigPath = path.join(copilotHome, 'hooks.json');
329
+ if (!fs.existsSync(hooksConfigPath)) {
330
+ return;
331
+ }
332
+ let hooksConfig;
333
+ try {
334
+ hooksConfig = readJson(hooksConfigPath);
335
+ } catch {
336
+ return;
337
+ }
338
+ if (!hooksConfig.hooks || typeof hooksConfig.hooks !== 'object') {
339
+ return;
340
+ }
341
+ for (const eventName of ['SessionStart', 'UserPromptSubmit', 'Stop', 'sessionStart', 'sessionEnd', 'userPromptSubmitted']) {
342
+ const eventHooks = Array.isArray(hooksConfig.hooks[eventName]) ? hooksConfig.hooks[eventName] : [];
343
+ const filteredMatchers = eventHooks
344
+ .map((matcher) => {
345
+ const hooks = Array.isArray(matcher.hooks) ? matcher.hooks : [];
346
+ const keptHooks = hooks.filter((hook) => {
347
+ const command = String(hook.command || '');
348
+ return !LEGACY_HOOK_SCRIPT_NAMES.some((name) => command.includes(name));
349
+ });
350
+ return keptHooks.length > 0 ? { ...matcher, hooks: keptHooks } : null;
351
+ })
352
+ .filter(Boolean);
353
+ if (filteredMatchers.length > 0) {
354
+ hooksConfig.hooks[eventName] = filteredMatchers;
355
+ } else {
356
+ delete hooksConfig.hooks[eventName];
357
+ }
358
+ }
359
+ if (Object.keys(hooksConfig.hooks).length === 0) {
360
+ fs.rmSync(hooksConfigPath, { force: true });
361
+ } else {
362
+ writeJson(hooksConfigPath, hooksConfig);
363
+ }
364
+ }
365
+
366
+ function installCopilotSurface(packageRoot, copilotHome) {
367
+ removeLegacyHooks(copilotHome);
368
+ installManagedSkills(packageRoot, copilotHome);
369
+ installManagedHooks(packageRoot, copilotHome);
370
+ }
371
+
372
+ function resolveBabysitterCommand(packageRoot) {
373
+ if (process.env.BABYSITTER_SDK_CLI) {
374
+ return {
375
+ command: process.execPath,
376
+ argsPrefix: [path.resolve(process.env.BABYSITTER_SDK_CLI)],
377
+ };
378
+ }
379
+ try {
380
+ return {
381
+ command: process.execPath,
382
+ argsPrefix: [
383
+ require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
384
+ paths: [packageRoot],
385
+ }),
386
+ ],
387
+ };
388
+ } catch {
389
+ return {
390
+ command: 'babysitter',
391
+ argsPrefix: [],
392
+ };
393
+ }
394
+ }
395
+
396
+ function runBabysitterCli(packageRoot, cliArgs, options = {}) {
397
+ const resolved = resolveBabysitterCommand(packageRoot);
398
+ const result = spawnSync(resolved.command, [...resolved.argsPrefix, ...cliArgs], {
399
+ cwd: options.cwd || process.cwd(),
400
+ stdio: ['ignore', 'pipe', 'pipe'],
401
+ encoding: 'utf8',
402
+ env: {
403
+ ...process.env,
404
+ ...(options.env || {}),
405
+ },
406
+ });
407
+ if (result.status !== 0) {
408
+ const stderr = (result.stderr || '').trim();
409
+ const stdout = (result.stdout || '').trim();
410
+ throw new Error(
411
+ `babysitter ${cliArgs.join(' ')} failed` +
412
+ (stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
413
+ );
414
+ }
415
+ return result.stdout;
416
+ }
417
+
418
+ function ensureGlobalProcessLibrary(packageRoot) {
419
+ return JSON.parse(
420
+ runBabysitterCli(
421
+ packageRoot,
422
+ ['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
423
+ { cwd: packageRoot },
424
+ ),
425
+ );
426
+ }
427
+
428
+ function warnWindowsHooks() {
429
+ if (process.platform !== 'win32') {
430
+ return;
431
+ }
432
+ console.warn(`[${PLUGIN_NAME}] Note: On Windows, Copilot CLI will use .ps1 PowerShell hooks.`);
433
+ console.warn(`[${PLUGIN_NAME}] Both bash (.sh) and PowerShell (.ps1) hook scripts are included.`);
434
+ }
435
+
436
+ module.exports = {
437
+ copyPluginBundle,
438
+ deregisterCopilotPlugin,
439
+ ensureGlobalProcessLibrary,
440
+ ensureMarketplaceEntry,
441
+ getCopilotHome,
442
+ getHomeMarketplacePath,
443
+ getHomePluginRoot,
444
+ installCopilotSurface,
445
+ registerCopilotPlugin,
446
+ removeLegacyHooks,
447
+ removeMarketplaceEntry,
448
+ warnWindowsHooks,
449
+ writeJson,
450
+ };
package/bin/install.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+ const {
7
+ copyPluginBundle,
8
+ ensureGlobalProcessLibrary,
9
+ ensureMarketplaceEntry,
10
+ getCopilotHome,
11
+ getHomeMarketplacePath,
12
+ getHomePluginRoot,
13
+ installCopilotSurface,
14
+ warnWindowsHooks,
15
+ } = require('./install-shared');
16
+
17
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
18
+
19
+ /**
20
+ * Attempt to register the plugin via `copilot plugin install ./path`.
21
+ * Falls back to manual config.json registration if the CLI is not available.
22
+ */
23
+ function registerViaCopilotCli(pluginRoot) {
24
+ const result = spawnSync('copilot', ['plugin', 'install', pluginRoot], {
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ encoding: 'utf8',
27
+ timeout: 30000,
28
+ });
29
+
30
+ if (result.status === 0) {
31
+ console.log(`[babysitter] Registered plugin via 'copilot plugin install'`);
32
+ return true;
33
+ }
34
+
35
+ // CLI not available or failed -- fall back to manual registration
36
+ const stderr = (result.stderr || '').trim();
37
+ if (result.error || stderr.includes('not found') || stderr.includes('not recognized')) {
38
+ console.log(`[babysitter] Copilot CLI not found, using manual registration`);
39
+ } else {
40
+ console.warn(`[babysitter] 'copilot plugin install' failed: ${stderr || 'unknown error'}, using manual registration`);
41
+ }
42
+ return false;
43
+ }
44
+
45
+ function main() {
46
+ const copilotHome = getCopilotHome();
47
+ const pluginRoot = getHomePluginRoot();
48
+ const marketplacePath = getHomeMarketplacePath();
49
+
50
+ console.log(`[babysitter] Installing plugin to ${pluginRoot}`);
51
+
52
+ try {
53
+ copyPluginBundle(PACKAGE_ROOT, pluginRoot);
54
+ ensureMarketplaceEntry(marketplacePath, pluginRoot);
55
+
56
+ // Try native copilot CLI registration first; fall back to manual config.json
57
+ if (!registerViaCopilotCli(pluginRoot)) {
58
+ const { registerCopilotPlugin } = require('./install-shared');
59
+ registerCopilotPlugin(pluginRoot);
60
+ }
61
+
62
+ installCopilotSurface(PACKAGE_ROOT, copilotHome);
63
+
64
+ const active = ensureGlobalProcessLibrary(PACKAGE_ROOT);
65
+ console.log(`[babysitter] marketplace: ${marketplacePath}`);
66
+ console.log(`[babysitter] copilot config: ${path.join(copilotHome, 'config.json')}`);
67
+ console.log(`[babysitter] process library: ${active.binding?.dir}`);
68
+ if (active.defaultSpec?.cloneDir) {
69
+ console.log(`[babysitter] process library clone: ${active.defaultSpec.cloneDir}`);
70
+ }
71
+ console.log(`[babysitter] process library state: ${active.stateFile}`);
72
+ warnWindowsHooks();
73
+ console.log('[babysitter] Installation complete!');
74
+ console.log('[babysitter] Restart GitHub Copilot CLI to pick up the installed plugin and config changes.');
75
+ } catch (err) {
76
+ console.error(`[babysitter] Failed to install plugin: ${err.message}`);
77
+ process.exitCode = 1;
78
+ }
79
+ }
80
+
81
+ main();
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const { spawnSync } = require('child_process');
6
+ const {
7
+ deregisterCopilotPlugin,
8
+ getCopilotHome,
9
+ getHomeMarketplacePath,
10
+ getHomePluginRoot,
11
+ removeLegacyHooks,
12
+ removeMarketplaceEntry,
13
+ } = require('./install-shared');
14
+
15
+ const PLUGIN_NAME = 'babysitter';
16
+
17
+ /**
18
+ * Attempt to unregister the plugin via `copilot plugin uninstall`.
19
+ * Falls back to manual config.json cleanup if the CLI is not available.
20
+ */
21
+ function unregisterViaCopilotCli() {
22
+ const result = spawnSync('copilot', ['plugin', 'uninstall', PLUGIN_NAME], {
23
+ stdio: ['ignore', 'pipe', 'pipe'],
24
+ encoding: 'utf8',
25
+ timeout: 30000,
26
+ });
27
+
28
+ if (result.status === 0) {
29
+ console.log(`[${PLUGIN_NAME}] Unregistered plugin via 'copilot plugin uninstall'`);
30
+ return true;
31
+ }
32
+
33
+ // CLI not available or failed
34
+ const stderr = (result.stderr || '').trim();
35
+ if (result.error || stderr.includes('not found') || stderr.includes('not recognized')) {
36
+ console.log(`[${PLUGIN_NAME}] Copilot CLI not found, using manual cleanup`);
37
+ } else {
38
+ console.warn(`[${PLUGIN_NAME}] 'copilot plugin uninstall' failed: ${stderr || 'unknown error'}, using manual cleanup`);
39
+ }
40
+ return false;
41
+ }
42
+
43
+ function main() {
44
+ const copilotHome = getCopilotHome();
45
+ const pluginRoot = getHomePluginRoot();
46
+ const marketplacePath = getHomeMarketplacePath();
47
+ let removedPlugin = false;
48
+
49
+ if (fs.existsSync(pluginRoot)) {
50
+ try {
51
+ fs.rmSync(pluginRoot, { recursive: true, force: true });
52
+ console.log(`[${PLUGIN_NAME}] Removed ${pluginRoot}`);
53
+ removedPlugin = true;
54
+ } catch (err) {
55
+ console.warn(`[${PLUGIN_NAME}] Warning: Could not remove plugin directory ${pluginRoot}: ${err.message}`);
56
+ }
57
+ }
58
+
59
+ removeMarketplaceEntry(marketplacePath);
60
+
61
+ // Try native copilot CLI unregistration first; fall back to manual config.json
62
+ if (!unregisterViaCopilotCli()) {
63
+ deregisterCopilotPlugin(pluginRoot);
64
+ }
65
+
66
+ removeLegacyHooks(copilotHome);
67
+
68
+ if (!removedPlugin) {
69
+ console.log(`[${PLUGIN_NAME}] Plugin directory not found, config and hooks cleaned if present.`);
70
+ return;
71
+ }
72
+
73
+ console.log(`[${PLUGIN_NAME}] Restart GitHub Copilot CLI to complete uninstallation.`);
74
+ }
75
+
76
+ main();
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: Assimilate an external methodology, harness, or specification into babysitter process definitions with skills and agents.
3
+ argument-hint: Target to assimilate (e.g. repo URL, harness name, or spec path)
4
+ allowed-tools: Read, Grep, Write, Task, Bash, Edit, Grep, Glob, WebFetch, WebSearch, Search, AskUserQuestion, TodoWrite, TodoRead, Skill, BashOutput, KillShell, MultiEdit, LS
5
+ ---
6
+
7
+ Invoke the babysitter:babysit skill (using the Skill tool) and follow its instructions (SKILL.md).
8
+
9
+ Use the assimilation domain processes from the active process library to convert external sources into well-defined babysitter process definitions with accompanying skills/ and agents/ directories.
10
+
11
+ If the workspace does not already have an active process-library binding, initialize it first through the shared global SDK binding:
12
+
13
+ ```bash
14
+ babysitter process-library:active --json
15
+ ```
16
+
17
+ Run the process after formalizing it.
18
+
19
+ Available assimilation workflows:
20
+ - **methodology-assimilation** (`specializations/domains/assimilation/workflows/methodology-assimilation`) - Learns an external methodology from its repo and converts procedural instructions, commands, and manual flows into babysitter processes with refactored skills and agents. Supports output as methodology or specialization.
21
+ - **harness integration** (`specializations/domains/assimilation/harness/*`) - Integrates babysitter SDK with a specific AI coding harness (generic, codex, opencode, gemini-cli, openclaw, antigravity).
22
+
23
+ During the interview phase, determine which assimilation workflow to use based on the user's target:
24
+ - If the target is a **repo URL or methodology name** then use the methodology-assimilation workflow.
25
+ - If the target is a **harness name** (e.g. codex, opencode, antigravity) then use the matching harness process.
26
+ - If the target is a **specification or other source** then adapt the methodology-assimilation workflow for the spec format.
27
+ - If unclear, ask the user to clarify the assimilation target and type.
28
+
29
+ ## After Assimilation: Contribute Back
30
+
31
+ After successfully assimilating a methodology or harness integration, prompt the user to share it with the community. The assimilated process definitions, skills, and agents could benefit other babysitter users:
32
+
33
+ - **Completed a methodology assimilation**: `/babysitter:contrib library contribution: assimilated [methodology-name] into babysitter process definitions`
34
+ - **Completed a harness integration**: `/babysitter:contrib library contribution: [harness-name] harness integration`
35
+ - **Hit issues during assimilation** (e.g. unsupported patterns, missing SDK features): `/babysitter:contrib bug report: assimilation of [target] failed because [description]` or `/babysitter:contrib feature request: [what the SDK needs to support]`
36
+
37
+ Even just reporting that an assimilation didn't work well helps improve babysitter for everyone.
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Orchestrate a babysitter run. use this command to start babysitting a complex workflow.
3
+ argument-hint: Specific instructions for the run.
4
+ allowed-tools: Read, Grep, Write, Task, Bash, Edit, Grep, Glob, WebFetch, WebSearch, Search, AskUserQuestion, TodoWrite, TodoRead, Skill, BashOutput, KillShell, MultiEdit, LS
5
+ ---
6
+
7
+ Invoke the babysitter:babysit skill (using the Skill tool) and follow its instructions (SKILL.md).
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: Clean up .a5c/runs and .a5c/processes directories. Aggregates insights from completed/failed runs into docs/run-history-insights.md, then removes old run data and orphaned process files.
3
+ argument-hint: "[--dry-run] [--keep-days N] Optional flags. --dry-run shows what would be removed without deleting. --keep-days N keeps runs newer than N days (default 7)."
4
+ allowed-tools: Read, Grep, Write, Task, Bash, Edit, Grep, Glob, WebFetch, WebSearch, Search, AskUserQuestion, TodoWrite, TodoRead, Skill, BashOutput, KillShell, MultiEdit, LS
5
+ ---
6
+
7
+ Invoke the babysitter:babysit skill (using the Skill tool) and follow its instructions (SKILL.md).
8
+
9
+ Create and run a cleanup process using the process at `skills\babysit\process\cradle\cleanup-runs.js/processes/cleanup-runs.js`.
10
+
11
+ Implementation notes (for the process):
12
+ - Parse arguments for `--dry-run` flag (if present, set dryRun: true in inputs) and `--keep-days N` (default: 7)
13
+ - The process scans .a5c/runs/ for completed/failed runs, aggregates insights, writes summaries, then removes old data
14
+ - Always show the user what will be removed before removing (in interactive mode via breakpoints)
15
+ - In non-interactive mode (yolo), proceed with cleanup using defaults
16
+ - The insights file goes to docs/run-history-insights.md
17
+ - Only remove terminal runs (completed/failed) older than the keep-days threshold
18
+ - Never remove active/in-progress runs
19
+ - Remove orphaned process files not referenced by remaining runs
20
+ - After cleanup, show remaining run count and disk usage