@a5c-ai/babysitter-cursor 5.0.1-staging.e4f17eff → 5.0.1-staging.e920fef118ef

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 (56) hide show
  1. package/README.md +23 -17
  2. package/bin/cli.js +8 -16
  3. package/bin/install-shared.js +236 -120
  4. package/bin/install.js +12 -28
  5. package/bin/uninstall.js +11 -27
  6. package/commands/call.md +5 -1
  7. package/commands/cleanup.md +30 -8
  8. package/commands/doctor.md +7 -8
  9. package/commands/help.md +246 -244
  10. package/commands/observe.md +17 -12
  11. package/commands/yolo.md +11 -7
  12. package/hooks/babysitter-proxied-post-tool-use.ps1 +12 -0
  13. package/hooks/babysitter-proxied-post-tool-use.sh +3 -0
  14. package/hooks/babysitter-proxied-pre-tool-use.ps1 +12 -0
  15. package/hooks/babysitter-proxied-pre-tool-use.sh +3 -0
  16. package/hooks/babysitter-proxied-session-end.ps1 +12 -0
  17. package/hooks/babysitter-proxied-session-end.sh +3 -0
  18. package/hooks/babysitter-proxied-session-start.ps1 +10 -194
  19. package/hooks/babysitter-proxied-session-start.sh +7 -170
  20. package/hooks/babysitter-proxied-stop.ps1 +12 -0
  21. package/hooks/babysitter-proxied-stop.sh +3 -0
  22. package/hooks/babysitter-proxied-user-prompt-submit.ps1 +12 -0
  23. package/hooks/babysitter-proxied-user-prompt-submit.sh +3 -0
  24. package/hooks/hooks-cursor.json +34 -2
  25. package/hooks.json +34 -2
  26. package/package.json +20 -21
  27. package/plugin.json +7 -5
  28. package/scripts/create-release-tag.mjs +18 -0
  29. package/scripts/publish-from-tag.mjs +41 -0
  30. package/scripts/team-install.js +14 -72
  31. package/skills/babysit/SKILL.md +2 -4
  32. package/skills/call/SKILL.md +5 -1
  33. package/skills/cleanup/SKILL.md +43 -0
  34. package/skills/contrib/SKILL.md +34 -0
  35. package/skills/doctor/SKILL.md +7 -8
  36. package/skills/forever/SKILL.md +8 -0
  37. package/skills/help/SKILL.md +4 -2
  38. package/skills/observe/SKILL.md +7 -2
  39. package/skills/plugins/SKILL.md +257 -0
  40. package/skills/project-install/SKILL.md +18 -0
  41. package/skills/resume/SKILL.md +1 -1
  42. package/skills/retrospect/SKILL.md +48 -48
  43. package/skills/user-install/SKILL.md +3 -3
  44. package/skills/yolo/SKILL.md +12 -0
  45. package/versions.json +2 -1
  46. package/.cursor-plugin/plugin.json +0 -22
  47. package/.cursorrules +0 -55
  48. package/hooks/babysitter-proxied-stop-hook.ps1 +0 -125
  49. package/hooks/babysitter-proxied-stop-hook.sh +0 -112
  50. package/hooks/hooks-cursor.json.legacy +0 -21
  51. package/hooks/proxied-hooks.json +0 -22
  52. package/hooks/session-start.ps1 +0 -116
  53. package/hooks/session-start.sh +0 -106
  54. package/hooks/stop-hook.ps1 +0 -73
  55. package/hooks/stop-hook.sh +0 -61
  56. package/scripts/sync-command-surfaces.js +0 -62
package/README.md CHANGED
@@ -19,13 +19,13 @@ hooks so Cursor can execute Babysitter commands and hook scripts directly.
19
19
 
20
20
  ## Installation
21
21
 
22
- Install the SDK CLI first:
22
+ Install the Babysitter CLI once:
23
23
 
24
24
  ```bash
25
- npm install -g @a5c-ai/babysitter-sdk
25
+ npm install -g @a5c-ai/babysitter
26
26
  ```
27
27
 
28
- ### Via Cursor Marketplace (recommended)
28
+ ### Via Cursor Marketplace
29
29
 
30
30
  Install through Cursor's marketplace UI using the repo-root
31
31
  `/.cursor-plugin/marketplace.json` manifest:
@@ -34,10 +34,16 @@ Install through Cursor's marketplace UI using the repo-root
34
34
  2. Open the marketplace entry named **a5c-ai**
35
35
  3. Install the plugin named **babysitter**
36
36
 
37
- ### Via Babysitter harness install
37
+ ### Via Babysitter harness install (recommended for automation)
38
+
39
+ Use the SDK helper for scriptable global or workspace installs. This resolves to `npx --yes @a5c-ai/babysitter-cursor install ...` under the hood:
38
40
 
39
41
  ```bash
42
+ # Global install
40
43
  babysitter harness:install-plugin cursor
44
+
45
+ # Workspace install
46
+ babysitter harness:install-plugin cursor --workspace /path/to/repo
41
47
  ```
42
48
 
43
49
  If the workspace does not already have an active process-library binding, the
@@ -51,18 +57,18 @@ babysitter process-library:active --json
51
57
 
52
58
  For local development or environments without marketplace access:
53
59
 
54
- #### Via npm
60
+ #### Via the published package installer
55
61
 
56
62
  ```bash
57
- npm install -g @a5c-ai/babysitter-cursor
58
- babysitter-cursor install
63
+ npx --yes @a5c-ai/babysitter-cursor install --global
64
+ npx --yes @a5c-ai/babysitter-cursor install --workspace /path/to/repo
59
65
  ```
60
66
 
61
- #### From source
67
+ #### From generated source
62
68
 
63
69
  ```bash
64
- cd plugins/babysitter-cursor
65
- node bin/install.js
70
+ npm run generate:plugins
71
+ node artifacts/generated-plugins/cursor/bin/install.js
66
72
  ```
67
73
 
68
74
  #### Manual installation
@@ -70,9 +76,9 @@ node bin/install.js
70
76
  Copy the plugin directory to your local Cursor plugins path:
71
77
 
72
78
  ```bash
73
- cp -r plugins/babysitter-cursor ~/.cursor/plugins/local/babysitter-cursor
79
+ cp -r artifacts/generated-plugins/cursor ~/.cursor/plugins/local/babysitter-cursor
74
80
  # Or symlink for faster iteration:
75
- ln -s "$(pwd)/plugins/babysitter-cursor" ~/.cursor/plugins/local/babysitter-cursor
81
+ ln -s "$(pwd)/artifacts/generated-plugins/cursor" ~/.cursor/plugins/local/babysitter-cursor
76
82
  ```
77
83
 
78
84
  ## Uninstallation
@@ -90,7 +96,7 @@ npm uninstall -g @a5c-ai/babysitter-cursor
90
96
  ## Plugin Structure (Directory Layout)
91
97
 
92
98
  ```
93
- plugins/babysitter-cursor/
99
+ artifacts/generated-plugins/cursor/
94
100
  .cursor-plugin/
95
101
  plugin.json # Cursor plugin manifest (skills, commands, hooks, metadata)
96
102
  plugin.json # Babysitter plugin manifest (skills dir, hooks path, metadata)
@@ -311,7 +317,7 @@ The Cursor marketplace manifest lives at `.cursor-plugin/plugin.json`:
311
317
  "email": "support@a5c.ai",
312
318
  "url": "https://github.com/a5c-ai/babysitter"
313
319
  },
314
- "homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-cursor#readme",
320
+ "homepage": "https://github.com/a5c-ai/babysitter/tree/main/plugins/babysitter-unified/per-harness/cursor#readme",
315
321
  "repository": "https://github.com/a5c-ai/babysitter",
316
322
  "license": "MIT",
317
323
  "skills": "skills/",
@@ -438,14 +444,14 @@ or WSL. The Cursor hook manifest includes explicit `powershell` entries alongsid
438
444
  git clone https://github.com/a5c-ai/babysitter.git
439
445
  cd babysitter
440
446
  npm install
441
- cd plugins/babysitter-cursor
447
+ npm run generate:plugins
442
448
  node bin/install.js
443
449
  ```
444
450
 
445
451
  ### Publishing
446
452
 
447
453
  ```bash
448
- cd plugins/babysitter-cursor
454
+ cd artifacts/generated-plugins/cursor
449
455
  npm run deploy # Publish to npm (public)
450
456
  npm run deploy:staging # Publish to npm with staging tag
451
457
  ```
@@ -453,7 +459,7 @@ npm run deploy:staging # Publish to npm with staging tag
453
459
  ### Team installation
454
460
 
455
461
  ```bash
456
- cd plugins/babysitter-cursor
462
+ cd artifacts/generated-plugins/cursor
457
463
  npm run team:install
458
464
  ```
459
465
 
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,14 +5,205 @@ 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(), ".cursor");
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(getHarnessHome(), 'plugins'), PLUGIN_NAME);
22
+ }
23
+
24
+ function getHomeMarketplacePath() {
25
+ return path.join(getHarnessHome(), '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.e920fef118ef",
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
+ // Cursor-specific install surface.
198
+ // Appended after the compiler base and SDK surface — can override base functions.
199
+
10
200
  const HOOK_SCRIPT_NAMES = [
11
- 'session-start.sh',
12
- 'session-start.ps1',
13
- 'stop-hook.sh',
14
- 'stop-hook.ps1',
201
+ 'babysitter-proxied-session-start.sh',
202
+ 'babysitter-proxied-session-start.ps1',
203
+ 'babysitter-proxied-stop.sh',
204
+ 'babysitter-proxied-stop.ps1',
15
205
  ];
206
+
16
207
  const DEFAULT_MARKETPLACE = {
17
208
  name: 'local-plugins',
18
209
  interface: {
@@ -20,10 +211,12 @@ const DEFAULT_MARKETPLACE = {
20
211
  },
21
212
  plugins: [],
22
213
  };
214
+
23
215
  const MANAGED_HOOKS_CONFIG_PATHS = [
24
216
  path.join('hooks', 'hooks-cursor.json'),
25
217
  'hooks.json',
26
218
  ];
219
+
27
220
  const PLUGIN_BUNDLE_ENTRIES = [
28
221
  '.cursor-plugin',
29
222
  'plugin.json',
@@ -40,19 +233,6 @@ function getCursorHome() {
40
233
  return path.join(os.homedir(), '.cursor');
41
234
  }
42
235
 
43
- function getUserHome() {
44
- if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
45
- if (process.env.HOME) return path.resolve(process.env.HOME);
46
- return os.homedir();
47
- }
48
-
49
- function getGlobalStateDir() {
50
- if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
51
- return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
52
- }
53
- return path.join(getUserHome(), '.a5c');
54
- }
55
-
56
236
  function getHomePluginRoot() {
57
237
  if (process.env.BABYSITTER_CURSOR_PLUGIN_DIR) {
58
238
  return path.resolve(process.env.BABYSITTER_CURSOR_PLUGIN_DIR, PLUGIN_NAME);
@@ -67,18 +247,6 @@ function getHomeMarketplacePath() {
67
247
  return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
68
248
  }
69
249
 
70
- function writeFileIfChanged(filePath, contents) {
71
- if (fs.existsSync(filePath)) {
72
- const current = fs.readFileSync(filePath, 'utf8');
73
- if (current === contents) {
74
- return false;
75
- }
76
- }
77
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
78
- fs.writeFileSync(filePath, contents, 'utf8');
79
- return true;
80
- }
81
-
82
250
  function copyRecursive(src, dest) {
83
251
  const stat = fs.statSync(src);
84
252
  if (stat.isDirectory()) {
@@ -90,6 +258,7 @@ function copyRecursive(src, dest) {
90
258
  return;
91
259
  }
92
260
 
261
+ // Strip UTF-8 BOM from SKILL.md files
93
262
  if (path.basename(src) === 'SKILL.md') {
94
263
  const file = fs.readFileSync(src);
95
264
  const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
@@ -116,14 +285,6 @@ function copyPluginBundle(packageRoot, pluginRoot) {
116
285
  }
117
286
  }
118
287
 
119
- function ensureExecutable(filePath) {
120
- try {
121
- fs.chmodSync(filePath, 0o755);
122
- } catch {
123
- // Best-effort only. Windows and some filesystems may ignore mode changes.
124
- }
125
- }
126
-
127
288
  function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
128
289
  let next = pluginSourcePath;
129
290
  if (path.isAbsolute(next)) {
@@ -136,24 +297,6 @@ function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
136
297
  return next;
137
298
  }
138
299
 
139
- function readJson(filePath) {
140
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
141
- }
142
-
143
- function getManagedHooksConfigPath(packageRoot) {
144
- for (const relativePath of MANAGED_HOOKS_CONFIG_PATHS) {
145
- const candidate = path.join(packageRoot, relativePath);
146
- if (fs.existsSync(candidate)) {
147
- return candidate;
148
- }
149
- }
150
- return null;
151
- }
152
-
153
- function writeJson(filePath, value) {
154
- writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
155
- }
156
-
157
300
  function normalizeMarketplaceName(name) {
158
301
  const raw = String(name || '').trim();
159
302
  const sanitized = raw
@@ -208,6 +351,16 @@ function removeMarketplaceEntry(marketplacePath) {
208
351
  writeJson(marketplacePath, marketplace);
209
352
  }
210
353
 
354
+ function getManagedHooksConfigPath(packageRoot) {
355
+ for (const relativePath of MANAGED_HOOKS_CONFIG_PATHS) {
356
+ const candidate = path.join(packageRoot, relativePath);
357
+ if (fs.existsSync(candidate)) {
358
+ return candidate;
359
+ }
360
+ }
361
+ return null;
362
+ }
363
+
211
364
  function mergeManagedHooksConfig(packageRoot, cursorHome) {
212
365
  const hooksJsonPath = getManagedHooksConfigPath(packageRoot);
213
366
  if (!hooksJsonPath) return;
@@ -313,62 +466,6 @@ function installCursorSurface(packageRoot, cursorHome) {
313
466
  }
314
467
  }
315
468
 
316
- function resolveBabysitterCommand(packageRoot) {
317
- if (process.env.BABYSITTER_SDK_CLI) {
318
- return {
319
- command: process.execPath,
320
- argsPrefix: [path.resolve(process.env.BABYSITTER_SDK_CLI)],
321
- };
322
- }
323
- try {
324
- return {
325
- command: process.execPath,
326
- argsPrefix: [
327
- require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
328
- paths: [packageRoot],
329
- }),
330
- ],
331
- };
332
- } catch {
333
- return {
334
- command: 'babysitter',
335
- argsPrefix: [],
336
- };
337
- }
338
- }
339
-
340
- function runBabysitterCli(packageRoot, cliArgs, options = {}) {
341
- const resolved = resolveBabysitterCommand(packageRoot);
342
- const result = spawnSync(resolved.command, [...resolved.argsPrefix, ...cliArgs], {
343
- cwd: options.cwd || process.cwd(),
344
- stdio: ['ignore', 'pipe', 'pipe'],
345
- encoding: 'utf8',
346
- env: {
347
- ...process.env,
348
- ...(options.env || {}),
349
- },
350
- });
351
- if (result.status !== 0) {
352
- const stderr = (result.stderr || '').trim();
353
- const stdout = (result.stdout || '').trim();
354
- throw new Error(
355
- `babysitter ${cliArgs.join(' ')} failed` +
356
- (stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
357
- );
358
- }
359
- return result.stdout;
360
- }
361
-
362
- function ensureGlobalProcessLibrary(packageRoot) {
363
- return JSON.parse(
364
- runBabysitterCli(
365
- packageRoot,
366
- ['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
367
- { cwd: packageRoot },
368
- ),
369
- );
370
- }
371
-
372
469
  function warnWindowsHooks() {
373
470
  if (process.platform !== 'win32') {
374
471
  return;
@@ -377,18 +474,37 @@ function warnWindowsHooks() {
377
474
  console.warn(`[${PLUGIN_NAME}] Both bash (.sh) and PowerShell (.ps1) hook scripts are included.`);
378
475
  }
379
476
 
477
+
380
478
  module.exports = {
381
- copyPluginBundle,
479
+ PLUGIN_NAME,
480
+ PLUGIN_CATEGORY,
481
+ getUserHome,
482
+ getHarnessHome,
483
+ writeFileIfChanged,
484
+ readJson,
485
+ writeJson,
486
+ ensureExecutable,
487
+ runPostInstall,
488
+ getGlobalStateDir,
489
+ resolveCliCommand,
490
+ runCli,
382
491
  ensureGlobalProcessLibrary,
492
+ PLUGIN_BUNDLE_ENTRIES,
493
+ copyRecursive,
494
+ copyPluginBundle,
495
+ DEFAULT_MARKETPLACE,
496
+ normalizeMarketplaceSourcePath,
497
+ normalizeMarketplaceName,
383
498
  ensureMarketplaceEntry,
384
- getManagedHooksConfigPath,
499
+ removeMarketplaceEntry,
500
+ mergeManagedHooksConfig,
501
+ warnWindowsHooks,
502
+ HOOK_SCRIPT_NAMES,
503
+ MANAGED_HOOKS_CONFIG_PATHS,
385
504
  getCursorHome,
386
- getHomeMarketplacePath,
387
505
  getHomePluginRoot,
388
- installCursorSurface,
506
+ getHomeMarketplacePath,
507
+ getManagedHooksConfigPath,
389
508
  removeManagedHooks,
390
- removeMarketplaceEntry,
391
- warnWindowsHooks,
392
- writeJson,
393
- normalizeMarketplaceName,
509
+ installCursorSurface,
394
510
  };
package/bin/install.js CHANGED
@@ -2,43 +2,27 @@
2
2
  'use strict';
3
3
 
4
4
  const path = require('path');
5
- const {
6
- copyPluginBundle,
7
- ensureGlobalProcessLibrary,
8
- ensureMarketplaceEntry,
9
- getCursorHome,
10
- getHomeMarketplacePath,
11
- getHomePluginRoot,
12
- installCursorSurface,
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 cursorHome = getCursorHome();
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
- installCursorSurface(PACKAGE_ROOT, cursorHome);
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 Cursor 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.`);
40
24
  } catch (err) {
41
- console.error(`[babysitter] Failed to install plugin: ${err.message}`);
25
+ console.error(`[${shared.PLUGIN_NAME}] Failed to install: ${err.message}`);
42
26
  process.exitCode = 1;
43
27
  }
44
28
  }