@a5c-ai/babysitter-codex 0.1.11-staging.fd3ab4c2 → 5.0.0

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.
@@ -5,8 +5,196 @@ 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.0",
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
+
197
+ const LEGACY_MARKETPLACE_PLUGIN_NAMES = ['babysitter-codex'];
10
198
  const LEGACY_SKILL_NAMES = [
11
199
  'babysit',
12
200
  'babysitter-codex',
@@ -49,6 +237,11 @@ const LEGACY_HOOK_SCRIPT_NAMES = [
49
237
  'babysitter-stop-hook.sh',
50
238
  'user-prompt-submit.sh',
51
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
+ ];
52
245
  const DEFAULT_MARKETPLACE = {
53
246
  name: 'local-plugins',
54
247
  interface: {
@@ -58,12 +251,13 @@ const DEFAULT_MARKETPLACE = {
58
251
  };
59
252
  const PLUGIN_BUNDLE_ENTRIES = [
60
253
  '.codex-plugin',
61
- '.app.json',
62
254
  'assets',
63
255
  'hooks',
64
256
  'hooks.json',
65
257
  'skills',
66
- 'babysitter.lock.json',
258
+ '.app.json',
259
+ 'plugin.lock.json',
260
+ 'README.md',
67
261
  ];
68
262
 
69
263
  function getCodexHome() {
@@ -71,24 +265,11 @@ function getCodexHome() {
71
265
  return path.join(os.homedir(), '.codex');
72
266
  }
73
267
 
74
- function getUserHome() {
75
- if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
76
- if (process.env.HOME) return path.resolve(process.env.HOME);
77
- return os.homedir();
78
- }
79
-
80
- function getGlobalStateDir() {
81
- if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
82
- return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
83
- }
84
- return path.join(getUserHome(), '.a5c');
85
- }
86
-
87
268
  function getHomePluginRoot() {
88
269
  if (process.env.BABYSITTER_CODEX_PLUGIN_DIR) {
89
270
  return path.resolve(process.env.BABYSITTER_CODEX_PLUGIN_DIR, PLUGIN_NAME);
90
271
  }
91
- return path.join(getCodexHome(), 'plugins', PLUGIN_NAME);
272
+ return path.join(getUserHome(), '.agents', 'plugins', PLUGIN_NAME);
92
273
  }
93
274
 
94
275
  function getHomeMarketplacePath() {
@@ -98,6 +279,14 @@ function getHomeMarketplacePath() {
98
279
  return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
99
280
  }
100
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
+
101
290
  function renderCodexConfigToml() {
102
291
  return [
103
292
  'approval_policy = "on-request"',
@@ -118,18 +307,6 @@ function renderCodexConfigToml() {
118
307
  ].join('\n');
119
308
  }
120
309
 
121
- function writeFileIfChanged(filePath, contents) {
122
- if (fs.existsSync(filePath)) {
123
- const current = fs.readFileSync(filePath, 'utf8');
124
- if (current === contents) {
125
- return false;
126
- }
127
- }
128
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
129
- fs.writeFileSync(filePath, contents, 'utf8');
130
- return true;
131
- }
132
-
133
310
  function copyRecursive(src, dest) {
134
311
  const stat = fs.statSync(src);
135
312
  if (stat.isDirectory()) {
@@ -290,38 +467,77 @@ function runBabysitterCli(packageRoot, cliArgs, options = {}) {
290
467
  }
291
468
 
292
469
  function ensureGlobalProcessLibrary(packageRoot) {
293
- 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(
294
508
  runBabysitterCli(
295
509
  packageRoot,
296
- ['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
510
+ ['process-library:active', '--state-dir', stateDir, '--json'],
297
511
  { cwd: packageRoot },
298
512
  ),
299
513
  );
300
- }
301
514
 
302
- function readJson(filePath) {
303
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
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
+ };
304
524
  }
305
525
 
306
- function writeJson(filePath, value) {
307
- writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
308
- }
309
-
310
- function ensureExecutable(filePath) {
311
- try {
312
- fs.chmodSync(filePath, 0o755);
313
- } catch {
314
- // Best-effort only. Windows and some filesystems may ignore mode changes.
315
- }
526
+ function getMarketplaceRootDir(marketplacePath) {
527
+ const pluginsDir = path.dirname(marketplacePath);
528
+ const dotAgentsDir = path.dirname(pluginsDir);
529
+ return path.dirname(dotAgentsDir);
316
530
  }
317
531
 
318
532
  function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
319
- let next = pluginSourcePath;
320
- if (path.isAbsolute(next)) {
321
- next = path.relative(path.dirname(marketplacePath), next);
322
- }
533
+ let next = path.relative(getMarketplaceRootDir(marketplacePath), pluginSourcePath);
323
534
  next = String(next || '').replace(/\\/g, '/');
324
- if (!next.startsWith('./') && !next.startsWith('../')) {
535
+ if (!next || next === '.' || next.startsWith('../')) {
536
+ throw new Error(
537
+ `Plugin source path must live under ${getMarketplaceRootDir(marketplacePath)} so Codex can load it via a ./-prefixed marketplace entry.`,
538
+ );
539
+ }
540
+ if (!next.startsWith('./')) {
325
541
  next = `./${next}`;
326
542
  }
327
543
  return next;
@@ -347,15 +563,15 @@ function ensureMarketplaceEntry(marketplacePath, pluginSourcePath) {
347
563
  },
348
564
  category: PLUGIN_CATEGORY,
349
565
  };
350
- const existingIndex = Array.isArray(marketplace.plugins)
351
- ? marketplace.plugins.findIndex((entry) => entry && entry.name === PLUGIN_NAME)
352
- : -1;
353
566
  if (!Array.isArray(marketplace.plugins)) {
354
567
  marketplace.plugins = [nextEntry];
355
- } else if (existingIndex >= 0) {
356
- marketplace.plugins[existingIndex] = nextEntry;
357
568
  } else {
358
- marketplace.plugins.push(nextEntry);
569
+ const sanitized = marketplace.plugins.filter((entry) => (
570
+ entry &&
571
+ entry.name !== PLUGIN_NAME &&
572
+ !LEGACY_MARKETPLACE_PLUGIN_NAMES.includes(entry.name)
573
+ ));
574
+ marketplace.plugins = [...sanitized, nextEntry];
359
575
  }
360
576
  writeJson(marketplacePath, marketplace);
361
577
  return nextEntry;
@@ -369,7 +585,11 @@ function removeMarketplaceEntry(marketplacePath) {
369
585
  if (!Array.isArray(marketplace.plugins)) {
370
586
  return;
371
587
  }
372
- marketplace.plugins = marketplace.plugins.filter((entry) => entry && entry.name !== PLUGIN_NAME);
588
+ marketplace.plugins = marketplace.plugins.filter((entry) => (
589
+ entry &&
590
+ entry.name !== PLUGIN_NAME &&
591
+ !LEGACY_MARKETPLACE_PLUGIN_NAMES.includes(entry.name)
592
+ ));
373
593
  writeJson(marketplacePath, marketplace);
374
594
  }
375
595
 
@@ -469,7 +689,7 @@ function installManagedHooks(packageRoot, codexHome) {
469
689
  const targetRoot = path.join(codexHome, 'hooks');
470
690
  fs.mkdirSync(targetRoot, { recursive: true });
471
691
 
472
- for (const scriptName of LEGACY_HOOK_SCRIPT_NAMES) {
692
+ for (const scriptName of MANAGED_HOOK_SCRIPT_NAMES) {
473
693
  const sourcePath = path.join(sourceRoot, scriptName);
474
694
  const targetPath = path.join(targetRoot, scriptName);
475
695
  copyRecursive(sourcePath, targetPath);
@@ -485,25 +705,105 @@ function installCodexSurface(packageRoot, codexHome) {
485
705
  installManagedHooks(packageRoot, codexHome);
486
706
  }
487
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
+
488
748
  function warnWindowsHooks() {
489
749
  if (process.platform !== 'win32') {
490
750
  return;
491
751
  }
492
- console.warn('[babysitter] Warning: Codex hooks are currently disabled on native Windows.');
493
- console.warn('[babysitter] The plugin will install correctly, but SessionStart/UserPromptSubmit/Stop hooks will not fire until Codex enables Windows hook execution.');
752
+ // Codex enabled Windows hooks in v0.119.0 (2026-04-10, openai/codex#17268).
753
+ // Older Codex CLIs still skip hook execution on Windows; warn so users on
754
+ // pinned/older versions know to upgrade.
755
+ console.warn('[babysitter] Note: Codex hooks on Windows require Codex CLI >= 0.119.0.');
756
+ console.warn('[babysitter] If hooks do not fire, run `codex --version` and upgrade if you are below 0.119.0.');
494
757
  }
495
758
 
759
+
496
760
  module.exports = {
497
- 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,
498
773
  ensureGlobalProcessLibrary,
774
+ PLUGIN_BUNDLE_ENTRIES,
775
+ copyRecursive,
776
+ copyPluginBundle,
777
+ DEFAULT_MARKETPLACE,
778
+ normalizeMarketplaceSourcePath,
499
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,
500
790
  getCodexHome,
501
- getHomeMarketplacePath,
502
791
  getHomePluginRoot,
503
- installCodexSurface,
792
+ getHomeMarketplacePath,
793
+ getWorkspacePluginRoot,
794
+ getWorkspaceMarketplacePath,
795
+ renderCodexConfigToml,
796
+ insertRootKey,
797
+ ensureSectionLine,
798
+ ensureWritableRoots,
799
+ mergeCodexConfig,
504
800
  mergeCodexConfigFile,
801
+ resolveBabysitterCommand,
802
+ runBabysitterCli,
803
+ ensureGlobalProcessLibrary,
804
+ getMarketplaceRootDir,
505
805
  removeLegacyCodexSurface,
506
- removeMarketplaceEntry,
507
- warnWindowsHooks,
508
- writeJson,
806
+ installCodexSurface,
807
+ harnessInstall,
808
+ harnessTeamInstall,
509
809
  };
package/bin/install.js CHANGED
@@ -2,45 +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
- installCodexSurface,
13
- mergeCodexConfigFile,
14
- warnWindowsHooks,
15
- } = require('./install-shared');
5
+ const shared = require('./install-shared');
16
6
 
17
7
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
18
8
 
19
9
  function main() {
20
- const codexHome = getCodexHome();
21
- const pluginRoot = getHomePluginRoot();
22
- const marketplacePath = getHomeMarketplacePath();
10
+ const pluginRoot = shared.getHomePluginRoot();
11
+ const marketplacePath = shared.getHomeMarketplacePath();
23
12
 
24
- console.log(`[babysitter] Installing plugin to ${pluginRoot}`);
13
+ console.log(`[${shared.PLUGIN_NAME}] Installing plugin to ${pluginRoot}`);
25
14
 
26
15
  try {
27
- copyPluginBundle(PACKAGE_ROOT, pluginRoot);
28
- ensureMarketplaceEntry(marketplacePath, pluginRoot);
29
- mergeCodexConfigFile(path.join(codexHome, 'config.toml'));
30
- installCodexSurface(PACKAGE_ROOT, codexHome);
31
-
32
- const active = ensureGlobalProcessLibrary(PACKAGE_ROOT);
33
- console.log(`[babysitter] marketplace: ${marketplacePath}`);
34
- console.log(`[babysitter] process library: ${active.binding?.dir}`);
35
- if (active.defaultSpec?.cloneDir) {
36
- 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);
37
20
  }
38
- console.log(`[babysitter] process library state: ${active.stateFile}`);
39
- warnWindowsHooks();
40
- console.log('[babysitter] Installation complete!');
41
- console.log('[babysitter] Restart Codex to pick up the installed plugin and config changes.');
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.`);
42
24
  } catch (err) {
43
- console.error(`[babysitter] Failed to install plugin: ${err.message}`);
25
+ console.error(`[${shared.PLUGIN_NAME}] Failed to install: ${err.message}`);
44
26
  process.exitCode = 1;
45
27
  }
46
28
  }
package/bin/uninstall.js CHANGED
@@ -2,39 +2,23 @@
2
2
  'use strict';
3
3
 
4
4
  const fs = require('fs');
5
- const {
6
- getCodexHome,
7
- getHomeMarketplacePath,
8
- getHomePluginRoot,
9
- removeLegacyCodexSurface,
10
- removeMarketplaceEntry,
11
- } = require('./install-shared');
5
+ const shared = require('./install-shared');
12
6
 
13
7
  function main() {
14
- const codexHome = getCodexHome();
15
- const pluginRoot = getHomePluginRoot();
16
- const marketplacePath = getHomeMarketplacePath();
17
- let removedPlugin = false;
8
+ const pluginRoot = shared.getHomePluginRoot();
18
9
 
19
- if (fs.existsSync(pluginRoot)) {
20
- try {
21
- fs.rmSync(pluginRoot, { recursive: true, force: true });
22
- console.log(`[babysitter] Removed ${pluginRoot}`);
23
- removedPlugin = true;
24
- } catch (err) {
25
- console.warn(`[babysitter] Warning: Could not remove plugin directory ${pluginRoot}: ${err.message}`);
26
- }
27
- }
28
-
29
- removeMarketplaceEntry(marketplacePath);
30
- removeLegacyCodexSurface(codexHome);
31
-
32
- if (!removedPlugin) {
33
- console.log('[babysitter] Plugin directory not found, legacy Codex surface cleaned if present.');
10
+ if (!fs.existsSync(pluginRoot)) {
11
+ console.log(`[${shared.PLUGIN_NAME}] Plugin not installed at ${pluginRoot}`);
34
12
  return;
35
13
  }
36
14
 
37
- console.log('[babysitter] Restart Codex to complete uninstallation.');
15
+ try {
16
+ fs.rmSync(pluginRoot, { recursive: true, force: true });
17
+ console.log(`[${shared.PLUGIN_NAME}] Uninstalled from ${pluginRoot}`);
18
+ } catch (err) {
19
+ console.error(`[${shared.PLUGIN_NAME}] Failed to uninstall: ${err.message}`);
20
+ process.exitCode = 1;
21
+ }
38
22
  }
39
23
 
40
24
  main();
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+ # Session Start — installs SDK if needed, then runs hook handler.
3
+ set -euo pipefail
4
+ PLUGIN_ROOT="${PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
5
+ SDK_VERSION=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync('${PLUGIN_ROOT}/versions.json','utf8')).sdkVersion||'latest')}catch{console.log('latest')}" 2>/dev/null || echo "latest")
6
+ if ! command -v babysitter &>/dev/null; then
7
+ npm i -g "@a5c-ai/babysitter-sdk@${SDK_VERSION}" --loglevel=error 2>/dev/null || \
8
+ npm i -g "@a5c-ai/babysitter-sdk@${SDK_VERSION}" --prefix "$HOME/.local" --loglevel=error 2>/dev/null || true
9
+ [ -d "$HOME/.local/bin" ] && export PATH="$HOME/.local/bin:$PATH"
10
+ fi
11
+ babysitter hook:run --harness unified --hook-type session-start --json
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ babysitter hook:run --harness unified --hook-type stop --json
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ babysitter hook:run --harness unified --hook-type user-prompt-submit --json