@a5c-ai/babysitter-codex 5.0.1-staging.b1d7d32a → 5.0.1-staging.b3bfcc2487ad

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 (38) hide show
  1. package/.codex-plugin/plugin.json +14 -12
  2. package/README.md +21 -8
  3. package/bin/cli.js +8 -16
  4. package/bin/install-shared.js +335 -52
  5. package/bin/install.js +12 -29
  6. package/bin/uninstall.js +11 -27
  7. package/hooks/babysitter-proxied-post-tool-use.sh +3 -0
  8. package/hooks/babysitter-proxied-pre-tool-use.sh +3 -0
  9. package/hooks/babysitter-proxied-session-end.sh +3 -0
  10. package/hooks/babysitter-proxied-session-start.sh +7 -162
  11. package/hooks/babysitter-proxied-stop.sh +3 -0
  12. package/hooks/babysitter-proxied-user-prompt-submit.sh +2 -88
  13. package/hooks.json +37 -4
  14. package/package.json +10 -14
  15. package/scripts/create-release-tag.mjs +18 -0
  16. package/scripts/publish-from-tag.mjs +41 -0
  17. package/scripts/team-install.js +14 -78
  18. package/skills/call/SKILL.md +5 -1
  19. package/skills/cleanup/SKILL.md +43 -0
  20. package/skills/contrib/SKILL.md +34 -0
  21. package/skills/doctor/SKILL.md +7 -8
  22. package/skills/help/SKILL.md +3 -2
  23. package/skills/observe/SKILL.md +1 -1
  24. package/skills/plugins/SKILL.md +257 -0
  25. package/skills/project-install/SKILL.md +3 -3
  26. package/skills/resume/SKILL.md +1 -1
  27. package/skills/retrospect/SKILL.md +48 -48
  28. package/skills/user-install/SKILL.md +3 -3
  29. package/skills/yolo/SKILL.md +5 -1
  30. package/hooks/babysitter-proxied-stop-hook.sh +0 -103
  31. package/hooks/babysitter-session-start.sh +0 -45
  32. package/hooks/babysitter-stop-hook.sh +0 -45
  33. package/hooks/proxied-hooks.json +0 -38
  34. package/hooks/user-prompt-submit.sh +0 -34
  35. package/scripts/sync-command-skills.js +0 -45
  36. package/skills/issue/SKILL.md +0 -16
  37. package/skills/model/SKILL.md +0 -15
  38. package/skills/team-install/SKILL.md +0 -15
@@ -1,21 +1,23 @@
1
1
  {
2
2
  "name": "babysitter",
3
- "version": "0.1.5",
4
- "description": "Babysitter orchestration plugin for Codex with skill entrypoints and lifecycle hooks.",
3
+ "version": "5.0.1-staging.b3bfcc2487ad",
4
+ "description": "Orchestrate complex, multi-step workflows with event-sourced state management, hook-based extensibility, and human-in-the-loop approval",
5
5
  "author": {
6
6
  "name": "a5c.ai",
7
- "email": "support@a5c.ai",
8
- "url": "https://github.com/a5c-ai/babysitter"
7
+ "email": "support@a5c.ai"
9
8
  },
10
- "homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-codex#readme",
11
- "repository": "https://github.com/a5c-ai/babysitter",
12
9
  "license": "MIT",
10
+ "repository": "https://github.com/a5c-ai/babysitter",
13
11
  "keywords": [
14
- "babysitter",
15
- "codex",
16
12
  "orchestration",
13
+ "workflow",
14
+ "automation",
15
+ "event-sourced",
17
16
  "hooks",
18
- "skills"
17
+ "TDD",
18
+ "quality-convergence",
19
+ "agent",
20
+ "LLM"
19
21
  ],
20
22
  "skills": "./skills/",
21
23
  "hooks": "./hooks.json",
@@ -41,7 +43,7 @@
41
43
  ],
42
44
  "brandColor": "#0F766E",
43
45
  "composerIcon": "./assets/icon.svg",
44
- "logo": "./assets/logo.png",
45
- "screenshots": []
46
- }
46
+ "logo": "./assets/logo.png"
47
+ },
48
+ "homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-unified/per-harness/codex#readme"
47
49
  }
package/README.md CHANGED
@@ -17,23 +17,36 @@ workspace-local Codex surface for team setup.
17
17
 
18
18
  ## Installation
19
19
 
20
- Install the SDK CLI first:
20
+ Install the Babysitter CLI once:
21
21
 
22
22
  ```bash
23
- npm install -g @a5c-ai/babysitter-sdk
23
+ npm install -g @a5c-ai/babysitter
24
24
  ```
25
25
 
26
- clone the repo and install the plugin globally:
26
+ Install the Codex plugin through the SDK helper. This is the canonical path used by the installer tests and resolves to `npx --yes @a5c-ai/babysitter-codex install ...` under the hood:
27
27
 
28
28
  ```bash
29
- npx -y @a5c-ai/babysitter-codex install --global
29
+ # Global install
30
+ babysitter harness:install-plugin codex
30
31
 
31
- codex
32
+ # Workspace install
33
+ babysitter harness:install-plugin codex --workspace /path/to/repo
34
+ ```
35
+
36
+ You can also run the published package installer directly:
37
+
38
+ ```bash
39
+ npx --yes @a5c-ai/babysitter-codex install --global
40
+ npx --yes @a5c-ai/babysitter-codex install --workspace /path/to/repo
41
+ ```
42
+
43
+ Then open Codex and finish enabling the plugin from the plugin UI:
32
44
 
33
- > /plugins
45
+ ```text
46
+ /plugins
34
47
  ```
35
48
 
36
- then navigate to the 'babysitter' entry and select 'Install'.
49
+ Navigate to the `babysitter` entry and select `Install`.
37
50
 
38
51
  If Codex was already open when you ran `install --global`, start a new thread
39
52
  after installing from `/plugins` before expecting `babysitter:*` skills such as
@@ -74,7 +87,7 @@ Verify the installed plugin bundle:
74
87
  npm ls -g @a5c-ai/babysitter-codex --depth=0
75
88
  test -f ~/.agents/plugins/babysitter/.codex-plugin/plugin.json
76
89
  test -f ~/.agents/plugins/babysitter/hooks.json
77
- test -f ~/.agents/plugins/babysitter/hooks/babysitter-stop-hook.sh
90
+ test -f ~/.agents/plugins/babysitter/hooks/babysitter-proxied-stop.sh
78
91
  test -f ~/.agents/plugins/babysitter/skills/babysit/SKILL.md
79
92
  test -f ~/.agents/plugins/marketplace.json
80
93
  ```
package/bin/cli.js CHANGED
@@ -5,6 +5,8 @@ const path = require('path');
5
5
  const { spawnSync } = require('child_process');
6
6
 
7
7
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
8
+ let shared;
9
+ try { shared = require('./install-shared'); } catch {}
8
10
 
9
11
  function printUsage() {
10
12
  console.error([
@@ -23,16 +25,10 @@ function parseInstallArgs(argv) {
23
25
  for (let i = 0; i < argv.length; i += 1) {
24
26
  const arg = argv[i];
25
27
  if (arg === '--global') {
26
- if (scope === 'workspace') {
27
- throw new Error('install accepts either --global or --workspace, not both');
28
- }
29
28
  scope = 'global';
30
29
  continue;
31
30
  }
32
31
  if (arg === '--workspace') {
33
- if (scope === 'global' && workspace !== null) {
34
- throw new Error('install accepts either --global or --workspace, not both');
35
- }
36
32
  scope = 'workspace';
37
33
  const next = argv[i + 1];
38
34
  if (next && !next.startsWith('-')) {
@@ -46,21 +42,14 @@ function parseInstallArgs(argv) {
46
42
  passthrough.push(arg);
47
43
  }
48
44
 
49
- return {
50
- scope,
51
- workspace,
52
- passthrough,
53
- };
45
+ return { scope, workspace, passthrough };
54
46
  }
55
47
 
56
48
  function runNodeScript(scriptPath, args, extraEnv = {}) {
57
49
  const result = spawnSync(process.execPath, [scriptPath, ...args], {
58
50
  cwd: process.cwd(),
59
51
  stdio: 'inherit',
60
- env: {
61
- ...process.env,
62
- ...extraEnv,
63
- },
52
+ env: { ...process.env, ...extraEnv },
64
53
  });
65
54
  process.exitCode = result.status ?? 1;
66
55
  }
@@ -74,6 +63,9 @@ function main() {
74
63
  }
75
64
 
76
65
  if (command === 'install') {
66
+ if (shared && typeof shared.harnessCliRoute === 'function' && shared.harnessCliRoute(rest, PACKAGE_ROOT, runNodeScript)) {
67
+ return;
68
+ }
77
69
  const parsed = parseInstallArgs(rest);
78
70
  if (parsed.scope === 'workspace') {
79
71
  const args = [];
@@ -84,7 +76,7 @@ function main() {
84
76
  runNodeScript(
85
77
  path.join(PACKAGE_ROOT, 'scripts', 'team-install.js'),
86
78
  args,
87
- { BABYSITTER_PACKAGE_ROOT: PACKAGE_ROOT },
79
+ { PLUGIN_PACKAGE_ROOT: PACKAGE_ROOT },
88
80
  );
89
81
  return;
90
82
  }
@@ -5,8 +5,195 @@ const os = require('os');
5
5
  const path = require('path');
6
6
  const { spawnSync } = require('child_process');
7
7
 
8
- const PLUGIN_NAME = 'babysitter';
8
+ const PLUGIN_NAME = "babysitter";
9
9
  const PLUGIN_CATEGORY = 'Coding';
10
+
11
+ function getUserHome() {
12
+ return os.homedir();
13
+ }
14
+
15
+ function getHarnessHome() {
16
+ return path.join(os.homedir(), ".codex");
17
+ }
18
+
19
+ function getHomePluginRoot(scope) {
20
+ if (scope === 'workspace') return path.join(process.cwd(), '.a5c', 'plugins', PLUGIN_NAME);
21
+ return path.join(path.join(os.homedir(), ".agents/plugins"), PLUGIN_NAME);
22
+ }
23
+
24
+ function getHomeMarketplacePath() {
25
+ return path.join(os.homedir(), ".agents/plugins/marketplace.json");
26
+ }
27
+
28
+ function writeFileIfChanged(filePath, contents) {
29
+ try {
30
+ const existing = fs.readFileSync(filePath, 'utf8');
31
+ if (existing === contents) return false;
32
+ } catch {}
33
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
34
+ fs.writeFileSync(filePath, contents);
35
+ return true;
36
+ }
37
+
38
+ function copyRecursive(src, dest) {
39
+ fs.mkdirSync(dest, { recursive: true });
40
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
41
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
42
+ const s = path.join(src, entry.name);
43
+ const d = path.join(dest, entry.name);
44
+ if (entry.isDirectory()) {
45
+ copyRecursive(s, d);
46
+ } else {
47
+ fs.copyFileSync(s, d);
48
+ }
49
+ }
50
+ }
51
+
52
+ function copyPluginBundle(packageRoot, pluginRoot) {
53
+ const bundleEntries = fs.readdirSync(packageRoot).filter(
54
+ e => !['node_modules', '.git', 'test', 'dist'].includes(e)
55
+ );
56
+ fs.mkdirSync(pluginRoot, { recursive: true });
57
+ for (const entry of bundleEntries) {
58
+ const src = path.join(packageRoot, entry);
59
+ const dest = path.join(pluginRoot, entry);
60
+ const stat = fs.statSync(src);
61
+ if (stat.isDirectory()) {
62
+ copyRecursive(src, dest);
63
+ } else {
64
+ fs.copyFileSync(src, dest);
65
+ }
66
+ }
67
+ }
68
+
69
+ function readJson(filePath) {
70
+ try {
71
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+
77
+ function writeJson(filePath, value) {
78
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
79
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n');
80
+ }
81
+
82
+ function ensureExecutable(filePath) {
83
+ try {
84
+ fs.chmodSync(filePath, 0o755);
85
+ } catch {}
86
+ }
87
+
88
+ function normalizeMarketplaceSourcePath(source, marketplacePath) {
89
+ if (typeof source === 'string') {
90
+ return path.relative(path.dirname(marketplacePath), source).replace(/\\/g, '/');
91
+ }
92
+ return source;
93
+ }
94
+
95
+ function ensureMarketplaceEntry(marketplacePath, pluginRoot) {
96
+ let marketplace = readJson(marketplacePath) || {
97
+ name: "a5c.ai",
98
+ plugins: [],
99
+ };
100
+ if (!Array.isArray(marketplace.plugins)) marketplace.plugins = [];
101
+ const idx = marketplace.plugins.findIndex(p => p.name === PLUGIN_NAME);
102
+ const relSource = './' + normalizeMarketplaceSourcePath(pluginRoot, marketplacePath);
103
+ const entry = {
104
+ name: PLUGIN_NAME,
105
+ source: relSource,
106
+ description: "Orchestrate complex, multi-step workflows with event-sourced state management, hook-based extensibility, and human-in-the-loop approval",
107
+ version: "5.0.1-staging.b3bfcc2487ad",
108
+ author: { name: "a5c.ai" },
109
+ };
110
+ if (idx >= 0) marketplace.plugins[idx] = entry;
111
+ else marketplace.plugins.push(entry);
112
+ writeJson(marketplacePath, marketplace);
113
+ }
114
+
115
+ function removeMarketplaceEntry(marketplacePath) {
116
+ const marketplace = readJson(marketplacePath);
117
+ if (!marketplace || !Array.isArray(marketplace.plugins)) return;
118
+ marketplace.plugins = marketplace.plugins.filter(p => p.name !== PLUGIN_NAME);
119
+ writeJson(marketplacePath, marketplace);
120
+ }
121
+
122
+ function warnWindowsHooks() {
123
+ if (process.platform === 'win32') {
124
+ console.warn('[' + PLUGIN_NAME + '] Windows detected — shell hooks (.sh) require Git Bash or WSL.');
125
+ }
126
+ }
127
+
128
+ function runPostInstall(pluginRoot) {
129
+ const postInstall = path.join(pluginRoot, 'scripts', 'post-install.js');
130
+ if (fs.existsSync(postInstall)) {
131
+ spawnSync(process.execPath, [postInstall], {
132
+ cwd: pluginRoot, stdio: 'inherit',
133
+ env: { ...process.env, PLUGIN_ROOT: pluginRoot },
134
+ });
135
+ }
136
+ }
137
+
138
+ function getGlobalStateDir() {
139
+ return process.env.BABYSITTER_GLOBAL_STATE_DIR || path.join(getUserHome(), '.a5c');
140
+ }
141
+
142
+ function resolveCliCommand(packageRoot) {
143
+ try {
144
+ const result = spawnSync('babysitter', ['--version'], { stdio: 'pipe', timeout: 10000 });
145
+ if (result.status === 0) return 'babysitter';
146
+ } catch {}
147
+ const versionsPath = path.join(packageRoot, 'versions.json');
148
+ const versions = readJson(versionsPath) || {};
149
+ const ver = versions.sdkVersion || 'latest';
150
+ return `npx -y @a5c-ai/babysitter-sdk@${ver}`;
151
+ }
152
+
153
+ function runCli(packageRoot, cliArgs, options = {}) {
154
+ const cmd = resolveCliCommand(packageRoot);
155
+ const parts = cmd.split(' ');
156
+ const result = spawnSync(parts[0], [...parts.slice(1), ...cliArgs], {
157
+ stdio: options.stdio || 'inherit',
158
+ timeout: options.timeout || 120000,
159
+ cwd: options.cwd || process.cwd(),
160
+ env: { ...process.env, ...options.env },
161
+ });
162
+ return result;
163
+ }
164
+
165
+ function ensureGlobalProcessLibrary(packageRoot) {
166
+ const stateDir = getGlobalStateDir();
167
+ const activeFile = path.join(stateDir, 'active', 'process-library.json');
168
+ let active = readJson(activeFile);
169
+ if (active && active.binding && active.binding.dir) {
170
+ return active;
171
+ }
172
+ const defaultSpec = readJson(path.join(stateDir, 'process-library-defaults.json'));
173
+ const cloneDir = defaultSpec && defaultSpec.cloneDir
174
+ ? defaultSpec.cloneDir
175
+ : path.join(stateDir, 'process-library', PLUGIN_NAME + '-repo');
176
+ runCli(packageRoot, [
177
+ 'process-library:clone',
178
+ '--dir', cloneDir,
179
+ '--state-dir', stateDir,
180
+ '--json',
181
+ ], { stdio: 'pipe' });
182
+ runCli(packageRoot, [
183
+ 'process-library:use',
184
+ '--dir', cloneDir,
185
+ '--state-dir', stateDir,
186
+ '--json',
187
+ ], { stdio: 'pipe' });
188
+ active = readJson(activeFile);
189
+ return {
190
+ binding: active && active.binding ? active.binding : { dir: cloneDir },
191
+ defaultSpec: defaultSpec || { cloneDir },
192
+ stateFile: activeFile,
193
+ };
194
+ }
195
+
196
+
10
197
  const LEGACY_MARKETPLACE_PLUGIN_NAMES = ['babysitter-codex'];
11
198
  const LEGACY_SKILL_NAMES = [
12
199
  'babysit',
@@ -50,6 +237,11 @@ const LEGACY_HOOK_SCRIPT_NAMES = [
50
237
  'babysitter-stop-hook.sh',
51
238
  'user-prompt-submit.sh',
52
239
  ];
240
+ const MANAGED_HOOK_SCRIPT_NAMES = [
241
+ 'babysitter-proxied-session-start.sh',
242
+ 'babysitter-proxied-stop.sh',
243
+ 'babysitter-proxied-user-prompt-submit.sh',
244
+ ];
53
245
  const DEFAULT_MARKETPLACE = {
54
246
  name: 'local-plugins',
55
247
  interface: {
@@ -73,19 +265,6 @@ function getCodexHome() {
73
265
  return path.join(os.homedir(), '.codex');
74
266
  }
75
267
 
76
- function getUserHome() {
77
- if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
78
- if (process.env.HOME) return path.resolve(process.env.HOME);
79
- return os.homedir();
80
- }
81
-
82
- function getGlobalStateDir() {
83
- if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
84
- return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
85
- }
86
- return path.join(getUserHome(), '.a5c');
87
- }
88
-
89
268
  function getHomePluginRoot() {
90
269
  if (process.env.BABYSITTER_CODEX_PLUGIN_DIR) {
91
270
  return path.resolve(process.env.BABYSITTER_CODEX_PLUGIN_DIR, PLUGIN_NAME);
@@ -100,6 +279,14 @@ function getHomeMarketplacePath() {
100
279
  return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
101
280
  }
102
281
 
282
+ function getWorkspacePluginRoot(workspaceRoot) {
283
+ return path.join(path.resolve(workspaceRoot), '.agents', 'plugins', PLUGIN_NAME);
284
+ }
285
+
286
+ function getWorkspaceMarketplacePath(workspaceRoot) {
287
+ return path.join(path.resolve(workspaceRoot), '.agents', 'plugins', 'marketplace.json');
288
+ }
289
+
103
290
  function renderCodexConfigToml() {
104
291
  return [
105
292
  'approval_policy = "on-request"',
@@ -110,7 +297,7 @@ function renderCodexConfigToml() {
110
297
  'writable_roots = [".a5c", ".codex"]',
111
298
  '',
112
299
  '[features]',
113
- 'codex_hooks = true',
300
+ 'hooks = true',
114
301
  'multi_agent = true',
115
302
  '',
116
303
  '[agents]',
@@ -120,18 +307,6 @@ function renderCodexConfigToml() {
120
307
  ].join('\n');
121
308
  }
122
309
 
123
- function writeFileIfChanged(filePath, contents) {
124
- if (fs.existsSync(filePath)) {
125
- const current = fs.readFileSync(filePath, 'utf8');
126
- if (current === contents) {
127
- return false;
128
- }
129
- }
130
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
131
- fs.writeFileSync(filePath, contents, 'utf8');
132
- return true;
133
- }
134
-
135
310
  function copyRecursive(src, dest) {
136
311
  const stat = fs.statSync(src);
137
312
  if (stat.isDirectory()) {
@@ -231,7 +406,7 @@ function mergeCodexConfig(existing) {
231
406
  content = insertRootKey(content, 'sandbox_mode', 'sandbox_mode = "workspace-write"');
232
407
  content = insertRootKey(content, 'project_doc_max_bytes', 'project_doc_max_bytes = 65536');
233
408
  content = ensureWritableRoots(content);
234
- content = ensureSectionLine(content, 'features', 'codex_hooks', 'codex_hooks = true');
409
+ content = ensureSectionLine(content, 'features', 'hooks', 'hooks = true');
235
410
  content = ensureSectionLine(content, 'features', 'multi_agent', 'multi_agent = true');
236
411
  content = ensureSectionLine(content, 'agents', 'max_depth', 'max_depth = 3');
237
412
  content = ensureSectionLine(content, 'agents', 'max_threads', 'max_threads = 4');
@@ -292,29 +467,60 @@ function runBabysitterCli(packageRoot, cliArgs, options = {}) {
292
467
  }
293
468
 
294
469
  function ensureGlobalProcessLibrary(packageRoot) {
295
- return JSON.parse(
470
+ const stateDir = getGlobalStateDir();
471
+ const activeFile = path.join(stateDir, 'active', 'process-library.json');
472
+ const current = readJson(activeFile);
473
+ if (current && current.defaultBinding && current.defaultBinding.dir) {
474
+ return {
475
+ stateFile: activeFile,
476
+ binding: current.defaultBinding,
477
+ defaultSpec: {
478
+ stateDir,
479
+ repo: current.defaultBinding.repoUrl,
480
+ cloneDir: current.defaultBinding.dir,
481
+ },
482
+ };
483
+ }
484
+
485
+ const cloneDir = path.join(stateDir, 'process-library', `${PLUGIN_NAME}-repo`);
486
+ runBabysitterCli(
487
+ packageRoot,
488
+ [
489
+ 'process-library:clone',
490
+ '--dir', cloneDir,
491
+ '--state-dir', stateDir,
492
+ '--json',
493
+ ],
494
+ { cwd: packageRoot },
495
+ );
496
+ runBabysitterCli(
497
+ packageRoot,
498
+ [
499
+ 'process-library:use',
500
+ '--dir', cloneDir,
501
+ '--state-dir', stateDir,
502
+ '--json',
503
+ ],
504
+ { cwd: packageRoot },
505
+ );
506
+
507
+ const active = JSON.parse(
296
508
  runBabysitterCli(
297
509
  packageRoot,
298
- ['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
510
+ ['process-library:active', '--state-dir', stateDir, '--json'],
299
511
  { cwd: packageRoot },
300
512
  ),
301
513
  );
302
- }
303
-
304
- function readJson(filePath) {
305
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
306
- }
307
-
308
- function writeJson(filePath, value) {
309
- writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
310
- }
311
514
 
312
- function ensureExecutable(filePath) {
313
- try {
314
- fs.chmodSync(filePath, 0o755);
315
- } catch {
316
- // Best-effort only. Windows and some filesystems may ignore mode changes.
317
- }
515
+ return {
516
+ stateFile: active.stateFile || activeFile,
517
+ binding: active.binding || active.defaultBinding || { dir: cloneDir },
518
+ defaultSpec: active.defaultSpec || {
519
+ stateDir,
520
+ repo: process.env.BABYSITTER_PROCESS_LIBRARY_REPO || null,
521
+ cloneDir,
522
+ },
523
+ };
318
524
  }
319
525
 
320
526
  function getMarketplaceRootDir(marketplacePath) {
@@ -483,7 +689,7 @@ function installManagedHooks(packageRoot, codexHome) {
483
689
  const targetRoot = path.join(codexHome, 'hooks');
484
690
  fs.mkdirSync(targetRoot, { recursive: true });
485
691
 
486
- for (const scriptName of LEGACY_HOOK_SCRIPT_NAMES) {
692
+ for (const scriptName of MANAGED_HOOK_SCRIPT_NAMES) {
487
693
  const sourcePath = path.join(sourceRoot, scriptName);
488
694
  const targetPath = path.join(targetRoot, scriptName);
489
695
  copyRecursive(sourcePath, targetPath);
@@ -499,6 +705,46 @@ function installCodexSurface(packageRoot, codexHome) {
499
705
  installManagedHooks(packageRoot, codexHome);
500
706
  }
501
707
 
708
+ function harnessTeamInstall(packageRoot, pluginRoot, workspace) {
709
+ var workspaceRoot = path.resolve(workspace);
710
+ var resolvedPluginRoot = pluginRoot ? path.resolve(pluginRoot) : getWorkspacePluginRoot(workspaceRoot);
711
+ var marketplacePath = getWorkspaceMarketplacePath(workspaceRoot);
712
+ var codexHome = path.join(workspaceRoot, '.codex');
713
+ var codexConfigPath = path.join(codexHome, 'config.toml');
714
+ var teamDir = path.join(workspaceRoot, '.a5c', 'team');
715
+
716
+ var processLibraryState = ensureGlobalProcessLibrary(packageRoot);
717
+ ensureMarketplaceEntry(marketplacePath, resolvedPluginRoot);
718
+ mergeCodexConfigFile(codexConfigPath);
719
+ installCodexSurface(packageRoot, codexHome);
720
+ warnWindowsHooks();
721
+
722
+ writeJson(path.join(teamDir, 'install.json'), {
723
+ packageRoot: packageRoot,
724
+ workspaceRoot: workspaceRoot,
725
+ pluginRoot: resolvedPluginRoot,
726
+ marketplacePath: marketplacePath,
727
+ codexConfigPath: codexConfigPath,
728
+ processLibraryCloneDir: (processLibraryState.defaultSpec && processLibraryState.defaultSpec.cloneDir)
729
+ || (processLibraryState.binding && processLibraryState.binding.dir),
730
+ processLibraryStateFile: processLibraryState.stateFile
731
+ || path.join(getGlobalStateDir(), 'active', 'process-library.json'),
732
+ });
733
+ writeJson(path.join(teamDir, 'profile.json'), {
734
+ pluginRoot: resolvedPluginRoot,
735
+ marketplacePath: marketplacePath,
736
+ codexConfigPath: codexConfigPath,
737
+ processLibraryLookupCommand: 'babysitter process-library:active --json',
738
+ });
739
+ }
740
+
741
+ function harnessInstall(packageRoot, _pluginRoot) {
742
+ const codexConfigPath = path.join(getCodexHome(), 'config.toml');
743
+ mergeCodexConfigFile(codexConfigPath);
744
+ ensureGlobalProcessLibrary(packageRoot);
745
+ warnWindowsHooks();
746
+ }
747
+
502
748
  function warnWindowsHooks() {
503
749
  if (process.platform !== 'win32') {
504
750
  return;
@@ -510,17 +756,54 @@ function warnWindowsHooks() {
510
756
  console.warn('[babysitter] If hooks do not fire, run `codex --version` and upgrade if you are below 0.119.0.');
511
757
  }
512
758
 
759
+
513
760
  module.exports = {
514
- copyPluginBundle,
761
+ PLUGIN_NAME,
762
+ PLUGIN_CATEGORY,
763
+ getUserHome,
764
+ getHarnessHome,
765
+ writeFileIfChanged,
766
+ readJson,
767
+ writeJson,
768
+ ensureExecutable,
769
+ runPostInstall,
770
+ getGlobalStateDir,
771
+ resolveCliCommand,
772
+ runCli,
515
773
  ensureGlobalProcessLibrary,
774
+ PLUGIN_BUNDLE_ENTRIES,
775
+ copyRecursive,
776
+ copyPluginBundle,
777
+ DEFAULT_MARKETPLACE,
778
+ normalizeMarketplaceSourcePath,
516
779
  ensureMarketplaceEntry,
780
+ removeMarketplaceEntry,
781
+ installManagedSkills,
782
+ mergeManagedHooksConfig,
783
+ installManagedHooks,
784
+ warnWindowsHooks,
785
+ LEGACY_MARKETPLACE_PLUGIN_NAMES,
786
+ LEGACY_SKILL_NAMES,
787
+ LEGACY_PROMPT_NAMES,
788
+ LEGACY_HOOK_SCRIPT_NAMES,
789
+ MANAGED_HOOK_SCRIPT_NAMES,
517
790
  getCodexHome,
518
- getHomeMarketplacePath,
519
791
  getHomePluginRoot,
520
- installCodexSurface,
792
+ getHomeMarketplacePath,
793
+ getWorkspacePluginRoot,
794
+ getWorkspaceMarketplacePath,
795
+ renderCodexConfigToml,
796
+ insertRootKey,
797
+ ensureSectionLine,
798
+ ensureWritableRoots,
799
+ mergeCodexConfig,
521
800
  mergeCodexConfigFile,
801
+ resolveBabysitterCommand,
802
+ runBabysitterCli,
803
+ ensureGlobalProcessLibrary,
804
+ getMarketplaceRootDir,
522
805
  removeLegacyCodexSurface,
523
- removeMarketplaceEntry,
524
- warnWindowsHooks,
525
- writeJson,
806
+ installCodexSurface,
807
+ harnessInstall,
808
+ harnessTeamInstall,
526
809
  };
package/bin/install.js CHANGED
@@ -2,44 +2,27 @@
2
2
  'use strict';
3
3
 
4
4
  const path = require('path');
5
- const {
6
- copyPluginBundle,
7
- ensureGlobalProcessLibrary,
8
- ensureMarketplaceEntry,
9
- getCodexHome,
10
- getHomeMarketplacePath,
11
- getHomePluginRoot,
12
- mergeCodexConfigFile,
13
- warnWindowsHooks,
14
- } = require('./install-shared');
5
+ const shared = require('./install-shared');
15
6
 
16
7
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
17
8
 
18
9
  function main() {
19
- const codexHome = getCodexHome();
20
- const pluginRoot = getHomePluginRoot();
21
- const marketplacePath = getHomeMarketplacePath();
10
+ const pluginRoot = shared.getHomePluginRoot();
11
+ const marketplacePath = shared.getHomeMarketplacePath();
22
12
 
23
- console.log(`[babysitter] Installing plugin to ${pluginRoot}`);
13
+ console.log(`[${shared.PLUGIN_NAME}] Installing plugin to ${pluginRoot}`);
24
14
 
25
15
  try {
26
- copyPluginBundle(PACKAGE_ROOT, pluginRoot);
27
- ensureMarketplaceEntry(marketplacePath, pluginRoot);
28
- mergeCodexConfigFile(path.join(codexHome, 'config.toml'));
29
-
30
- const active = ensureGlobalProcessLibrary(PACKAGE_ROOT);
31
- console.log(`[babysitter] marketplace: ${marketplacePath}`);
32
- console.log(`[babysitter] process library: ${active.binding?.dir}`);
33
- if (active.defaultSpec?.cloneDir) {
34
- console.log(`[babysitter] process library clone: ${active.defaultSpec.cloneDir}`);
16
+ shared.copyPluginBundle(PACKAGE_ROOT, pluginRoot);
17
+ shared.ensureMarketplaceEntry(marketplacePath, pluginRoot);
18
+ if (typeof shared.harnessInstall === 'function') {
19
+ shared.harnessInstall(PACKAGE_ROOT, pluginRoot);
35
20
  }
36
- console.log(`[babysitter] process library state: ${active.stateFile}`);
37
- warnWindowsHooks();
38
- console.log('[babysitter] Installation complete!');
39
- console.log('[babysitter] Restart Codex to pick up the installed plugin and config changes, then run Codex and install Babysitter from `/plugins`.');
40
- console.log('[babysitter] If Codex was already open, start a new thread after install before expecting `babysitter:*` skills such as `$babysitter:babysit` or `$babysitter:call` to appear.');
21
+ shared.runPostInstall && shared.runPostInstall(pluginRoot);
22
+ console.log(`[${shared.PLUGIN_NAME}] Installation complete!`);
23
+ console.log(`[${shared.PLUGIN_NAME}] Restart your IDE/CLI to pick up the plugin.`);
41
24
  } catch (err) {
42
- console.error(`[babysitter] Failed to install plugin: ${err.message}`);
25
+ console.error(`[${shared.PLUGIN_NAME}] Failed to install: ${err.message}`);
43
26
  process.exitCode = 1;
44
27
  }
45
28
  }