@a5c-ai/babysitter-codex 0.1.6-staging.2dca8387

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 (51) hide show
  1. package/.codex/AGENTS.md +53 -0
  2. package/.codex/command-catalog.json +130 -0
  3. package/.codex/config.toml +24 -0
  4. package/.codex/hooks/babysitter-session-start.sh +15 -0
  5. package/.codex/hooks/babysitter-stop-hook.sh +15 -0
  6. package/.codex/hooks/user-prompt-submit.sh +15 -0
  7. package/.codex/hooks.json +37 -0
  8. package/.codex/plugin.json +132 -0
  9. package/.codex/skills/babysitter/assimilate/SKILL.md +58 -0
  10. package/.codex/skills/babysitter/call/SKILL.md +590 -0
  11. package/.codex/skills/babysitter/doctor/SKILL.md +89 -0
  12. package/.codex/skills/babysitter/forever/SKILL.md +45 -0
  13. package/.codex/skills/babysitter/help/SKILL.md +49 -0
  14. package/.codex/skills/babysitter/issue/SKILL.md +36 -0
  15. package/.codex/skills/babysitter/model/SKILL.md +31 -0
  16. package/.codex/skills/babysitter/observe/SKILL.md +38 -0
  17. package/.codex/skills/babysitter/plan/SKILL.md +44 -0
  18. package/.codex/skills/babysitter/project-install/SKILL.md +65 -0
  19. package/.codex/skills/babysitter/resume/SKILL.md +30 -0
  20. package/.codex/skills/babysitter/retrospect/SKILL.md +43 -0
  21. package/.codex/skills/babysitter/team-install/SKILL.md +31 -0
  22. package/.codex/skills/babysitter/user-install/SKILL.md +53 -0
  23. package/.codex/skills/babysitter/yolo/SKILL.md +48 -0
  24. package/AGENTS.md +91 -0
  25. package/CHANGELOG.md +162 -0
  26. package/README.md +146 -0
  27. package/SKILL.md +89 -0
  28. package/agents/openai.yaml +4 -0
  29. package/babysitter.lock.json +18 -0
  30. package/bin/postinstall.js +225 -0
  31. package/bin/uninstall.js +37 -0
  32. package/commands/README.md +23 -0
  33. package/commands/assimilate.md +27 -0
  34. package/commands/call.md +30 -0
  35. package/commands/doctor.md +27 -0
  36. package/commands/forever.md +27 -0
  37. package/commands/help.md +28 -0
  38. package/commands/issue.md +27 -0
  39. package/commands/model.md +27 -0
  40. package/commands/observe.md +27 -0
  41. package/commands/plan.md +27 -0
  42. package/commands/project-install.md +31 -0
  43. package/commands/resume.md +29 -0
  44. package/commands/retrospect.md +27 -0
  45. package/commands/team-install.md +29 -0
  46. package/commands/user-install.md +27 -0
  47. package/commands/yolo.md +28 -0
  48. package/package.json +50 -0
  49. package/scripts/team-install.js +257 -0
  50. package/test/integration.test.js +69 -0
  51. package/test/packaged-install.test.js +191 -0
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Start a non-interactive autonomous orchestration run (no breakpoints).
3
+ argument-hint: Specific instructions for the run
4
+ ---
5
+
6
+ # babysitter:yolo
7
+
8
+ ## Purpose
9
+
10
+ Start a non-interactive autonomous orchestration run (no breakpoints).
11
+
12
+ ## Usage
13
+
14
+ ```text
15
+ babysitter yolo Specific instructions for the run
16
+ ```
17
+
18
+ ## Example
19
+
20
+ ```text
21
+ babysitter yolo fix lint and failing tests
22
+ ```
23
+
24
+ ## Notes
25
+
26
+ - Use command phrases in Codex chat (`babysitter ...`), not custom slash commands.
27
+ - Autonomous progression still uses the hook-first runtime; yolo mode only auto-approves breakpoints.
28
+ - If SDK capabilities are missing in your installed version, babysitter-codex falls back to compatibility behavior where possible.
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@a5c-ai/babysitter-codex",
3
+ "version": "0.1.6-staging.2dca8387",
4
+ "description": "Babysitter Codex skill bundle and integration package for OpenAI Codex CLI with SDK-managed process-library bootstrapping, 15 orchestration modes, and BOM-safe SKILL installation",
5
+ "scripts": {
6
+ "test": "node test/integration.test.js && node test/packaged-install.test.js",
7
+ "test:integration": "node test/integration.test.js",
8
+ "check:mapping": "node scripts/check-mapping-contract.js",
9
+ "team:install": "node scripts/team-install.js",
10
+ "lint": "find .codex -name '*.js' | xargs -I{} node --check {}",
11
+ "deploy": "npm publish --access public",
12
+ "deploy:staging": "npm publish --access public --tag staging",
13
+ "postinstall": "node bin/postinstall.js",
14
+ "preuninstall": "node bin/uninstall.js"
15
+ },
16
+ "files": [
17
+ "SKILL.md",
18
+ "AGENTS.md",
19
+ "CHANGELOG.md",
20
+ "agents/",
21
+ "commands/",
22
+ ".codex/",
23
+ "bin/",
24
+ "scripts/",
25
+ "test/",
26
+ "babysitter.lock.json",
27
+ "README.md"
28
+ ],
29
+ "keywords": [
30
+ "babysitter",
31
+ "codex",
32
+ "orchestration",
33
+ "ai-agent",
34
+ "sdk-integration",
35
+ "codex-skill"
36
+ ],
37
+ "author": "a5c.ai",
38
+ "license": "MIT",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/a5c-ai/babysitter"
45
+ },
46
+ "homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-codex#readme",
47
+ "dependencies": {
48
+ "@a5c-ai/babysitter-sdk": "0.0.183-staging.2dca8387"
49
+ }
50
+ }
@@ -0,0 +1,257 @@
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
+ function readJson(filePath) {
9
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
10
+ }
11
+
12
+ function parseArgs(argv) {
13
+ const args = {
14
+ workspace: process.cwd(),
15
+ dryRun: false,
16
+ };
17
+ for (let i = 2; i < argv.length; i += 1) {
18
+ if (argv[i] === '--workspace' && argv[i + 1]) {
19
+ args.workspace = path.resolve(argv[++i]);
20
+ } else if (argv[i] === '--dry-run') {
21
+ args.dryRun = true;
22
+ }
23
+ }
24
+ return args;
25
+ }
26
+
27
+ function renderWorkspaceConfigToml() {
28
+ return [
29
+ 'approval_policy = "on-request"',
30
+ 'sandbox_mode = "workspace-write"',
31
+ 'project_doc_max_bytes = 65536',
32
+ '',
33
+ '[sandbox_workspace_write]',
34
+ 'writable_roots = [".a5c", ".codex"]',
35
+ '',
36
+ '[features]',
37
+ 'codex_hooks = true',
38
+ 'multi_agent = true',
39
+ '',
40
+ '[agents]',
41
+ 'max_depth = 3',
42
+ 'max_threads = 4',
43
+ '',
44
+ ].join('\n');
45
+ }
46
+
47
+ function resolveSdkCli(packageRoot) {
48
+ if (process.env.BABYSITTER_SDK_CLI) {
49
+ return path.resolve(process.env.BABYSITTER_SDK_CLI);
50
+ }
51
+ try {
52
+ return require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
53
+ paths: [packageRoot],
54
+ });
55
+ } catch (error) {
56
+ throw new Error(`could not resolve Babysitter SDK CLI entrypoint: ${error.message}`);
57
+ }
58
+ }
59
+
60
+ function runBabysitterCli(packageRoot, cliArgs, options = {}) {
61
+ const cliMain = resolveSdkCli(packageRoot);
62
+ const result = spawnSync(process.execPath, [cliMain, ...cliArgs], {
63
+ cwd: options.cwd || process.cwd(),
64
+ stdio: ['ignore', 'pipe', 'pipe'],
65
+ encoding: 'utf8',
66
+ env: {
67
+ ...process.env,
68
+ ...(options.env || {}),
69
+ },
70
+ });
71
+ if (result.status !== 0) {
72
+ const stderr = (result.stderr || '').trim();
73
+ const stdout = (result.stdout || '').trim();
74
+ throw new Error(
75
+ `babysitter ${cliArgs.join(' ')} failed` +
76
+ (stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
77
+ );
78
+ }
79
+ return result.stdout;
80
+ }
81
+
82
+ function resolveProcessLibrarySpec(lock, workspaceRoot) {
83
+ const processLibraryConfig = (lock && lock.content && (lock.content.processLibrary || lock.content.upstream)) || {};
84
+ const repo = process.env.BABYSITTER_PROCESS_LIBRARY_REPO || processLibraryConfig.repo;
85
+ if (!repo) {
86
+ throw new Error('missing process-library repo configuration in babysitter.lock.json');
87
+ }
88
+ const ref = process.env.BABYSITTER_PROCESS_LIBRARY_REF || processLibraryConfig.ref || '';
89
+ const cloneDir = path.join(workspaceRoot, '.a5c', 'process-library', 'babysitter-repo');
90
+ const processSubpath = process.env.BABYSITTER_PROCESS_LIBRARY_SUBPATH ||
91
+ processLibraryConfig.processSubpath ||
92
+ 'library';
93
+ const referenceSubpath = process.env.BABYSITTER_PROCESS_LIBRARY_REFERENCE_SUBPATH ||
94
+ processLibraryConfig.referenceSubpath ||
95
+ 'library/reference';
96
+ return {
97
+ repo,
98
+ ref: ref || undefined,
99
+ cloneDir,
100
+ processRoot: path.join(cloneDir, ...processSubpath.split('/')),
101
+ referenceRoot: path.join(cloneDir, ...referenceSubpath.split('/')),
102
+ stateDir: path.join(workspaceRoot, '.a5c'),
103
+ };
104
+ }
105
+
106
+ function ensureActiveProcessLibrary(packageRoot, lock, workspaceRoot, dryRun) {
107
+ const spec = resolveProcessLibrarySpec(lock, workspaceRoot);
108
+ const cloneExists = fs.existsSync(path.join(spec.cloneDir, '.git'));
109
+ const cloneArgs = cloneExists
110
+ ? ['process-library:update', '--dir', spec.cloneDir, '--json']
111
+ : ['process-library:clone', '--repo', spec.repo, '--dir', spec.cloneDir, '--json'];
112
+ if (spec.ref) {
113
+ cloneArgs.splice(cloneExists ? 3 : 5, 0, '--ref', spec.ref);
114
+ }
115
+ const useArgs = ['process-library:use', '--dir', spec.processRoot, '--state-dir', spec.stateDir, '--json'];
116
+ const activeArgs = ['process-library:active', '--state-dir', spec.stateDir, '--json'];
117
+
118
+ if (dryRun) {
119
+ return {
120
+ ...spec,
121
+ plannedCommands: [
122
+ `babysitter ${cloneArgs.join(' ')}`,
123
+ `babysitter ${useArgs.join(' ')}`,
124
+ `babysitter ${activeArgs.join(' ')}`,
125
+ ],
126
+ activeStateFile: path.join(spec.stateDir, 'active', 'process-library.json'),
127
+ binding: null,
128
+ };
129
+ }
130
+
131
+ runBabysitterCli(packageRoot, cloneArgs, { cwd: workspaceRoot });
132
+ if (!fs.existsSync(spec.processRoot)) {
133
+ throw new Error(`fetched process library root is missing: ${spec.processRoot}`);
134
+ }
135
+ runBabysitterCli(packageRoot, useArgs, { cwd: workspaceRoot });
136
+ const active = JSON.parse(runBabysitterCli(packageRoot, activeArgs, { cwd: workspaceRoot }));
137
+ return {
138
+ ...spec,
139
+ plannedCommands: [],
140
+ activeStateFile: active.stateFile,
141
+ binding: active.binding || null,
142
+ };
143
+ }
144
+
145
+ function buildHooksConfig(packageRoot) {
146
+ return {
147
+ hooks: {
148
+ SessionStart: [
149
+ {
150
+ matcher: '*',
151
+ hooks: [
152
+ {
153
+ type: 'command',
154
+ command: path.join(packageRoot, '.codex', 'hooks', 'babysitter-session-start.sh'),
155
+ },
156
+ ],
157
+ },
158
+ ],
159
+ UserPromptSubmit: [
160
+ {
161
+ matcher: '*',
162
+ hooks: [
163
+ {
164
+ type: 'command',
165
+ command: path.join(packageRoot, '.codex', 'hooks', 'user-prompt-submit.sh'),
166
+ },
167
+ ],
168
+ },
169
+ ],
170
+ Stop: [
171
+ {
172
+ matcher: '*',
173
+ hooks: [
174
+ {
175
+ type: 'command',
176
+ command: path.join(packageRoot, '.codex', 'hooks', 'babysitter-stop-hook.sh'),
177
+ },
178
+ ],
179
+ },
180
+ ],
181
+ },
182
+ };
183
+ }
184
+
185
+ function main() {
186
+ const packageRoot = path.resolve(__dirname, '..');
187
+ const args = parseArgs(process.argv);
188
+ const workspaceRoot = args.workspace;
189
+ const lockPath = path.join(packageRoot, 'babysitter.lock.json');
190
+ if (!fs.existsSync(lockPath)) {
191
+ throw new Error(`missing lock file: ${lockPath}`);
192
+ }
193
+ const lock = readJson(lockPath);
194
+ const workspaceHooksConfigPath = path.join(workspaceRoot, '.codex', 'hooks.json');
195
+ const workspaceConfigPath = path.join(workspaceRoot, '.codex', 'config.toml');
196
+ const processLibrary = ensureActiveProcessLibrary(packageRoot, lock, workspaceRoot, args.dryRun);
197
+ const installInfo = {
198
+ installedAt: new Date().toISOString(),
199
+ runtime: lock.runtime,
200
+ content: lock.content,
201
+ lockVersion: lock.version,
202
+ packageRoot,
203
+ workspaceRoot,
204
+ workspaceConfigPath,
205
+ workspaceHooksConfigPath,
206
+ hookScriptsRoot: path.join(packageRoot, '.codex', 'hooks'),
207
+ processLibraryRepo: processLibrary.repo,
208
+ ...(processLibrary.ref ? { processLibraryRef: processLibrary.ref } : {}),
209
+ processLibraryCloneDir: processLibrary.cloneDir,
210
+ processLibraryRoot: processLibrary.processRoot,
211
+ processLibraryReferenceRoot: processLibrary.referenceRoot,
212
+ processLibraryStateFile: processLibrary.activeStateFile,
213
+ };
214
+
215
+ if (args.dryRun) {
216
+ console.log(JSON.stringify({
217
+ ok: true,
218
+ dryRun: true,
219
+ installInfo,
220
+ processLibrary: {
221
+ repo: processLibrary.repo,
222
+ ...(processLibrary.ref ? { ref: processLibrary.ref } : {}),
223
+ cloneDir: processLibrary.cloneDir,
224
+ processRoot: processLibrary.processRoot,
225
+ referenceRoot: processLibrary.referenceRoot,
226
+ stateFile: processLibrary.activeStateFile,
227
+ plannedCommands: processLibrary.plannedCommands,
228
+ },
229
+ }, null, 2));
230
+ return;
231
+ }
232
+
233
+ const outDir = path.join(workspaceRoot, '.a5c', 'team');
234
+ fs.mkdirSync(outDir, { recursive: true });
235
+ fs.mkdirSync(path.dirname(workspaceHooksConfigPath), { recursive: true });
236
+ fs.writeFileSync(workspaceHooksConfigPath, JSON.stringify(buildHooksConfig(packageRoot), null, 2), 'utf8');
237
+ if (!fs.existsSync(workspaceConfigPath)) {
238
+ fs.writeFileSync(workspaceConfigPath, renderWorkspaceConfigToml(), 'utf8');
239
+ }
240
+ fs.writeFileSync(path.join(outDir, 'install.json'), JSON.stringify(installInfo, null, 2), 'utf8');
241
+
242
+ const profilePath = path.join(outDir, 'profile.json');
243
+ if (!fs.existsSync(profilePath)) {
244
+ fs.writeFileSync(profilePath, JSON.stringify({
245
+ teamName: 'default',
246
+ installedSkillRoot: packageRoot,
247
+ workspaceConfigPath,
248
+ workspaceHooksConfigPath,
249
+ hookScriptsRoot: path.join(packageRoot, '.codex', 'hooks'),
250
+ processLibraryLookupCommand: 'babysitter process-library:active --state-dir .a5c --json',
251
+ }, null, 2), 'utf8');
252
+ }
253
+
254
+ console.log('[team-install] complete');
255
+ }
256
+
257
+ main();
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+ const { execFileSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const assert = require('assert');
6
+
7
+ const PROJECT_ROOT = path.resolve(__dirname, '..');
8
+ const CODEX_DIR = path.join(PROJECT_ROOT, '.codex');
9
+ const BIN_DIR = path.join(PROJECT_ROOT, 'bin');
10
+
11
+ // Test: All JS files pass node --check
12
+ function testSyntax() {
13
+ const jsFiles = [];
14
+ // Collect all JS files
15
+ function collectJs(dir) {
16
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
17
+ const full = path.join(dir, entry.name);
18
+ if (entry.isDirectory() && entry.name !== 'node_modules') collectJs(full);
19
+ else if (entry.isFile() && entry.name.endsWith('.js')) jsFiles.push(full);
20
+ }
21
+ }
22
+ collectJs(CODEX_DIR);
23
+ if (fs.existsSync(BIN_DIR)) collectJs(BIN_DIR);
24
+
25
+ let passed = 0;
26
+ for (const file of jsFiles) {
27
+ try {
28
+ execFileSync('node', ['--check', file], { encoding: 'utf8' });
29
+ passed++;
30
+ } catch (err) {
31
+ console.error(` ✗ Syntax error in ${path.relative(PROJECT_ROOT, file)}`);
32
+ throw err;
33
+ }
34
+ }
35
+ console.log(` ✓ syntax: ${passed} JS files pass node --check`);
36
+ }
37
+
38
+ // Test: Shell hook scripts have valid syntax
39
+ function testShellSyntax() {
40
+ const shellScripts = [
41
+ 'hooks/babysitter-session-start.sh',
42
+ 'hooks/babysitter-stop-hook.sh',
43
+ 'hooks/user-prompt-submit.sh',
44
+ ];
45
+
46
+ for (const script of shellScripts) {
47
+ const shellFile = path.join(CODEX_DIR, script);
48
+ if (!fs.existsSync(shellFile)) {
49
+ throw new Error(`Expected shell script not found: ${script}`);
50
+ }
51
+ try {
52
+ execFileSync('sh', ['-n', shellFile], { encoding: 'utf8' });
53
+ console.log(` ✓ shell: ${script} passes sh -n`);
54
+ } catch (err) {
55
+ console.error(` ✗ ${script} has syntax errors`);
56
+ throw err;
57
+ }
58
+ }
59
+ }
60
+
61
+ console.log('Integration Tests:');
62
+ try {
63
+ testSyntax();
64
+ testShellSyntax();
65
+ console.log('\nAll integration tests passed!');
66
+ } catch (err) {
67
+ console.error('\nTest failed:', err.message);
68
+ process.exit(1);
69
+ }
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const { execFileSync } = require('child_process');
8
+
9
+ const PROJECT_ROOT = path.resolve(__dirname, '..');
10
+
11
+ function run(cmd, args, options = {}) {
12
+ const execOptions = {
13
+ cwd: PROJECT_ROOT,
14
+ encoding: 'utf8',
15
+ stdio: ['ignore', 'pipe', 'pipe'],
16
+ maxBuffer: 20 * 1024 * 1024,
17
+ ...options,
18
+ };
19
+ if (process.platform === 'win32' && /\.(cmd|bat)$/i.test(cmd)) {
20
+ return execFileSync(cmd, args, {
21
+ ...execOptions,
22
+ shell: true,
23
+ });
24
+ }
25
+ return execFileSync(cmd, args, execOptions);
26
+ }
27
+
28
+ function readJson(filePath) {
29
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
30
+ }
31
+
32
+ function copyTree(src, dest) {
33
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
34
+ fs.cpSync(src, dest, { recursive: true });
35
+ }
36
+
37
+ function assertExists(root, relativePath) {
38
+ const full = path.join(root, relativePath);
39
+ assert.ok(fs.existsSync(full), `Missing installed payload: ${relativePath}`);
40
+ return full;
41
+ }
42
+
43
+ function cliPath(filePath) {
44
+ return process.platform === 'win32' ? String(filePath).replace(/\\/g, '/') : filePath;
45
+ }
46
+
47
+ function resolveNpmCommand() {
48
+ if (process.platform !== 'win32') return 'npm';
49
+ return path.join(path.dirname(process.execPath), 'npm.cmd');
50
+ }
51
+
52
+ console.log('Packaged Install Tests:');
53
+
54
+ let tmpRoot;
55
+ let packedTgzPath;
56
+ try {
57
+ tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'babysitter-codex-pack-'));
58
+ const extractDir = path.join(tmpRoot, 'extract');
59
+ const codexHome = path.join(tmpRoot, 'codex-home');
60
+ const workspaceRoot = path.join(tmpRoot, 'workspace');
61
+ const processLibraryRepoRoot = path.join(tmpRoot, 'process-library-source');
62
+ fs.mkdirSync(extractDir, { recursive: true });
63
+ fs.mkdirSync(codexHome, { recursive: true });
64
+ fs.mkdirSync(workspaceRoot, { recursive: true });
65
+ copyTree(
66
+ path.join(PROJECT_ROOT, '..', '..', 'library'),
67
+ path.join(processLibraryRepoRoot, 'library'),
68
+ );
69
+ run('git', ['init'], { cwd: processLibraryRepoRoot });
70
+ run('git', ['config', 'gc.auto', '0'], { cwd: processLibraryRepoRoot });
71
+ run('git', ['config', 'maintenance.auto', 'false'], { cwd: processLibraryRepoRoot });
72
+ run('git', ['add', '.'], { cwd: processLibraryRepoRoot });
73
+ run(
74
+ 'git',
75
+ ['-c', 'user.name=Babysitter Test', '-c', 'user.email=test@example.com', 'commit', '-m', 'seed process library'],
76
+ { cwd: processLibraryRepoRoot },
77
+ );
78
+
79
+ const packInfo = JSON.parse(run(resolveNpmCommand(), ['pack', '--json']).trim());
80
+ packedTgzPath = path.join(PROJECT_ROOT, packInfo[0].filename);
81
+ const tarArgs = process.platform === 'win32'
82
+ ? ['--force-local', '-xf', cliPath(packedTgzPath), '-C', cliPath(extractDir)]
83
+ : ['-xf', packedTgzPath, '-C', extractDir];
84
+ run('tar', tarArgs);
85
+
86
+ const packagedRoot = path.join(extractDir, 'package');
87
+ const installOutput = run(process.execPath, ['bin/postinstall.js'], {
88
+ cwd: packagedRoot,
89
+ env: {
90
+ ...process.env,
91
+ BABYSITTER_SDK_CLI: path.join(PROJECT_ROOT, '..', '..', 'packages', 'sdk', 'dist', 'cli', 'main.js'),
92
+ BABYSITTER_PROCESS_LIBRARY_REPO: processLibraryRepoRoot,
93
+ CODEX_HOME: codexHome,
94
+ INIT_CWD: workspaceRoot,
95
+ },
96
+ });
97
+ assert.ok(installOutput.includes('Installation complete!'));
98
+
99
+ const installedSkillRoot = path.join(codexHome, 'skills', 'babysitter-codex');
100
+ [
101
+ 'SKILL.md',
102
+ 'AGENTS.md',
103
+ 'README.md',
104
+ 'agents',
105
+ '.codex',
106
+ 'bin',
107
+ 'commands',
108
+ 'scripts',
109
+ 'babysitter.lock.json',
110
+ ].forEach((relativePath) => assertExists(installedSkillRoot, relativePath));
111
+ assert.ok(!fs.existsSync(path.join(installedSkillRoot, 'upstream')), 'installed package should not bundle upstream content');
112
+ assert.ok(!fs.existsSync(path.join(installedSkillRoot, 'config')), 'installed package should not ship redundant config payload');
113
+ assert.ok(!fs.existsSync(path.join(installedSkillRoot, 'docs')), 'installed package should not ship redundant docs payload');
114
+
115
+ const skillBytes = fs.readFileSync(path.join(installedSkillRoot, 'SKILL.md'));
116
+ const hasBom = skillBytes.length >= 3 && skillBytes[0] === 0xef && skillBytes[1] === 0xbb && skillBytes[2] === 0xbf;
117
+ assert.strictEqual(hasBom, false, 'Installed SKILL.md should not contain a UTF-8 BOM');
118
+ const installedSkill = fs.readFileSync(path.join(installedSkillRoot, 'SKILL.md'), 'utf8');
119
+ assert.ok(installedSkill.includes('SessionStart'));
120
+ assert.ok(installedSkill.includes('Stop'));
121
+ const homeConfig = fs.readFileSync(path.join(codexHome, 'config.toml'), 'utf8');
122
+ assert.ok(homeConfig.includes('codex_hooks = true'));
123
+ assert.ok(homeConfig.includes('multi_agent = true'));
124
+
125
+ const teamInstallOutput = run(process.execPath, [path.join(installedSkillRoot, 'scripts', 'team-install.js')], {
126
+ cwd: workspaceRoot,
127
+ env: {
128
+ ...process.env,
129
+ BABYSITTER_PACKAGE_ROOT: installedSkillRoot,
130
+ BABYSITTER_SDK_CLI: path.join(PROJECT_ROOT, '..', '..', 'packages', 'sdk', 'dist', 'cli', 'main.js'),
131
+ BABYSITTER_PROCESS_LIBRARY_REPO: processLibraryRepoRoot,
132
+ },
133
+ });
134
+ assert.ok(teamInstallOutput.includes('[team-install] complete'));
135
+ assert.ok(fs.existsSync(path.join(workspaceRoot, '.codex', 'hooks.json')));
136
+ assert.ok(fs.existsSync(path.join(workspaceRoot, '.codex', 'config.toml')));
137
+
138
+ const installJson = readJson(path.join(workspaceRoot, '.a5c', 'team', 'install.json'));
139
+ const profileJson = readJson(path.join(workspaceRoot, '.a5c', 'team', 'profile.json'));
140
+
141
+ assert.strictEqual(path.resolve(installJson.packageRoot), path.resolve(installedSkillRoot));
142
+ assert.strictEqual(path.resolve(installJson.workspaceRoot), path.resolve(workspaceRoot));
143
+ assert.strictEqual(
144
+ path.resolve(installJson.workspaceHooksConfigPath),
145
+ path.resolve(path.join(workspaceRoot, '.codex', 'hooks.json')),
146
+ );
147
+ assert.strictEqual(
148
+ path.resolve(installJson.workspaceConfigPath),
149
+ path.resolve(path.join(workspaceRoot, '.codex', 'config.toml')),
150
+ );
151
+ assert.strictEqual(
152
+ path.resolve(installJson.hookScriptsRoot),
153
+ path.resolve(path.join(installedSkillRoot, '.codex', 'hooks')),
154
+ );
155
+ assert.strictEqual(path.resolve(installJson.processLibraryCloneDir), path.resolve(path.join(workspaceRoot, '.a5c', 'process-library', 'babysitter-repo')));
156
+ assert.strictEqual(path.resolve(installJson.processLibraryRoot), path.resolve(path.join(installJson.processLibraryCloneDir, 'library')));
157
+ assert.strictEqual(path.resolve(installJson.processLibraryReferenceRoot), path.resolve(path.join(installJson.processLibraryCloneDir, 'library', 'reference')));
158
+ assert.strictEqual(
159
+ path.resolve(installJson.processLibraryStateFile),
160
+ path.resolve(path.join(workspaceRoot, '.a5c', 'active', 'process-library.json')),
161
+ );
162
+ assert.strictEqual(path.resolve(profileJson.installedSkillRoot), path.resolve(installedSkillRoot));
163
+ assert.ok(!('processLibraryRoot' in profileJson), 'team profile should not pin an active process-library root');
164
+ assert.ok(!('rulesLayer' in profileJson), 'team profile should not emit a missing rules layer path');
165
+ assert.ok(String(profileJson.processLibraryLookupCommand || '').includes('process-library:active'));
166
+ assert.strictEqual(
167
+ path.resolve(profileJson.workspaceHooksConfigPath),
168
+ path.resolve(path.join(workspaceRoot, '.codex', 'hooks.json')),
169
+ );
170
+ assert.strictEqual(
171
+ path.resolve(profileJson.workspaceConfigPath),
172
+ path.resolve(path.join(workspaceRoot, '.codex', 'config.toml')),
173
+ );
174
+ assert.strictEqual(
175
+ path.resolve(profileJson.hookScriptsRoot),
176
+ path.resolve(path.join(installedSkillRoot, '.codex', 'hooks')),
177
+ );
178
+
179
+ console.log(' ok packed install includes the portable skill payload and team-install paths');
180
+ console.log('\nPackaged install tests passed!');
181
+ } catch (err) {
182
+ console.error('\nTest failed:', err.message);
183
+ process.exitCode = 1;
184
+ } finally {
185
+ if (packedTgzPath && fs.existsSync(packedTgzPath)) {
186
+ fs.rmSync(packedTgzPath, { force: true });
187
+ }
188
+ if (tmpRoot) {
189
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
190
+ }
191
+ }