@a5c-ai/babysitter-openclaw 0.1.1-staging.fd09e30e → 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.
Files changed (47) hide show
  1. package/README.md +6 -9
  2. package/bin/cli.cjs +41 -25
  3. package/bin/install-shared.cjs +219 -0
  4. package/bin/install.cjs +13 -353
  5. package/bin/uninstall.cjs +9 -157
  6. package/commands/call.md +7 -7
  7. package/commands/contrib.md +31 -31
  8. package/commands/doctor.md +92 -6
  9. package/commands/forever.md +6 -6
  10. package/commands/help.md +245 -244
  11. package/commands/observe.md +12 -12
  12. package/commands/plan.md +7 -7
  13. package/commands/plugins.md +249 -249
  14. package/commands/project-install.md +10 -10
  15. package/commands/resume.md +8 -8
  16. package/commands/retrospect.md +55 -55
  17. package/commands/user-install.md +10 -10
  18. package/commands/yolo.md +7 -7
  19. package/extensions/hooks/agent-end.ts +8 -115
  20. package/extensions/hooks/before-prompt-build.ts +9 -87
  21. package/extensions/hooks/session-end.ts +8 -68
  22. package/extensions/hooks/session-start.ts +8 -69
  23. package/hooks/babysitter-proxied-agent-end.js +24 -0
  24. package/hooks/babysitter-proxied-agent-end.sh +3 -0
  25. package/hooks/babysitter-proxied-before-prompt-build.js +24 -0
  26. package/hooks/babysitter-proxied-before-prompt-build.sh +3 -0
  27. package/hooks/babysitter-proxied-session-end.js +24 -0
  28. package/hooks/babysitter-proxied-session-end.sh +3 -0
  29. package/hooks/babysitter-proxied-session-start.js +24 -0
  30. package/hooks/babysitter-proxied-session-start.sh +11 -0
  31. package/hooks/babysitter-proxied-stop-hook.sh +3 -0
  32. package/hooks.json +3 -2
  33. package/openclaw.plugin.json +4 -4
  34. package/package.json +10 -8
  35. package/plugin.json +3 -2
  36. package/scripts/create-release-tag.mjs +18 -0
  37. package/scripts/publish-from-tag.mjs +12 -0
  38. package/scripts/team-install.cjs +23 -0
  39. package/skills/babysit/SKILL.md +1 -0
  40. package/skills/doctor/SKILL.md +92 -6
  41. package/skills/help/SKILL.md +3 -2
  42. package/skills/observe/SKILL.md +1 -1
  43. package/versions.json +2 -1
  44. package/hooks/babysitter-session-start.sh +0 -61
  45. package/hooks/babysitter-stop-hook.sh +0 -44
  46. package/scripts/setup.sh +0 -99
  47. package/scripts/sync-command-docs.cjs +0 -106
package/README.md CHANGED
@@ -13,12 +13,14 @@ The SDK (`@a5c-ai/babysitter-sdk`) remains the single source of truth for orches
13
13
 
14
14
  ## Installation
15
15
 
16
- ### Primary: Babysitter Marketplace
16
+ ### Primary: Babysitter Harness Install
17
17
 
18
18
  ```bash
19
- babysitter plugin:install babysitter-openclaw
19
+ babysitter harness:install-plugin openclaw
20
20
  ```
21
21
 
22
+ This installs the `@a5c-ai/babysitter-openclaw` npm package and registers it with OpenClaw.
23
+
22
24
  ### Secondary: npm
23
25
 
24
26
  ```bash
@@ -35,7 +37,6 @@ npx @a5c-ai/babysitter-openclaw install --workspace /path/to/repo
35
37
 
36
38
  ```bash
37
39
  babysitter harness:discover --json
38
- babysitter plugin:list-installed --json
39
40
  ```
40
41
 
41
42
  ### Removal
@@ -44,12 +45,6 @@ babysitter plugin:list-installed --json
44
45
  npm uninstall -g @a5c-ai/babysitter-openclaw
45
46
  ```
46
47
 
47
- Or via the marketplace:
48
-
49
- ```bash
50
- babysitter plugin:uninstall babysitter-openclaw
51
- ```
52
-
53
48
  ## Architecture
54
49
 
55
50
  ### Daemon Model vs CLI Model
@@ -295,6 +290,8 @@ Read the pinned SDK version from `versions.json`:
295
290
  ```bash
296
291
  PLUGIN_ROOT="$(pwd)"
297
292
  SDK_VERSION=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync('${PLUGIN_ROOT}/versions.json','utf8')).sdkVersion||'latest')}catch{console.log('latest')}")
293
+ npm i -g @a5c-ai/babysitter-sdk@$SDK_VERSION
294
+
298
295
  CLI="npx -y @a5c-ai/babysitter-sdk@$SDK_VERSION"
299
296
  ```
300
297
 
package/bin/cli.cjs CHANGED
@@ -5,47 +5,51 @@ 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([
11
13
  'Usage:',
12
14
  ' babysitter-openclaw install [--global]',
13
15
  ' babysitter-openclaw install --workspace [path]',
14
- ' babysitter-openclaw uninstall [--global]',
15
- ' babysitter-openclaw uninstall --workspace [path]',
16
- ' babysitter-openclaw version',
17
- ' babysitter-openclaw help',
16
+ ' babysitter-openclaw uninstall',
18
17
  ].join('\n'));
19
18
  }
20
19
 
21
- function parseArgs(argv) {
20
+ function parseInstallArgs(argv) {
21
+ let scope = 'global';
22
22
  let workspace = null;
23
+ const passthrough = [];
23
24
 
24
25
  for (let i = 0; i < argv.length; i += 1) {
25
26
  const arg = argv[i];
27
+ if (arg === '--global') {
28
+ scope = 'global';
29
+ continue;
30
+ }
26
31
  if (arg === '--workspace') {
32
+ scope = 'workspace';
27
33
  const next = argv[i + 1];
28
- workspace = next && !next.startsWith('-') ? path.resolve(next) : process.cwd();
29
34
  if (next && !next.startsWith('-')) {
35
+ workspace = path.resolve(next);
30
36
  i += 1;
37
+ } else {
38
+ workspace = process.cwd();
31
39
  }
32
40
  continue;
33
41
  }
34
- if (arg === '--global') {
35
- workspace = null;
36
- continue;
37
- }
38
- throw new Error(`unknown argument: ${arg}`);
42
+ passthrough.push(arg);
39
43
  }
40
44
 
41
- return { workspace };
45
+ return { scope, workspace, passthrough };
42
46
  }
43
47
 
44
- function runNodeScript(scriptPath, args) {
48
+ function runNodeScript(scriptPath, args, extraEnv = {}) {
45
49
  const result = spawnSync(process.execPath, [scriptPath, ...args], {
46
50
  cwd: process.cwd(),
47
51
  stdio: 'inherit',
48
- env: process.env,
52
+ env: { ...process.env, ...extraEnv },
49
53
  });
50
54
  process.exitCode = result.status ?? 1;
51
55
  }
@@ -58,23 +62,35 @@ function main() {
58
62
  return;
59
63
  }
60
64
 
61
- if (command === 'version' || command === '--version' || command === '-v') {
62
- const fs = require('fs');
63
- const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
64
- console.log(`${pkg.name}@${pkg.version}`);
65
+ if (command === 'install') {
66
+ if (shared && typeof shared.harnessCliRoute === 'function' && shared.harnessCliRoute(rest, PACKAGE_ROOT, runNodeScript)) {
67
+ return;
68
+ }
69
+ const parsed = parseInstallArgs(rest);
70
+ if (parsed.scope === 'workspace') {
71
+ const args = [];
72
+ if (parsed.workspace) {
73
+ args.push('--workspace', parsed.workspace);
74
+ }
75
+ args.push(...parsed.passthrough);
76
+ runNodeScript(
77
+ path.join(PACKAGE_ROOT, 'scripts', 'team-install.cjs'),
78
+ args,
79
+ { PLUGIN_PACKAGE_ROOT: PACKAGE_ROOT },
80
+ );
81
+ return;
82
+ }
83
+ runNodeScript(path.join(PACKAGE_ROOT, 'bin', 'install.cjs'), parsed.passthrough);
65
84
  return;
66
85
  }
67
86
 
68
- if (command !== 'install' && command !== 'uninstall') {
69
- console.error(`[babysitter] Unknown command: ${command}`);
70
- printUsage();
71
- process.exitCode = 1;
87
+ if (command === 'uninstall') {
88
+ runNodeScript(path.join(PACKAGE_ROOT, 'bin', 'uninstall.cjs'), rest);
72
89
  return;
73
90
  }
74
91
 
75
- const parsed = parseArgs(rest);
76
- const args = parsed.workspace ? ['--workspace', parsed.workspace] : ['--global'];
77
- runNodeScript(path.join(PACKAGE_ROOT, 'bin', `${command}.cjs`), args);
92
+ printUsage();
93
+ process.exitCode = 1;
78
94
  }
79
95
 
80
96
  main();
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+
8
+ const PLUGIN_NAME = "babysitter";
9
+ const PLUGIN_CATEGORY = 'Coding';
10
+
11
+ function getUserHome() {
12
+ return os.homedir();
13
+ }
14
+
15
+ function getHarnessHome() {
16
+ return path.join(os.homedir(), ".openclaw");
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.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
+ module.exports = {
198
+ PLUGIN_NAME,
199
+ PLUGIN_CATEGORY,
200
+ getUserHome,
201
+ getHarnessHome,
202
+ getHomePluginRoot,
203
+ getHomeMarketplacePath,
204
+ writeFileIfChanged,
205
+ copyRecursive,
206
+ copyPluginBundle,
207
+ readJson,
208
+ writeJson,
209
+ ensureExecutable,
210
+ normalizeMarketplaceSourcePath,
211
+ ensureMarketplaceEntry,
212
+ removeMarketplaceEntry,
213
+ warnWindowsHooks,
214
+ runPostInstall,
215
+ getGlobalStateDir,
216
+ resolveCliCommand,
217
+ runCli,
218
+ ensureGlobalProcessLibrary,
219
+ };