@a5c-ai/babysitter-codex 0.1.6-staging.060a3463

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.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Summarize a completed or current babysitter run.
3
+ argument-hint: [run-id]
4
+ ---
5
+
6
+ Use the installed `babysit` skill in `retrospect` mode.
7
+
8
+ Treat everything after `/retrospect` as the run selector to summarize.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Install or refresh the team-pinned babysitter setup.
3
+ argument-hint: [--dry-run]
4
+ ---
5
+
6
+ Use the installed `babysit` skill in `team-install` mode.
7
+
8
+ Treat everything after `/team-install` as team-install arguments or intent.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Set up babysitter user profile and defaults.
3
+ argument-hint: User setup instructions
4
+ ---
5
+
6
+ Use the installed `babysit` skill in `user-install` mode.
7
+
8
+ Treat everything after `/user-install` as the user-setup request.
@@ -0,0 +1,8 @@
1
+ ---
2
+ description: Start an autonomous babysitter orchestration run.
3
+ argument-hint: Specific instructions for the run
4
+ ---
5
+
6
+ Use the installed `babysit` skill in `yolo` mode.
7
+
8
+ Treat everything after `/yolo` as the autonomous execution request.
@@ -0,0 +1,439 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+
8
+ const SKILL_NAME = 'babysit';
9
+ const WORKSPACE_SKILL_ENTRIES = [
10
+ { source: 'SKILL.md', target: 'SKILL.md' },
11
+ { source: 'README.md', target: 'README.md' },
12
+ { source: 'agents', target: 'agents' },
13
+ { source: 'scripts', target: 'scripts' },
14
+ { source: 'babysitter.lock.json', target: 'babysitter.lock.json' },
15
+ ];
16
+
17
+ function listPromptEntries(packageRoot) {
18
+ const promptsDir = path.join(packageRoot, 'prompts');
19
+ return fs
20
+ .readdirSync(promptsDir)
21
+ .filter((name) => name.endsWith('.md') && name !== 'README.md')
22
+ .sort();
23
+ }
24
+
25
+ function readJson(filePath) {
26
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
27
+ }
28
+
29
+ function parseArgs(argv) {
30
+ const args = {
31
+ workspace: process.cwd(),
32
+ dryRun: false,
33
+ };
34
+ for (let i = 2; i < argv.length; i += 1) {
35
+ if (argv[i] === '--workspace' && argv[i + 1]) {
36
+ args.workspace = path.resolve(argv[++i]);
37
+ } else if (argv[i] === '--dry-run') {
38
+ args.dryRun = true;
39
+ }
40
+ }
41
+ return args;
42
+ }
43
+
44
+ function renderWorkspaceConfigToml() {
45
+ return [
46
+ 'approval_policy = "on-request"',
47
+ 'sandbox_mode = "workspace-write"',
48
+ 'project_doc_max_bytes = 65536',
49
+ '',
50
+ '[sandbox_workspace_write]',
51
+ 'writable_roots = [".a5c", ".codex"]',
52
+ '',
53
+ '[features]',
54
+ 'codex_hooks = true',
55
+ 'multi_agent = true',
56
+ '',
57
+ '[agents]',
58
+ 'max_depth = 3',
59
+ 'max_threads = 4',
60
+ '',
61
+ ].join('\n');
62
+ }
63
+
64
+ function writeFileIfChanged(filePath, contents) {
65
+ if (fs.existsSync(filePath)) {
66
+ const current = fs.readFileSync(filePath, 'utf8');
67
+ if (current === contents) {
68
+ return false;
69
+ }
70
+ }
71
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
72
+ fs.writeFileSync(filePath, contents, 'utf8');
73
+ return true;
74
+ }
75
+
76
+ function copyRecursive(src, dest) {
77
+ const stat = fs.statSync(src);
78
+ if (stat.isDirectory()) {
79
+ fs.mkdirSync(dest, { recursive: true });
80
+ for (const entry of fs.readdirSync(src)) {
81
+ if (['node_modules', '.a5c', '.git', 'test', '.gitignore'].includes(entry)) continue;
82
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
83
+ }
84
+ return;
85
+ }
86
+
87
+ if (path.basename(src) === 'SKILL.md') {
88
+ const file = fs.readFileSync(src);
89
+ const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
90
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
91
+ fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
92
+ return;
93
+ }
94
+
95
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
96
+ fs.copyFileSync(src, dest);
97
+ }
98
+
99
+ function installWorkspaceSkill(packageRoot, workspaceRoot, dryRun) {
100
+ const workspaceSkillRoot = path.join(workspaceRoot, '.codex', 'skills', SKILL_NAME);
101
+ const workspaceHookScriptsRoot = path.join(workspaceRoot, '.codex', 'hooks');
102
+ const workspacePromptsRoot = path.join(workspaceRoot, '.codex', 'prompts');
103
+
104
+ if (dryRun) {
105
+ return {
106
+ workspaceSkillRoot,
107
+ workspaceHookScriptsRoot,
108
+ workspacePromptsRoot,
109
+ };
110
+ }
111
+
112
+ fs.rmSync(workspaceSkillRoot, { recursive: true, force: true });
113
+ fs.mkdirSync(workspaceSkillRoot, { recursive: true });
114
+
115
+ for (const entry of WORKSPACE_SKILL_ENTRIES) {
116
+ copyRecursive(
117
+ path.join(packageRoot, entry.source),
118
+ path.join(workspaceSkillRoot, entry.target),
119
+ );
120
+ }
121
+
122
+ fs.mkdirSync(workspaceHookScriptsRoot, { recursive: true });
123
+ copyRecursive(path.join(packageRoot, '.codex', 'hooks'), workspaceHookScriptsRoot);
124
+
125
+ fs.mkdirSync(workspacePromptsRoot, { recursive: true });
126
+ for (const promptName of listPromptEntries(packageRoot)) {
127
+ copyRecursive(
128
+ path.join(packageRoot, 'prompts', promptName),
129
+ path.join(workspacePromptsRoot, promptName),
130
+ );
131
+ }
132
+
133
+ return {
134
+ workspaceSkillRoot,
135
+ workspaceHookScriptsRoot,
136
+ workspacePromptsRoot,
137
+ };
138
+ }
139
+
140
+ function insertRootKey(content, key, line) {
141
+ const keyPattern = new RegExp(`^\\s*${key}\\s*=`, 'm');
142
+ if (keyPattern.test(content)) {
143
+ return content;
144
+ }
145
+ const sectionMatch = content.match(/^\[[^\]]+\]\s*$/m);
146
+ if (!sectionMatch || sectionMatch.index === undefined) {
147
+ return content.trim()
148
+ ? `${content.trimEnd()}\n${line}\n`
149
+ : `${line}\n`;
150
+ }
151
+ const before = content.slice(0, sectionMatch.index).trimEnd();
152
+ const after = content.slice(sectionMatch.index);
153
+ return before
154
+ ? `${before}\n${line}\n\n${after}`
155
+ : `${line}\n\n${after}`;
156
+ }
157
+
158
+ function ensureSectionLine(content, sectionName, lineKey, line) {
159
+ const keyPattern = new RegExp(`^\\s*${lineKey}\\s*=`, 'm');
160
+ if (keyPattern.test(content)) {
161
+ return content;
162
+ }
163
+ const sectionHeader = `[${sectionName}]`;
164
+ const sectionPattern = new RegExp(`^\\[${sectionName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\]\\s*$`, 'm');
165
+ if (sectionPattern.test(content)) {
166
+ return content.replace(sectionPattern, `${sectionHeader}\n${line}`);
167
+ }
168
+ return content.trim()
169
+ ? `${content.trimEnd()}\n\n${sectionHeader}\n${line}\n`
170
+ : `${sectionHeader}\n${line}\n`;
171
+ }
172
+
173
+ function ensureWritableRoots(content) {
174
+ const sectionPattern = /^\[sandbox_workspace_write\]\s*$/m;
175
+ const rootsPattern = /^writable_roots\s*=\s*\[(.*?)\]\s*$/m;
176
+ const requiredRoots = ['.a5c', '.codex'];
177
+
178
+ if (!sectionPattern.test(content)) {
179
+ return content.trim()
180
+ ? `${content.trimEnd()}\n\n[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]\n`
181
+ : '[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]\n';
182
+ }
183
+
184
+ if (!rootsPattern.test(content)) {
185
+ return content.replace(sectionPattern, '[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]');
186
+ }
187
+
188
+ return content.replace(rootsPattern, (_match, inner) => {
189
+ const values = inner
190
+ .split(',')
191
+ .map((part) => part.trim())
192
+ .filter(Boolean)
193
+ .map((part) => part.replace(/^"(.*)"$/, '$1'));
194
+ const merged = [...new Set([...values, ...requiredRoots])];
195
+ const rendered = merged.map((value) => `"${value}"`).join(', ');
196
+ return `writable_roots = [${rendered}]`;
197
+ });
198
+ }
199
+
200
+ function mergeWorkspaceConfig(existing) {
201
+ let content = existing.trim() ? existing : '';
202
+ content = insertRootKey(content, 'approval_policy', 'approval_policy = "on-request"');
203
+ content = insertRootKey(content, 'sandbox_mode', 'sandbox_mode = "workspace-write"');
204
+ content = insertRootKey(content, 'project_doc_max_bytes', 'project_doc_max_bytes = 65536');
205
+ content = ensureWritableRoots(content);
206
+ content = ensureSectionLine(content, 'features', 'codex_hooks', 'codex_hooks = true');
207
+ content = ensureSectionLine(content, 'features', 'multi_agent', 'multi_agent = true');
208
+ content = ensureSectionLine(content, 'agents', 'max_depth', 'max_depth = 3');
209
+ content = ensureSectionLine(content, 'agents', 'max_threads', 'max_threads = 4');
210
+ return `${content.trimEnd()}\n`;
211
+ }
212
+
213
+ function resolveBabysitterCommand(packageRoot) {
214
+ if (process.env.BABYSITTER_SDK_CLI) {
215
+ return {
216
+ command: process.execPath,
217
+ argsPrefix: [path.resolve(process.env.BABYSITTER_SDK_CLI)],
218
+ };
219
+ }
220
+ try {
221
+ return {
222
+ command: process.execPath,
223
+ argsPrefix: [
224
+ require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
225
+ paths: [packageRoot],
226
+ }),
227
+ ],
228
+ };
229
+ } catch {
230
+ return {
231
+ command: 'babysitter',
232
+ argsPrefix: [],
233
+ };
234
+ }
235
+ }
236
+
237
+ function runBabysitterCli(packageRoot, cliArgs, options = {}) {
238
+ const resolved = resolveBabysitterCommand(packageRoot);
239
+ const result = spawnSync(resolved.command, [...resolved.argsPrefix, ...cliArgs], {
240
+ cwd: options.cwd || process.cwd(),
241
+ stdio: ['ignore', 'pipe', 'pipe'],
242
+ encoding: 'utf8',
243
+ env: {
244
+ ...process.env,
245
+ ...(options.env || {}),
246
+ },
247
+ });
248
+ if (result.status !== 0) {
249
+ const stderr = (result.stderr || '').trim();
250
+ const stdout = (result.stdout || '').trim();
251
+ throw new Error(
252
+ `babysitter ${cliArgs.join(' ')} failed` +
253
+ (stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
254
+ );
255
+ }
256
+ return result.stdout;
257
+ }
258
+
259
+ function resolveProcessLibrarySpec(lock, workspaceRoot) {
260
+ const processLibraryConfig = (lock && lock.content && (lock.content.processLibrary || lock.content.upstream)) || {};
261
+ const repo = process.env.BABYSITTER_PROCESS_LIBRARY_REPO || processLibraryConfig.repo;
262
+ if (!repo) {
263
+ throw new Error('missing process-library repo configuration in babysitter.lock.json');
264
+ }
265
+ const ref = process.env.BABYSITTER_PROCESS_LIBRARY_REF || processLibraryConfig.ref || '';
266
+ const cloneDir = path.join(workspaceRoot, '.a5c', 'process-library', 'babysitter-repo');
267
+ const processSubpath = process.env.BABYSITTER_PROCESS_LIBRARY_SUBPATH ||
268
+ processLibraryConfig.processSubpath ||
269
+ 'library';
270
+ const referenceSubpath = process.env.BABYSITTER_PROCESS_LIBRARY_REFERENCE_SUBPATH ||
271
+ processLibraryConfig.referenceSubpath ||
272
+ 'library/reference';
273
+ return {
274
+ repo,
275
+ ref: ref || undefined,
276
+ cloneDir,
277
+ processRoot: path.join(cloneDir, ...processSubpath.split('/')),
278
+ referenceRoot: path.join(cloneDir, ...referenceSubpath.split('/')),
279
+ stateDir: path.join(workspaceRoot, '.a5c'),
280
+ };
281
+ }
282
+
283
+ function ensureActiveProcessLibrary(packageRoot, lock, workspaceRoot, dryRun) {
284
+ const spec = resolveProcessLibrarySpec(lock, workspaceRoot);
285
+ const cloneExists = fs.existsSync(path.join(spec.cloneDir, '.git'));
286
+ const cloneArgs = cloneExists
287
+ ? ['process-library:update', '--dir', spec.cloneDir, '--json']
288
+ : ['process-library:clone', '--repo', spec.repo, '--dir', spec.cloneDir, '--json'];
289
+ if (spec.ref) {
290
+ cloneArgs.splice(cloneExists ? 3 : 5, 0, '--ref', spec.ref);
291
+ }
292
+ const useArgs = ['process-library:use', '--dir', spec.processRoot, '--state-dir', spec.stateDir, '--json'];
293
+ const activeArgs = ['process-library:active', '--state-dir', spec.stateDir, '--json'];
294
+
295
+ if (dryRun) {
296
+ return {
297
+ ...spec,
298
+ plannedCommands: [
299
+ `babysitter ${cloneArgs.join(' ')}`,
300
+ `babysitter ${useArgs.join(' ')}`,
301
+ `babysitter ${activeArgs.join(' ')}`,
302
+ ],
303
+ activeStateFile: path.join(spec.stateDir, 'active', 'process-library.json'),
304
+ binding: null,
305
+ };
306
+ }
307
+
308
+ runBabysitterCli(packageRoot, cloneArgs, { cwd: workspaceRoot });
309
+ if (!fs.existsSync(spec.processRoot)) {
310
+ throw new Error(`fetched process library root is missing: ${spec.processRoot}`);
311
+ }
312
+ runBabysitterCli(packageRoot, useArgs, { cwd: workspaceRoot });
313
+ const active = JSON.parse(runBabysitterCli(packageRoot, activeArgs, { cwd: workspaceRoot }));
314
+ return {
315
+ ...spec,
316
+ plannedCommands: [],
317
+ activeStateFile: active.stateFile,
318
+ binding: active.binding || null,
319
+ };
320
+ }
321
+
322
+ function buildHooksConfig() {
323
+ return {
324
+ hooks: {
325
+ SessionStart: [
326
+ {
327
+ matcher: '*',
328
+ hooks: [
329
+ {
330
+ type: 'command',
331
+ command: '.codex/hooks/babysitter-session-start.sh',
332
+ },
333
+ ],
334
+ },
335
+ ],
336
+ UserPromptSubmit: [
337
+ {
338
+ matcher: '*',
339
+ hooks: [
340
+ {
341
+ type: 'command',
342
+ command: '.codex/hooks/user-prompt-submit.sh',
343
+ },
344
+ ],
345
+ },
346
+ ],
347
+ Stop: [
348
+ {
349
+ matcher: '*',
350
+ hooks: [
351
+ {
352
+ type: 'command',
353
+ command: '.codex/hooks/babysitter-stop-hook.sh',
354
+ },
355
+ ],
356
+ },
357
+ ],
358
+ },
359
+ };
360
+ }
361
+
362
+ function main() {
363
+ const args = parseArgs(process.argv);
364
+ const packageRoot = path.resolve(process.env.BABYSITTER_PACKAGE_ROOT || path.join(__dirname, '..'));
365
+ const workspaceRoot = args.workspace;
366
+ const lockPath = path.join(packageRoot, 'babysitter.lock.json');
367
+ if (!fs.existsSync(lockPath)) {
368
+ throw new Error(`missing lock file: ${lockPath}`);
369
+ }
370
+ const lock = readJson(lockPath);
371
+ const workspaceHooksConfigPath = path.join(workspaceRoot, '.codex', 'hooks.json');
372
+ const workspaceConfigPath = path.join(workspaceRoot, '.codex', 'config.toml');
373
+ const { workspaceSkillRoot, workspaceHookScriptsRoot, workspacePromptsRoot } = installWorkspaceSkill(packageRoot, workspaceRoot, args.dryRun);
374
+ const processLibrary = ensureActiveProcessLibrary(packageRoot, lock, workspaceRoot, args.dryRun);
375
+ const installInfo = {
376
+ installedAt: new Date().toISOString(),
377
+ runtime: lock.runtime,
378
+ content: lock.content,
379
+ lockVersion: lock.version,
380
+ packageRoot,
381
+ workspaceRoot,
382
+ workspaceSkillRoot,
383
+ workspacePromptsRoot,
384
+ workspaceConfigPath,
385
+ workspaceHooksConfigPath,
386
+ hookScriptsRoot: workspaceHookScriptsRoot,
387
+ processLibraryRepo: processLibrary.repo,
388
+ ...(processLibrary.ref ? { processLibraryRef: processLibrary.ref } : {}),
389
+ processLibraryCloneDir: processLibrary.cloneDir,
390
+ processLibraryRoot: processLibrary.processRoot,
391
+ processLibraryReferenceRoot: processLibrary.referenceRoot,
392
+ processLibraryStateFile: processLibrary.activeStateFile,
393
+ };
394
+
395
+ if (args.dryRun) {
396
+ console.log(JSON.stringify({
397
+ ok: true,
398
+ dryRun: true,
399
+ installInfo,
400
+ processLibrary: {
401
+ repo: processLibrary.repo,
402
+ ...(processLibrary.ref ? { ref: processLibrary.ref } : {}),
403
+ cloneDir: processLibrary.cloneDir,
404
+ processRoot: processLibrary.processRoot,
405
+ referenceRoot: processLibrary.referenceRoot,
406
+ stateFile: processLibrary.activeStateFile,
407
+ plannedCommands: processLibrary.plannedCommands,
408
+ },
409
+ }, null, 2));
410
+ return;
411
+ }
412
+
413
+ const outDir = path.join(workspaceRoot, '.a5c', 'team');
414
+ fs.mkdirSync(outDir, { recursive: true });
415
+ fs.mkdirSync(path.dirname(workspaceHooksConfigPath), { recursive: true });
416
+ writeFileIfChanged(workspaceHooksConfigPath, `${JSON.stringify(buildHooksConfig(), null, 2)}\n`);
417
+ const existingWorkspaceConfig = fs.existsSync(workspaceConfigPath)
418
+ ? fs.readFileSync(workspaceConfigPath, 'utf8')
419
+ : renderWorkspaceConfigToml();
420
+ writeFileIfChanged(workspaceConfigPath, mergeWorkspaceConfig(existingWorkspaceConfig));
421
+ fs.writeFileSync(path.join(outDir, 'install.json'), JSON.stringify(installInfo, null, 2), 'utf8');
422
+
423
+ const profilePath = path.join(outDir, 'profile.json');
424
+ if (!fs.existsSync(profilePath)) {
425
+ fs.writeFileSync(profilePath, JSON.stringify({
426
+ teamName: 'default',
427
+ installedSkillRoot: workspaceSkillRoot,
428
+ workspacePromptsRoot,
429
+ workspaceConfigPath,
430
+ workspaceHooksConfigPath,
431
+ hookScriptsRoot: workspaceHookScriptsRoot,
432
+ processLibraryLookupCommand: 'babysitter process-library:active --state-dir .a5c --json',
433
+ }, null, 2), 'utf8');
434
+ }
435
+
436
+ console.log('[team-install] complete');
437
+ }
438
+
439
+ main();