@a5c-ai/babysitter-github 5.0.1-staging.e4f17eff → 5.0.1-staging.ef4e872c
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/bin/cli.js +14 -26
- package/bin/install-shared.js +398 -215
- package/bin/install.js +49 -89
- package/bin/uninstall.js +30 -60
- package/commands/doctor.md +5 -5
- package/commands/help.md +245 -244
- package/commands/observe.md +12 -12
- package/hooks/babysitter-proxied-session-end.ps1 +10 -114
- package/hooks/babysitter-proxied-session-end.sh +2 -111
- package/hooks/babysitter-proxied-session-start.ps1 +10 -187
- package/hooks/babysitter-proxied-session-start.sh +6 -168
- package/hooks/babysitter-proxied-user-prompt-submitted.ps1 +10 -90
- package/hooks/babysitter-proxied-user-prompt-submitted.sh +2 -86
- package/hooks.json +10 -10
- package/package.json +20 -20
- package/plugin.json +7 -6
- package/scripts/sync-command-surfaces.js +12 -54
- package/scripts/team-install.js +14 -84
- package/skills/cleanup/SKILL.md +21 -0
- package/skills/contrib/SKILL.md +34 -0
- package/skills/doctor/SKILL.md +5 -5
- package/skills/forever/SKILL.md +8 -0
- package/skills/help/SKILL.md +3 -2
- package/skills/observe/SKILL.md +1 -1
- package/skills/plugins/SKILL.md +257 -0
- package/skills/project-install/SKILL.md +18 -0
- package/skills/resume/SKILL.md +1 -1
- package/skills/retrospect/SKILL.md +48 -48
- package/skills/user-install/SKILL.md +3 -3
- package/skills/yolo/SKILL.md +8 -0
- package/versions.json +2 -1
- package/.github/plugin.json +0 -25
- package/hooks/proxied-hooks.json +0 -29
- package/hooks/session-end.ps1 +0 -69
- package/hooks/session-end.sh +0 -54
- package/hooks/session-start.ps1 +0 -111
- package/hooks/session-start.sh +0 -101
- package/hooks/user-prompt-submitted.ps1 +0 -52
- package/hooks/user-prompt-submitted.sh +0 -31
package/bin/install-shared.js
CHANGED
|
@@ -5,20 +5,213 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
|
|
8
|
-
const PLUGIN_NAME =
|
|
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(), ".copilot");
|
|
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
|
+
// Per-harness surface for github-copilot.
|
|
198
|
+
// Contains only harness-specific constants, functions unique to GitHub Copilot,
|
|
199
|
+
// and overrides of base/SDK-surface functions.
|
|
200
|
+
// Generic infrastructure (file utils, marketplace base, SDK CLI resolution) is
|
|
201
|
+
// provided by the compiler base and SDK surface layers.
|
|
202
|
+
|
|
10
203
|
const LEGACY_HOOK_SCRIPT_NAMES = [
|
|
11
204
|
'session-start.sh',
|
|
12
205
|
'stop-hook.sh',
|
|
13
206
|
'user-prompt-submit.sh',
|
|
14
207
|
];
|
|
15
208
|
const HOOK_SCRIPT_NAMES = [
|
|
16
|
-
'session-start.sh',
|
|
17
|
-
'session-start.ps1',
|
|
18
|
-
'session-end.sh',
|
|
19
|
-
'session-end.ps1',
|
|
20
|
-
'user-prompt-submitted.sh',
|
|
21
|
-
'user-prompt-submitted.ps1',
|
|
209
|
+
'babysitter-proxied-session-start.sh',
|
|
210
|
+
'babysitter-proxied-session-start.ps1',
|
|
211
|
+
'babysitter-proxied-session-end.sh',
|
|
212
|
+
'babysitter-proxied-session-end.ps1',
|
|
213
|
+
'babysitter-proxied-user-prompt-submitted.sh',
|
|
214
|
+
'babysitter-proxied-user-prompt-submitted.ps1',
|
|
22
215
|
];
|
|
23
216
|
const DEFAULT_MARKETPLACE = {
|
|
24
217
|
name: 'local-plugins',
|
|
@@ -52,115 +245,13 @@ const CLOUD_AGENT_BUNDLE_ENTRIES = [
|
|
|
52
245
|
const MANAGED_BLOCK_START = '<!-- BEGIN BABYSITTER GITHUB CLOUD AGENT -->';
|
|
53
246
|
const MANAGED_BLOCK_END = '<!-- END BABYSITTER GITHUB CLOUD AGENT -->';
|
|
54
247
|
|
|
248
|
+
// --- Harness-specific functions ---
|
|
249
|
+
|
|
55
250
|
function getCopilotHome() {
|
|
56
251
|
if (process.env.COPILOT_HOME) return path.resolve(process.env.COPILOT_HOME);
|
|
57
252
|
return path.join(os.homedir(), '.copilot');
|
|
58
253
|
}
|
|
59
254
|
|
|
60
|
-
function getUserHome() {
|
|
61
|
-
if (process.env.USERPROFILE) return path.resolve(process.env.USERPROFILE);
|
|
62
|
-
if (process.env.HOME) return path.resolve(process.env.HOME);
|
|
63
|
-
return os.homedir();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function getGlobalStateDir() {
|
|
67
|
-
if (process.env.BABYSITTER_GLOBAL_STATE_DIR) {
|
|
68
|
-
return path.resolve(process.env.BABYSITTER_GLOBAL_STATE_DIR);
|
|
69
|
-
}
|
|
70
|
-
return path.join(getUserHome(), '.a5c');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function getHomePluginRoot() {
|
|
74
|
-
if (process.env.BABYSITTER_GITHUB_PLUGIN_DIR) {
|
|
75
|
-
return path.resolve(process.env.BABYSITTER_GITHUB_PLUGIN_DIR, PLUGIN_NAME);
|
|
76
|
-
}
|
|
77
|
-
return path.join(getCopilotHome(), 'plugins', PLUGIN_NAME);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getHomeMarketplacePath() {
|
|
81
|
-
if (process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH) {
|
|
82
|
-
return path.resolve(process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH);
|
|
83
|
-
}
|
|
84
|
-
return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function writeFileIfChanged(filePath, contents) {
|
|
88
|
-
if (fs.existsSync(filePath)) {
|
|
89
|
-
const current = fs.readFileSync(filePath, 'utf8');
|
|
90
|
-
if (current === contents) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
95
|
-
fs.writeFileSync(filePath, contents, 'utf8');
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function copyRecursive(src, dest) {
|
|
100
|
-
const stat = fs.statSync(src);
|
|
101
|
-
if (stat.isDirectory()) {
|
|
102
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
103
|
-
for (const entry of fs.readdirSync(src)) {
|
|
104
|
-
if (['node_modules', '.git', 'test', '.a5c'].includes(entry)) continue;
|
|
105
|
-
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
106
|
-
}
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (path.basename(src) === 'SKILL.md') {
|
|
111
|
-
const file = fs.readFileSync(src);
|
|
112
|
-
const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
|
|
113
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
114
|
-
fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
119
|
-
fs.copyFileSync(src, dest);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function copyPluginBundle(packageRoot, pluginRoot) {
|
|
123
|
-
if (path.resolve(packageRoot) === path.resolve(pluginRoot)) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
127
|
-
fs.mkdirSync(pluginRoot, { recursive: true });
|
|
128
|
-
for (const entry of PLUGIN_BUNDLE_ENTRIES) {
|
|
129
|
-
const src = path.join(packageRoot, entry);
|
|
130
|
-
if (fs.existsSync(src)) {
|
|
131
|
-
copyRecursive(src, path.join(pluginRoot, entry));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function ensureExecutable(filePath) {
|
|
137
|
-
try {
|
|
138
|
-
fs.chmodSync(filePath, 0o755);
|
|
139
|
-
} catch {
|
|
140
|
-
// Best-effort only. Windows and some filesystems may ignore mode changes.
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
|
|
145
|
-
let next = pluginSourcePath;
|
|
146
|
-
if (path.isAbsolute(next)) {
|
|
147
|
-
next = path.relative(path.dirname(marketplacePath), next);
|
|
148
|
-
}
|
|
149
|
-
next = String(next || '').replace(/\\/g, '/');
|
|
150
|
-
if (!next.startsWith('./') && !next.startsWith('../')) {
|
|
151
|
-
next = `./${next}`;
|
|
152
|
-
}
|
|
153
|
-
return next;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function readJson(filePath) {
|
|
157
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function writeJson(filePath, value) {
|
|
161
|
-
writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
255
|
function replaceManagedMarkdownBlock(existing, block) {
|
|
165
256
|
const normalized = String(existing || '').replace(/\r\n/g, '\n');
|
|
166
257
|
const managedBlock = `${MANAGED_BLOCK_START}\n${block.trim()}\n${MANAGED_BLOCK_END}`;
|
|
@@ -234,52 +325,6 @@ function rewriteCloudSkill(skillId, contents) {
|
|
|
234
325
|
return next.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
235
326
|
}
|
|
236
327
|
|
|
237
|
-
function ensureMarketplaceEntry(marketplacePath, pluginSourcePath) {
|
|
238
|
-
const marketplace = fs.existsSync(marketplacePath)
|
|
239
|
-
? readJson(marketplacePath)
|
|
240
|
-
: { ...DEFAULT_MARKETPLACE, plugins: [] };
|
|
241
|
-
marketplace.name = marketplace.name || DEFAULT_MARKETPLACE.name;
|
|
242
|
-
marketplace.interface = marketplace.interface || {};
|
|
243
|
-
marketplace.interface.displayName =
|
|
244
|
-
marketplace.interface.displayName || DEFAULT_MARKETPLACE.interface.displayName;
|
|
245
|
-
const nextEntry = {
|
|
246
|
-
name: PLUGIN_NAME,
|
|
247
|
-
source: {
|
|
248
|
-
source: 'local',
|
|
249
|
-
path: normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath),
|
|
250
|
-
},
|
|
251
|
-
policy: {
|
|
252
|
-
installation: 'AVAILABLE',
|
|
253
|
-
authentication: 'ON_INSTALL',
|
|
254
|
-
},
|
|
255
|
-
category: PLUGIN_CATEGORY,
|
|
256
|
-
};
|
|
257
|
-
const existingIndex = Array.isArray(marketplace.plugins)
|
|
258
|
-
? marketplace.plugins.findIndex((entry) => entry && entry.name === PLUGIN_NAME)
|
|
259
|
-
: -1;
|
|
260
|
-
if (!Array.isArray(marketplace.plugins)) {
|
|
261
|
-
marketplace.plugins = [nextEntry];
|
|
262
|
-
} else if (existingIndex >= 0) {
|
|
263
|
-
marketplace.plugins[existingIndex] = nextEntry;
|
|
264
|
-
} else {
|
|
265
|
-
marketplace.plugins.push(nextEntry);
|
|
266
|
-
}
|
|
267
|
-
writeJson(marketplacePath, marketplace);
|
|
268
|
-
return nextEntry;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function removeMarketplaceEntry(marketplacePath) {
|
|
272
|
-
if (!fs.existsSync(marketplacePath)) {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const marketplace = readJson(marketplacePath);
|
|
276
|
-
if (!Array.isArray(marketplace.plugins)) {
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
marketplace.plugins = marketplace.plugins.filter((entry) => entry && entry.name !== PLUGIN_NAME);
|
|
280
|
-
writeJson(marketplacePath, marketplace);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
328
|
/**
|
|
284
329
|
* Registers the plugin in ~/.copilot/config.json.
|
|
285
330
|
*/
|
|
@@ -409,8 +454,9 @@ function installManagedHooks(packageRoot, copilotHome) {
|
|
|
409
454
|
mergeManagedHooksConfig(packageRoot, copilotHome);
|
|
410
455
|
}
|
|
411
456
|
|
|
412
|
-
function
|
|
413
|
-
|
|
457
|
+
function removeManagedHooks(copilotHome) {
|
|
458
|
+
const managedHookNames = [...LEGACY_HOOK_SCRIPT_NAMES, ...HOOK_SCRIPT_NAMES];
|
|
459
|
+
for (const hookName of managedHookNames) {
|
|
414
460
|
fs.rmSync(path.join(copilotHome, 'hooks', hookName), { force: true });
|
|
415
461
|
}
|
|
416
462
|
|
|
@@ -431,10 +477,17 @@ function removeLegacyHooks(copilotHome) {
|
|
|
431
477
|
const eventHooks = Array.isArray(hooksConfig.hooks[eventName]) ? hooksConfig.hooks[eventName] : [];
|
|
432
478
|
const filteredMatchers = eventHooks
|
|
433
479
|
.map((matcher) => {
|
|
480
|
+
const directBash = String(matcher?.bash || matcher?.command || '');
|
|
481
|
+
const directPs = String(matcher?.powershell || '');
|
|
482
|
+
const hasDirectHook = directBash.length > 0 || directPs.length > 0;
|
|
483
|
+
const directIsManaged = managedHookNames.some((name) => directBash.includes(name) || directPs.includes(name));
|
|
484
|
+
if (hasDirectHook) {
|
|
485
|
+
return directIsManaged ? null : matcher;
|
|
486
|
+
}
|
|
434
487
|
const hooks = Array.isArray(matcher.hooks) ? matcher.hooks : [];
|
|
435
488
|
const keptHooks = hooks.filter((hook) => {
|
|
436
489
|
const command = String(hook.command || '');
|
|
437
|
-
return !
|
|
490
|
+
return !managedHookNames.some((name) => command.includes(name));
|
|
438
491
|
});
|
|
439
492
|
return keptHooks.length > 0 ? { ...matcher, hooks: keptHooks } : null;
|
|
440
493
|
})
|
|
@@ -453,11 +506,13 @@ function removeLegacyHooks(copilotHome) {
|
|
|
453
506
|
}
|
|
454
507
|
|
|
455
508
|
function installCopilotSurface(packageRoot, copilotHome) {
|
|
456
|
-
|
|
509
|
+
removeManagedHooks(copilotHome);
|
|
457
510
|
installManagedSkills(packageRoot, copilotHome);
|
|
458
511
|
installManagedHooks(packageRoot, copilotHome);
|
|
459
512
|
}
|
|
460
513
|
|
|
514
|
+
const removeLegacyHooks = removeManagedHooks;
|
|
515
|
+
|
|
461
516
|
function renderCloudAgentAgentsBlock() {
|
|
462
517
|
return [
|
|
463
518
|
'## Babysitter Cloud Agent',
|
|
@@ -619,60 +674,82 @@ function installCloudAgentSurface(packageRoot, workspaceRoot) {
|
|
|
619
674
|
};
|
|
620
675
|
}
|
|
621
676
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
677
|
+
// --- Overrides of base functions ---
|
|
678
|
+
|
|
679
|
+
function writeJson(filePath, value) {
|
|
680
|
+
writeFileIfChanged(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath) {
|
|
684
|
+
let next = pluginSourcePath;
|
|
685
|
+
if (path.isAbsolute(next)) {
|
|
686
|
+
next = path.relative(path.dirname(marketplacePath), next);
|
|
628
687
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
argsPrefix: [
|
|
633
|
-
require.resolve('@a5c-ai/babysitter-sdk/dist/cli/main.js', {
|
|
634
|
-
paths: [packageRoot],
|
|
635
|
-
}),
|
|
636
|
-
],
|
|
637
|
-
};
|
|
638
|
-
} catch {
|
|
639
|
-
return {
|
|
640
|
-
command: 'babysitter',
|
|
641
|
-
argsPrefix: [],
|
|
642
|
-
};
|
|
688
|
+
next = String(next || '').replace(/\\/g, '/');
|
|
689
|
+
if (!next.startsWith('./') && !next.startsWith('../')) {
|
|
690
|
+
next = `./${next}`;
|
|
643
691
|
}
|
|
692
|
+
return next;
|
|
644
693
|
}
|
|
645
694
|
|
|
646
|
-
function
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
695
|
+
function getHomePluginRoot() {
|
|
696
|
+
if (process.env.BABYSITTER_GITHUB_PLUGIN_DIR) {
|
|
697
|
+
return path.resolve(process.env.BABYSITTER_GITHUB_PLUGIN_DIR, PLUGIN_NAME);
|
|
698
|
+
}
|
|
699
|
+
return path.join(getCopilotHome(), 'plugins', PLUGIN_NAME);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function getHomeMarketplacePath() {
|
|
703
|
+
if (process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH) {
|
|
704
|
+
return path.resolve(process.env.BABYSITTER_GITHUB_MARKETPLACE_PATH);
|
|
705
|
+
}
|
|
706
|
+
return path.join(getUserHome(), '.agents', 'plugins', 'marketplace.json');
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function ensureMarketplaceEntry(marketplacePath, pluginSourcePath) {
|
|
710
|
+
const marketplace = fs.existsSync(marketplacePath)
|
|
711
|
+
? readJson(marketplacePath)
|
|
712
|
+
: { ...DEFAULT_MARKETPLACE, plugins: [] };
|
|
713
|
+
marketplace.name = marketplace.name || DEFAULT_MARKETPLACE.name;
|
|
714
|
+
marketplace.interface = marketplace.interface || {};
|
|
715
|
+
marketplace.interface.displayName =
|
|
716
|
+
marketplace.interface.displayName || DEFAULT_MARKETPLACE.interface.displayName;
|
|
717
|
+
const nextEntry = {
|
|
718
|
+
name: PLUGIN_NAME,
|
|
719
|
+
source: {
|
|
720
|
+
source: 'local',
|
|
721
|
+
path: normalizeMarketplaceSourcePath(marketplacePath, pluginSourcePath),
|
|
655
722
|
},
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
)
|
|
723
|
+
policy: {
|
|
724
|
+
installation: 'AVAILABLE',
|
|
725
|
+
authentication: 'ON_INSTALL',
|
|
726
|
+
},
|
|
727
|
+
category: PLUGIN_CATEGORY,
|
|
728
|
+
};
|
|
729
|
+
const existingIndex = Array.isArray(marketplace.plugins)
|
|
730
|
+
? marketplace.plugins.findIndex((entry) => entry && entry.name === PLUGIN_NAME)
|
|
731
|
+
: -1;
|
|
732
|
+
if (!Array.isArray(marketplace.plugins)) {
|
|
733
|
+
marketplace.plugins = [nextEntry];
|
|
734
|
+
} else if (existingIndex >= 0) {
|
|
735
|
+
marketplace.plugins[existingIndex] = nextEntry;
|
|
736
|
+
} else {
|
|
737
|
+
marketplace.plugins.push(nextEntry);
|
|
664
738
|
}
|
|
665
|
-
|
|
739
|
+
writeJson(marketplacePath, marketplace);
|
|
740
|
+
return nextEntry;
|
|
666
741
|
}
|
|
667
742
|
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
743
|
+
function removeMarketplaceEntry(marketplacePath) {
|
|
744
|
+
if (!fs.existsSync(marketplacePath)) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const marketplace = readJson(marketplacePath);
|
|
748
|
+
if (!Array.isArray(marketplace.plugins)) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
marketplace.plugins = marketplace.plugins.filter((entry) => entry && entry.name !== PLUGIN_NAME);
|
|
752
|
+
writeJson(marketplacePath, marketplace);
|
|
676
753
|
}
|
|
677
754
|
|
|
678
755
|
function warnWindowsHooks() {
|
|
@@ -683,19 +760,125 @@ function warnWindowsHooks() {
|
|
|
683
760
|
console.warn(`[${PLUGIN_NAME}] Both bash (.sh) and PowerShell (.ps1) hook scripts are included.`);
|
|
684
761
|
}
|
|
685
762
|
|
|
763
|
+
function copyPluginBundle(packageRoot, pluginRoot) {
|
|
764
|
+
if (path.resolve(packageRoot) === path.resolve(pluginRoot)) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
768
|
+
fs.mkdirSync(pluginRoot, { recursive: true });
|
|
769
|
+
for (const entry of PLUGIN_BUNDLE_ENTRIES) {
|
|
770
|
+
const src = path.join(packageRoot, entry);
|
|
771
|
+
if (fs.existsSync(src)) {
|
|
772
|
+
copyRecursive(src, path.join(pluginRoot, entry));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function copyRecursive(src, dest) {
|
|
778
|
+
const stat = fs.statSync(src);
|
|
779
|
+
if (stat.isDirectory()) {
|
|
780
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
781
|
+
for (const entry of fs.readdirSync(src)) {
|
|
782
|
+
if (['node_modules', '.git', 'test', '.a5c'].includes(entry)) continue;
|
|
783
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
784
|
+
}
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (path.basename(src) === 'SKILL.md') {
|
|
789
|
+
const file = fs.readFileSync(src);
|
|
790
|
+
const hasBom = file.length >= 3 && file[0] === 0xef && file[1] === 0xbb && file[2] === 0xbf;
|
|
791
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
792
|
+
fs.writeFileSync(dest, hasBom ? file.subarray(3) : file);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
797
|
+
fs.copyFileSync(src, dest);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function harnessCliRoute(argv, packageRoot, runNodeScript) {
|
|
801
|
+
if (argv.includes('--cloud-agent')) {
|
|
802
|
+
const args = argv.filter(a => a !== '--cloud-agent');
|
|
803
|
+
args.push('--cloud-agent');
|
|
804
|
+
runNodeScript(path.join(packageRoot, 'bin', 'install.js'), args);
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function harnessInstall(packageRoot, _pluginRoot) {
|
|
811
|
+
const argv = process.argv.slice(2);
|
|
812
|
+
if (!argv.includes('--cloud-agent')) return;
|
|
813
|
+
const workspaceIdx = argv.indexOf('--workspace');
|
|
814
|
+
const workspaceRoot = (workspaceIdx >= 0 && argv[workspaceIdx + 1])
|
|
815
|
+
? path.resolve(argv[workspaceIdx + 1])
|
|
816
|
+
: process.cwd();
|
|
817
|
+
console.log(`[${PLUGIN_NAME}] Installing cloud-agent support into ${workspaceRoot}`);
|
|
818
|
+
const activeProcessLibrary = runCli(packageRoot, [
|
|
819
|
+
'process-library:active',
|
|
820
|
+
'--json',
|
|
821
|
+
], { stdio: 'pipe' });
|
|
822
|
+
if (activeProcessLibrary.status !== 0) {
|
|
823
|
+
ensureGlobalProcessLibrary(packageRoot);
|
|
824
|
+
}
|
|
825
|
+
installCloudAgentSurface(packageRoot, workspaceRoot);
|
|
826
|
+
console.log(`[${PLUGIN_NAME}] Cloud-agent installation complete!`);
|
|
827
|
+
process.exit(0);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
|
|
686
831
|
module.exports = {
|
|
687
|
-
|
|
688
|
-
|
|
832
|
+
PLUGIN_NAME,
|
|
833
|
+
PLUGIN_CATEGORY,
|
|
834
|
+
getUserHome,
|
|
835
|
+
getHarnessHome,
|
|
836
|
+
writeFileIfChanged,
|
|
837
|
+
readJson,
|
|
838
|
+
ensureExecutable,
|
|
839
|
+
runPostInstall,
|
|
840
|
+
getGlobalStateDir,
|
|
841
|
+
resolveCliCommand,
|
|
842
|
+
runCli,
|
|
689
843
|
ensureGlobalProcessLibrary,
|
|
844
|
+
PLUGIN_BUNDLE_ENTRIES,
|
|
845
|
+
copyRecursive,
|
|
846
|
+
copyPluginBundle,
|
|
847
|
+
DEFAULT_MARKETPLACE,
|
|
848
|
+
normalizeMarketplaceSourcePath,
|
|
690
849
|
ensureMarketplaceEntry,
|
|
850
|
+
removeMarketplaceEntry,
|
|
851
|
+
installManagedSkills,
|
|
852
|
+
mergeManagedHooksConfig,
|
|
853
|
+
installManagedHooks,
|
|
854
|
+
warnWindowsHooks,
|
|
855
|
+
LEGACY_HOOK_SCRIPT_NAMES,
|
|
856
|
+
HOOK_SCRIPT_NAMES,
|
|
857
|
+
CLOUD_AGENT_BUNDLE_ENTRIES,
|
|
858
|
+
MANAGED_BLOCK_START,
|
|
859
|
+
MANAGED_BLOCK_END,
|
|
691
860
|
getCopilotHome,
|
|
692
|
-
getHomeMarketplacePath,
|
|
693
861
|
getHomePluginRoot,
|
|
694
|
-
|
|
695
|
-
|
|
862
|
+
getHomeMarketplacePath,
|
|
863
|
+
writeJson,
|
|
864
|
+
replaceManagedMarkdownBlock,
|
|
865
|
+
writeManagedMarkdown,
|
|
866
|
+
readSdkVersion,
|
|
867
|
+
toLowerHyphenName,
|
|
868
|
+
rewriteCloudSkill,
|
|
696
869
|
registerCopilotPlugin,
|
|
870
|
+
deregisterCopilotPlugin,
|
|
697
871
|
removeLegacyHooks,
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
872
|
+
removeManagedHooks,
|
|
873
|
+
installCopilotSurface,
|
|
874
|
+
renderCloudAgentAgentsBlock,
|
|
875
|
+
renderCloudAgentCopilotInstructionsBlock,
|
|
876
|
+
renderCloudAgentSetupWorkflow,
|
|
877
|
+
installCloudAgentBundle,
|
|
878
|
+
installCloudAgentSkills,
|
|
879
|
+
installCloudAgentInstructions,
|
|
880
|
+
installCloudAgentSetupSteps,
|
|
881
|
+
installCloudAgentSurface,
|
|
882
|
+
harnessCliRoute,
|
|
883
|
+
harnessInstall,
|
|
701
884
|
};
|