@a5c-ai/babysitter-codex 0.1.6-staging.f48a64a2 → 0.1.6
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.
- package/.app.json +3 -0
- package/.codex-plugin/plugin.json +47 -0
- package/README.md +41 -108
- package/assets/icon.svg +7 -0
- package/assets/logo.svg +8 -0
- package/bin/install-shared.js +509 -0
- package/bin/install.js +29 -433
- package/bin/uninstall.js +19 -120
- package/{.codex/hooks → hooks}/babysitter-session-start.sh +0 -0
- package/{.codex/hooks → hooks}/babysitter-stop-hook.sh +0 -0
- package/{.codex/hooks.json → hooks.json} +3 -3
- package/package.json +9 -5
- package/scripts/team-install.js +32 -424
- package/skills/babysit/SKILL.md +798 -0
- package/{.codex/skills → skills}/call/SKILL.md +1 -0
- package/{.codex/skills → skills}/yolo/SKILL.md +3 -0
- package/.codex/config.toml +0 -25
- package/.codex/skills/babysit/SKILL.md +0 -385
- package/SKILL.md +0 -385
- /package/{.codex/hooks → hooks}/user-prompt-submit.sh +0 -0
- /package/{.codex/skills → skills}/assimilate/SKILL.md +0 -0
- /package/{.codex/skills → skills}/doctor/SKILL.md +0 -0
- /package/{.codex/skills → skills}/forever/SKILL.md +0 -0
- /package/{.codex/skills → skills}/help/SKILL.md +0 -0
- /package/{.codex/skills → skills}/issue/SKILL.md +0 -0
- /package/{.codex/skills → skills}/model/SKILL.md +0 -0
- /package/{.codex/skills → skills}/observe/SKILL.md +0 -0
- /package/{.codex/skills → skills}/plan/SKILL.md +0 -0
- /package/{.codex/skills → skills}/project-install/SKILL.md +0 -0
- /package/{.codex/skills → skills}/resume/SKILL.md +0 -0
- /package/{.codex/skills → skills}/retrospect/SKILL.md +0 -0
- /package/{.codex/skills → skills}/team-install/SKILL.md +0 -0
- /package/{.codex/skills → skills}/user-install/SKILL.md +0 -0
package/bin/install.js
CHANGED
|
@@ -1,450 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* install.js
|
|
6
|
-
*
|
|
7
|
-
* Installs the Codex-facing Babysitter skill bundle globally under CODEX_HOME,
|
|
8
|
-
* installs Codex mode-wrapper skills such as `$call` and `$plan`,
|
|
9
|
-
* clones/updates the process library into ~/.a5c via the SDK CLI.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
4
|
const path = require('path');
|
|
14
|
-
const
|
|
15
|
-
|
|
5
|
+
const {
|
|
6
|
+
copyPluginBundle,
|
|
7
|
+
ensureGlobalProcessLibrary,
|
|
8
|
+
ensureMarketplaceEntry,
|
|
9
|
+
getCodexHome,
|
|
10
|
+
getHomeMarketplacePath,
|
|
11
|
+
getHomePluginRoot,
|
|
12
|
+
installCodexSurface,
|
|
13
|
+
mergeCodexConfigFile,
|
|
14
|
+
warnWindowsHooks,
|
|
15
|
+
} = require('./install-shared');
|
|
16
16
|
|
|
17
|
-
const SKILL_NAME = 'babysit';
|
|
18
|
-
const LEGACY_SKILL_NAME = 'babysitter-codex';
|
|
19
|
-
const MODE_SKILL_NAMES = [
|
|
20
|
-
'assimilate',
|
|
21
|
-
'call',
|
|
22
|
-
'doctor',
|
|
23
|
-
'forever',
|
|
24
|
-
'help',
|
|
25
|
-
'issue',
|
|
26
|
-
'model',
|
|
27
|
-
'observe',
|
|
28
|
-
'plan',
|
|
29
|
-
'project-install',
|
|
30
|
-
'resume',
|
|
31
|
-
'retrospect',
|
|
32
|
-
'team-install',
|
|
33
|
-
'user-install',
|
|
34
|
-
'yolo',
|
|
35
|
-
];
|
|
36
17
|
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
37
|
-
const IS_WIN = process.platform === 'win32';
|
|
38
|
-
const GLOBAL_HOOK_SPECS = [
|
|
39
|
-
{ event: 'SessionStart', script: 'babysitter-session-start.sh' },
|
|
40
|
-
{ event: 'UserPromptSubmit', script: 'user-prompt-submit.sh' },
|
|
41
|
-
{ event: 'Stop', script: 'babysitter-stop-hook.sh' },
|
|
42
|
-
];
|
|
43
|
-
const INSTALL_ENTRIES = [
|
|
44
|
-
{ source: 'SKILL.md', required: true },
|
|
45
|
-
{ source: '.codex', required: true },
|
|
46
|
-
{ source: 'scripts', required: true },
|
|
47
|
-
{ source: 'babysitter.lock.json', required: true },
|
|
48
|
-
];
|
|
49
|
-
const LEGACY_PROMPT_NAMES = ['babysit.md', ...MODE_SKILL_NAMES.map((name) => `${name}.md`)];
|
|
50
|
-
|
|
51
|
-
function getCodexHome() {
|
|
52
|
-
if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
|
|
53
|
-
return path.join(os.homedir(), '.codex');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function getGlobalStateDir() {
|
|
57
|
-
if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
|
|
58
|
-
return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
|
|
59
|
-
}
|
|
60
|
-
return path.join(os.homedir(), '.a5c');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function writeFileIfChanged(filePath, contents) {
|
|
64
|
-
if (fs.existsSync(filePath)) {
|
|
65
|
-
const current = fs.readFileSync(filePath, 'utf8');
|
|
66
|
-
if (current === contents) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
71
|
-
fs.writeFileSync(filePath, contents, 'utf8');
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function copyRecursive(src, dest) {
|
|
76
|
-
const stat = fs.statSync(src);
|
|
77
|
-
if (stat.isDirectory()) {
|
|
78
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
79
|
-
for (const entry of fs.readdirSync(src)) {
|
|
80
|
-
if (['node_modules', '.a5c', '.git', 'test', '.gitignore'].includes(entry)) continue;
|
|
81
|
-
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
82
|
-
}
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (path.basename(src) === 'SKILL.md') {
|
|
87
|
-
const file = fs.readFileSync(src);
|
|
88
|
-
const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
|
|
89
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
90
|
-
fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
95
|
-
fs.copyFileSync(src, dest);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function listModeSkillEntries() {
|
|
99
|
-
const skillsDir = path.join(PACKAGE_ROOT, '.codex', 'skills');
|
|
100
|
-
if (!fs.existsSync(skillsDir)) {
|
|
101
|
-
throw new Error(`required Codex skills directory is missing: ${skillsDir}`);
|
|
102
|
-
}
|
|
103
|
-
return fs
|
|
104
|
-
.readdirSync(skillsDir, { withFileTypes: true })
|
|
105
|
-
.filter((entry) => entry.isDirectory() && entry.name !== SKILL_NAME)
|
|
106
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
107
|
-
.map((entry) => {
|
|
108
|
-
const skillName = entry.name;
|
|
109
|
-
const skillFile = path.join(skillsDir, skillName, 'SKILL.md');
|
|
110
|
-
if (!fs.existsSync(skillFile)) {
|
|
111
|
-
throw new Error(`required mode skill payload is missing: ${skillFile}`);
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
skillName,
|
|
115
|
-
sourceDir: path.join('.codex', 'skills', skillName),
|
|
116
|
-
};
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function renderCodexConfigToml() {
|
|
121
|
-
return [
|
|
122
|
-
'approval_policy = "on-request"',
|
|
123
|
-
'sandbox_mode = "workspace-write"',
|
|
124
|
-
'project_doc_max_bytes = 65536',
|
|
125
|
-
'',
|
|
126
|
-
'[sandbox_workspace_write]',
|
|
127
|
-
'writable_roots = [".a5c", ".codex"]',
|
|
128
|
-
'',
|
|
129
|
-
'[features]',
|
|
130
|
-
'codex_hooks = true',
|
|
131
|
-
'multi_agent = true',
|
|
132
|
-
'',
|
|
133
|
-
'[agents]',
|
|
134
|
-
'max_depth = 3',
|
|
135
|
-
'max_threads = 4',
|
|
136
|
-
'',
|
|
137
|
-
].join('\n');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function insertRootKey(content, key, line) {
|
|
141
|
-
const keyPattern = new RegExp(`^\\s*${key}\\s*=`, 'm');
|
|
142
|
-
if (keyPattern.test(content)) {
|
|
143
|
-
return content;
|
|
144
|
-
}
|
|
145
|
-
const sectionMatch = content.match(/^\[[^\]]+\]\s*$/m);
|
|
146
|
-
if (!sectionMatch || sectionMatch.index === undefined) {
|
|
147
|
-
return content.trim()
|
|
148
|
-
? `${content.trimEnd()}\n${line}\n`
|
|
149
|
-
: `${line}\n`;
|
|
150
|
-
}
|
|
151
|
-
const before = content.slice(0, sectionMatch.index).trimEnd();
|
|
152
|
-
const after = content.slice(sectionMatch.index);
|
|
153
|
-
return before
|
|
154
|
-
? `${before}\n${line}\n\n${after}`
|
|
155
|
-
: `${line}\n\n${after}`;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function ensureSectionLine(content, sectionName, lineKey, line) {
|
|
159
|
-
const keyPattern = new RegExp(`^\\s*${lineKey}\\s*=`, 'm');
|
|
160
|
-
if (keyPattern.test(content)) {
|
|
161
|
-
return content;
|
|
162
|
-
}
|
|
163
|
-
const sectionHeader = `[${sectionName}]`;
|
|
164
|
-
const escapedSection = sectionName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
165
|
-
const sectionPattern = new RegExp(`^\\[${escapedSection}\\]\\s*$`, 'm');
|
|
166
|
-
if (sectionPattern.test(content)) {
|
|
167
|
-
return content.replace(sectionPattern, `${sectionHeader}\n${line}`);
|
|
168
|
-
}
|
|
169
|
-
return content.trim()
|
|
170
|
-
? `${content.trimEnd()}\n\n${sectionHeader}\n${line}\n`
|
|
171
|
-
: `${sectionHeader}\n${line}\n`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function ensureWritableRoots(content) {
|
|
175
|
-
const sectionPattern = /^\[sandbox_workspace_write\]\s*$/m;
|
|
176
|
-
const rootsPattern = /^writable_roots\s*=\s*\[(.*?)\]\s*$/m;
|
|
177
|
-
const requiredRoots = ['.a5c', '.codex'];
|
|
178
|
-
|
|
179
|
-
if (!sectionPattern.test(content)) {
|
|
180
|
-
return content.trim()
|
|
181
|
-
? `${content.trimEnd()}\n\n[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]\n`
|
|
182
|
-
: '[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]\n';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!rootsPattern.test(content)) {
|
|
186
|
-
return content.replace(
|
|
187
|
-
sectionPattern,
|
|
188
|
-
'[sandbox_workspace_write]\nwritable_roots = [".a5c", ".codex"]',
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return content.replace(rootsPattern, (_match, inner) => {
|
|
193
|
-
const values = inner
|
|
194
|
-
.split(',')
|
|
195
|
-
.map((part) => part.trim())
|
|
196
|
-
.filter(Boolean)
|
|
197
|
-
.map((part) => part.replace(/^"(.*)"$/, '$1'));
|
|
198
|
-
const merged = [...new Set([...values, ...requiredRoots])];
|
|
199
|
-
const rendered = merged.map((value) => `"${value}"`).join(', ');
|
|
200
|
-
return `writable_roots = [${rendered}]`;
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function mergeCodexConfig(existing) {
|
|
205
|
-
let content = existing.trim() ? existing : '';
|
|
206
|
-
content = insertRootKey(content, 'approval_policy', 'approval_policy = "on-request"');
|
|
207
|
-
content = insertRootKey(content, 'sandbox_mode', 'sandbox_mode = "workspace-write"');
|
|
208
|
-
content = insertRootKey(content, 'project_doc_max_bytes', 'project_doc_max_bytes = 65536');
|
|
209
|
-
content = ensureWritableRoots(content);
|
|
210
|
-
content = ensureSectionLine(content, 'features', 'codex_hooks', 'codex_hooks = true');
|
|
211
|
-
content = ensureSectionLine(content, 'features', 'multi_agent', 'multi_agent = true');
|
|
212
|
-
content = ensureSectionLine(content, 'agents', 'max_depth', 'max_depth = 3');
|
|
213
|
-
content = ensureSectionLine(content, 'agents', 'max_threads', 'max_threads = 4');
|
|
214
|
-
return `${content.trimEnd()}\n`;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function renderHookCommand(filePath) {
|
|
218
|
-
const normalized = String(filePath).replace(/\\/g, '/');
|
|
219
|
-
return normalized.includes(' ') ? `"${normalized}"` : normalized;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function buildHooksConfig(hooksRoot) {
|
|
223
|
-
return {
|
|
224
|
-
hooks: Object.fromEntries(
|
|
225
|
-
GLOBAL_HOOK_SPECS.map(({ event, script }) => [
|
|
226
|
-
event,
|
|
227
|
-
[
|
|
228
|
-
{
|
|
229
|
-
matcher: '*',
|
|
230
|
-
hooks: [
|
|
231
|
-
{
|
|
232
|
-
type: 'command',
|
|
233
|
-
command: renderHookCommand(path.join(hooksRoot, script)),
|
|
234
|
-
},
|
|
235
|
-
],
|
|
236
|
-
},
|
|
237
|
-
],
|
|
238
|
-
]),
|
|
239
|
-
),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function ensureExecutableHooks(hookDir) {
|
|
244
|
-
if (IS_WIN || !fs.existsSync(hookDir)) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
for (const name of fs.readdirSync(hookDir)) {
|
|
248
|
-
const hookPath = path.join(hookDir, name);
|
|
249
|
-
if (name.endsWith('.sh') && fs.statSync(hookPath).isFile()) {
|
|
250
|
-
fs.chmodSync(hookPath, 0o755);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
console.log(`[babysitter-codex] +x ${hookDir}`);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function resolveBabysitterCommand(packageRoot) {
|
|
257
|
-
if (process.env.BABYSITTER_SDK_CLI) {
|
|
258
|
-
return {
|
|
259
|
-
command: process.execPath,
|
|
260
|
-
argsPrefix: [path.resolve(process.env.BABYSITTER_SDK_CLI)],
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
try {
|
|
264
|
-
return {
|
|
265
|
-
command: process.execPath,
|
|
266
|
-
argsPrefix: [
|
|
267
|
-
require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
|
|
268
|
-
paths: [packageRoot],
|
|
269
|
-
}),
|
|
270
|
-
],
|
|
271
|
-
};
|
|
272
|
-
} catch {
|
|
273
|
-
return {
|
|
274
|
-
command: 'babysitter',
|
|
275
|
-
argsPrefix: [],
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function runBabysitterCli(packageRoot, cliArgs, options = {}) {
|
|
281
|
-
const resolved = resolveBabysitterCommand(packageRoot);
|
|
282
|
-
const result = spawnSync(resolved.command, [...resolved.argsPrefix, ...cliArgs], {
|
|
283
|
-
cwd: options.cwd || process.cwd(),
|
|
284
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
285
|
-
encoding: 'utf8',
|
|
286
|
-
env: {
|
|
287
|
-
...process.env,
|
|
288
|
-
...(options.env || {}),
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
if (result.status !== 0) {
|
|
292
|
-
const stderr = (result.stderr || '').trim();
|
|
293
|
-
const stdout = (result.stdout || '').trim();
|
|
294
|
-
throw new Error(
|
|
295
|
-
`babysitter ${cliArgs.join(' ')} failed` +
|
|
296
|
-
(stderr ? `: ${stderr}` : stdout ? `: ${stdout}` : ''),
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
return result.stdout;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function ensureGlobalProcessLibrary(packageRoot) {
|
|
303
|
-
const active = JSON.parse(
|
|
304
|
-
runBabysitterCli(
|
|
305
|
-
packageRoot,
|
|
306
|
-
['process-library:active', '--state-dir', getGlobalStateDir(), '--json'],
|
|
307
|
-
{ cwd: packageRoot },
|
|
308
|
-
),
|
|
309
|
-
);
|
|
310
|
-
console.log(`[babysitter-codex] process library: ${active.binding?.dir}`);
|
|
311
|
-
if (active.defaultSpec?.cloneDir) {
|
|
312
|
-
console.log(`[babysitter-codex] process library clone: ${active.defaultSpec.cloneDir}`);
|
|
313
|
-
}
|
|
314
|
-
console.log(`[babysitter-codex] process library state: ${active.stateFile}`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function mergeCodexHomeConfig(codexHome) {
|
|
318
|
-
const configPath = path.join(codexHome, 'config.toml');
|
|
319
|
-
if (!fs.existsSync(configPath)) {
|
|
320
|
-
writeFileIfChanged(configPath, renderCodexConfigToml());
|
|
321
|
-
console.log(`[babysitter-codex] wrote ${configPath}`);
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const content = mergeCodexConfig(fs.readFileSync(configPath, 'utf8'));
|
|
326
|
-
if (writeFileIfChanged(configPath, content)) {
|
|
327
|
-
console.log(`[babysitter-codex] merged ${configPath}`);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function installGlobalHooks(codexHome) {
|
|
332
|
-
const srcHooksDir = path.join(PACKAGE_ROOT, '.codex', 'hooks');
|
|
333
|
-
if (!fs.existsSync(srcHooksDir)) {
|
|
334
|
-
throw new Error(`required hook payload is missing: ${srcHooksDir}`);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const hooksDir = path.join(codexHome, 'hooks');
|
|
338
|
-
copyRecursive(srcHooksDir, hooksDir);
|
|
339
|
-
console.log('[babysitter-codex] hooks/');
|
|
340
|
-
|
|
341
|
-
const hooksConfigPath = path.join(codexHome, 'hooks.json');
|
|
342
|
-
writeFileIfChanged(
|
|
343
|
-
hooksConfigPath,
|
|
344
|
-
`${JSON.stringify(buildHooksConfig(hooksDir), null, 2)}\n`,
|
|
345
|
-
);
|
|
346
|
-
console.log('[babysitter-codex] hooks.json');
|
|
347
|
-
|
|
348
|
-
ensureExecutableHooks(hooksDir);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function removeLegacySkillDir(codexHome) {
|
|
352
|
-
const legacyDir = path.join(codexHome, 'skills', LEGACY_SKILL_NAME);
|
|
353
|
-
if (!fs.existsSync(legacyDir)) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
fs.rmSync(legacyDir, { recursive: true, force: true });
|
|
357
|
-
console.log(`[babysitter-codex] removed legacy skill ${legacyDir}`);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function removeEmbeddedPromptSource(skillDir) {
|
|
361
|
-
const embeddedPromptsDir = path.join(skillDir, 'prompts');
|
|
362
|
-
if (!fs.existsSync(embeddedPromptsDir)) {
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
fs.rmSync(embeddedPromptsDir, { recursive: true, force: true });
|
|
366
|
-
console.log(`[babysitter-codex] removed embedded prompt source ${embeddedPromptsDir}`);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function removeLegacyPrompts(codexHome) {
|
|
370
|
-
for (const promptName of LEGACY_PROMPT_NAMES) {
|
|
371
|
-
const promptPath = path.join(codexHome, 'prompts', promptName);
|
|
372
|
-
if (!fs.existsSync(promptPath)) {
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
fs.rmSync(promptPath, { force: true });
|
|
376
|
-
console.log(`[babysitter-codex] removed legacy prompt ${promptPath}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
function installEntry(skillDir, entry) {
|
|
381
|
-
const src = path.join(PACKAGE_ROOT, entry.source);
|
|
382
|
-
const dest = path.join(skillDir, entry.source);
|
|
383
|
-
if (!fs.existsSync(src)) {
|
|
384
|
-
if (entry.required) {
|
|
385
|
-
throw new Error(`required install payload is missing: ${src}`);
|
|
386
|
-
}
|
|
387
|
-
return false;
|
|
388
|
-
}
|
|
389
|
-
copyRecursive(src, dest);
|
|
390
|
-
console.log(`[babysitter-codex] ${entry.source}${fs.statSync(src).isDirectory() ? '/' : ''}`);
|
|
391
|
-
return true;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function installModeSkillEntry(codexHome, entry) {
|
|
395
|
-
const src = path.join(PACKAGE_ROOT, entry.sourceDir);
|
|
396
|
-
const dest = path.join(codexHome, 'skills', entry.skillName);
|
|
397
|
-
copyRecursive(src, dest);
|
|
398
|
-
console.log(`[babysitter-codex] skills/${entry.skillName}/`);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function verifyInstalledPayload(skillDir, codexHome) {
|
|
402
|
-
const modeSkillEntries = listModeSkillEntries();
|
|
403
|
-
const missing = INSTALL_ENTRIES
|
|
404
|
-
.map((entry) => entry.source)
|
|
405
|
-
.filter((source) => !fs.existsSync(path.join(skillDir, source)));
|
|
406
|
-
if (missing.length > 0) {
|
|
407
|
-
throw new Error(`installed skill is incomplete; missing: ${missing.join(', ')}`);
|
|
408
|
-
}
|
|
409
|
-
for (const entry of modeSkillEntries) {
|
|
410
|
-
if (!fs.existsSync(path.join(codexHome, 'skills', entry.skillName, 'SKILL.md'))) {
|
|
411
|
-
throw new Error(`installed mode skill is missing: ${entry.skillName}`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
18
|
|
|
416
19
|
function main() {
|
|
417
20
|
const codexHome = getCodexHome();
|
|
418
|
-
const
|
|
419
|
-
const
|
|
21
|
+
const pluginRoot = getHomePluginRoot();
|
|
22
|
+
const marketplacePath = getHomeMarketplacePath();
|
|
420
23
|
|
|
421
|
-
console.log(`[babysitter-codex] Installing
|
|
24
|
+
console.log(`[babysitter-codex] Installing plugin to ${pluginRoot}`);
|
|
422
25
|
|
|
423
26
|
try {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
mergeCodexHomeConfig(codexHome);
|
|
438
|
-
installGlobalHooks(codexHome);
|
|
439
|
-
|
|
440
|
-
ensureExecutableHooks(path.join(skillDir, '.codex', 'hooks'));
|
|
441
|
-
|
|
442
|
-
ensureGlobalProcessLibrary(PACKAGE_ROOT);
|
|
443
|
-
|
|
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-codex] marketplace: ${marketplacePath}`);
|
|
34
|
+
console.log(`[babysitter-codex] process library: ${active.binding?.dir}`);
|
|
35
|
+
if (active.defaultSpec?.cloneDir) {
|
|
36
|
+
console.log(`[babysitter-codex] process library clone: ${active.defaultSpec.cloneDir}`);
|
|
37
|
+
}
|
|
38
|
+
console.log(`[babysitter-codex] process library state: ${active.stateFile}`);
|
|
39
|
+
warnWindowsHooks();
|
|
444
40
|
console.log('[babysitter-codex] Installation complete!');
|
|
445
|
-
console.log('[babysitter-codex] Restart Codex to pick up the
|
|
41
|
+
console.log('[babysitter-codex] Restart Codex to pick up the installed plugin and config changes.');
|
|
446
42
|
} catch (err) {
|
|
447
|
-
console.error(`[babysitter-codex] Failed to install
|
|
43
|
+
console.error(`[babysitter-codex] Failed to install plugin: ${err.message}`);
|
|
448
44
|
process.exitCode = 1;
|
|
449
45
|
}
|
|
450
46
|
}
|
package/bin/uninstall.js
CHANGED
|
@@ -1,137 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* uninstall.js
|
|
6
|
-
*
|
|
7
|
-
* Removes the globally installed Codex Babysitter skill, its mode-wrapper
|
|
8
|
-
* skills, and any legacy prompt aliases left from older installs. The globally
|
|
9
|
-
* cloned process library is intentionally kept.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
4
|
const fs = require('fs');
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'call',
|
|
21
|
-
'doctor',
|
|
22
|
-
'forever',
|
|
23
|
-
'help',
|
|
24
|
-
'issue',
|
|
25
|
-
'model',
|
|
26
|
-
'observe',
|
|
27
|
-
'plan',
|
|
28
|
-
'project-install',
|
|
29
|
-
'resume',
|
|
30
|
-
'retrospect',
|
|
31
|
-
'team-install',
|
|
32
|
-
'user-install',
|
|
33
|
-
'yolo',
|
|
34
|
-
];
|
|
35
|
-
const PROMPT_NAMES = [
|
|
36
|
-
'assimilate.md',
|
|
37
|
-
'call.md',
|
|
38
|
-
'doctor.md',
|
|
39
|
-
'forever.md',
|
|
40
|
-
'help.md',
|
|
41
|
-
'issue.md',
|
|
42
|
-
'model.md',
|
|
43
|
-
'observe.md',
|
|
44
|
-
'plan.md',
|
|
45
|
-
'project-install.md',
|
|
46
|
-
'resume.md',
|
|
47
|
-
'retrospect.md',
|
|
48
|
-
'team-install.md',
|
|
49
|
-
'user-install.md',
|
|
50
|
-
'yolo.md',
|
|
51
|
-
'babysit.md',
|
|
52
|
-
];
|
|
53
|
-
const HOOK_SCRIPT_NAMES = [
|
|
54
|
-
'babysitter-session-start.sh',
|
|
55
|
-
'babysitter-stop-hook.sh',
|
|
56
|
-
'user-prompt-submit.sh',
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
function getCodexHome() {
|
|
60
|
-
if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
|
|
61
|
-
return path.join(os.homedir(), '.codex');
|
|
62
|
-
}
|
|
5
|
+
const {
|
|
6
|
+
getCodexHome,
|
|
7
|
+
getHomeMarketplacePath,
|
|
8
|
+
getHomePluginRoot,
|
|
9
|
+
removeLegacyCodexSurface,
|
|
10
|
+
removeMarketplaceEntry,
|
|
11
|
+
} = require('./install-shared');
|
|
63
12
|
|
|
64
13
|
function main() {
|
|
65
14
|
const codexHome = getCodexHome();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const skillDir = path.join(codexHome, 'skills', skillName);
|
|
70
|
-
if (!fs.existsSync(skillDir)) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
75
|
-
console.log(`[babysitter-codex] Removed ${skillDir}`);
|
|
76
|
-
removedAny = true;
|
|
77
|
-
} catch (err) {
|
|
78
|
-
console.warn(`[babysitter-codex] Warning: Could not remove skill directory ${skillDir}: ${err.message}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const promptName of PROMPT_NAMES) {
|
|
83
|
-
const promptPath = path.join(codexHome, 'prompts', promptName);
|
|
84
|
-
if (!fs.existsSync(promptPath)) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
fs.rmSync(promptPath, { force: true });
|
|
89
|
-
console.log(`[babysitter-codex] Removed ${promptPath}`);
|
|
90
|
-
removedAny = true;
|
|
91
|
-
} catch (err) {
|
|
92
|
-
console.warn(`[babysitter-codex] Warning: Could not remove prompt ${promptPath}: ${err.message}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
15
|
+
const pluginRoot = getHomePluginRoot();
|
|
16
|
+
const marketplacePath = getHomeMarketplacePath();
|
|
17
|
+
let removedPlugin = false;
|
|
95
18
|
|
|
96
|
-
|
|
97
|
-
const hookPath = path.join(codexHome, 'hooks', hookName);
|
|
98
|
-
if (!fs.existsSync(hookPath)) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
19
|
+
if (fs.existsSync(pluginRoot)) {
|
|
101
20
|
try {
|
|
102
|
-
fs.rmSync(
|
|
103
|
-
console.log(`[babysitter-codex] Removed ${
|
|
104
|
-
|
|
21
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
22
|
+
console.log(`[babysitter-codex] Removed ${pluginRoot}`);
|
|
23
|
+
removedPlugin = true;
|
|
105
24
|
} catch (err) {
|
|
106
|
-
console.warn(`[babysitter-codex] Warning: Could not remove
|
|
25
|
+
console.warn(`[babysitter-codex] Warning: Could not remove plugin directory ${pluginRoot}: ${err.message}`);
|
|
107
26
|
}
|
|
108
27
|
}
|
|
109
28
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
fs.rmSync(hooksConfigPath, { force: true });
|
|
114
|
-
console.log(`[babysitter-codex] Removed ${hooksConfigPath}`);
|
|
115
|
-
removedAny = true;
|
|
116
|
-
} catch (err) {
|
|
117
|
-
console.warn(`[babysitter-codex] Warning: Could not remove hook config ${hooksConfigPath}: ${err.message}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const hooksDir = path.join(codexHome, 'hooks');
|
|
122
|
-
if (fs.existsSync(hooksDir)) {
|
|
123
|
-
try {
|
|
124
|
-
if (fs.readdirSync(hooksDir).length === 0) {
|
|
125
|
-
fs.rmSync(hooksDir, { recursive: true, force: true });
|
|
126
|
-
console.log(`[babysitter-codex] Removed ${hooksDir}`);
|
|
127
|
-
}
|
|
128
|
-
} catch (err) {
|
|
129
|
-
console.warn(`[babysitter-codex] Warning: Could not clean hook directory ${hooksDir}: ${err.message}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
29
|
+
removeMarketplaceEntry(marketplacePath);
|
|
30
|
+
removeLegacyCodexSurface(codexHome);
|
|
132
31
|
|
|
133
|
-
if (!
|
|
134
|
-
console.log('[babysitter-codex]
|
|
32
|
+
if (!removedPlugin) {
|
|
33
|
+
console.log('[babysitter-codex] Plugin directory not found, legacy Codex surface cleaned if present.');
|
|
135
34
|
return;
|
|
136
35
|
}
|
|
137
36
|
|
|
File without changes
|
|
File without changes
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "
|
|
9
|
+
"command": "./hooks/babysitter-session-start.sh"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"hooks": [
|
|
18
18
|
{
|
|
19
19
|
"type": "command",
|
|
20
|
-
"command": "
|
|
20
|
+
"command": "./hooks/user-prompt-submit.sh"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"hooks": [
|
|
29
29
|
{
|
|
30
30
|
"type": "command",
|
|
31
|
-
"command": "
|
|
31
|
+
"command": "./hooks/babysitter-stop-hook.sh"
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
}
|