opencode-agenthub 0.1.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.
- package/LICENSE +21 -0
- package/README.md +373 -0
- package/dist/composer/bootstrap.js +493 -0
- package/dist/composer/builtin-assets.js +139 -0
- package/dist/composer/capabilities.js +20 -0
- package/dist/composer/compose.js +824 -0
- package/dist/composer/defaults.js +10 -0
- package/dist/composer/home-transfer.js +288 -0
- package/dist/composer/install-home.js +5 -0
- package/dist/composer/library/README.md +93 -0
- package/dist/composer/library/bundles/auto.json +18 -0
- package/dist/composer/library/bundles/build.json +17 -0
- package/dist/composer/library/bundles/hr-adapter.json +26 -0
- package/dist/composer/library/bundles/hr-cto.json +24 -0
- package/dist/composer/library/bundles/hr-evaluator.json +26 -0
- package/dist/composer/library/bundles/hr-planner.json +26 -0
- package/dist/composer/library/bundles/hr-sourcer.json +24 -0
- package/dist/composer/library/bundles/hr-verifier.json +26 -0
- package/dist/composer/library/bundles/hr.json +35 -0
- package/dist/composer/library/bundles/plan.json +19 -0
- package/dist/composer/library/instructions/hr-boundaries.md +38 -0
- package/dist/composer/library/instructions/hr-protocol.md +102 -0
- package/dist/composer/library/profiles/auto.json +9 -0
- package/dist/composer/library/profiles/hr.json +9 -0
- package/dist/composer/library/souls/auto.md +29 -0
- package/dist/composer/library/souls/build.md +21 -0
- package/dist/composer/library/souls/hr-adapter.md +64 -0
- package/dist/composer/library/souls/hr-cto.md +57 -0
- package/dist/composer/library/souls/hr-evaluator.md +64 -0
- package/dist/composer/library/souls/hr-planner.md +48 -0
- package/dist/composer/library/souls/hr-sourcer.md +70 -0
- package/dist/composer/library/souls/hr-verifier.md +62 -0
- package/dist/composer/library/souls/hr.md +186 -0
- package/dist/composer/library/souls/plan.md +23 -0
- package/dist/composer/library/workflow/auto-mode.json +139 -0
- package/dist/composer/model-utils.js +39 -0
- package/dist/composer/opencode-profile.js +2299 -0
- package/dist/composer/package-manager.js +75 -0
- package/dist/composer/package-version.js +20 -0
- package/dist/composer/platform.js +48 -0
- package/dist/composer/query.js +133 -0
- package/dist/composer/settings.js +400 -0
- package/dist/plugins/opencode-agenthub.js +310 -0
- package/dist/plugins/opencode-question.js +223 -0
- package/dist/plugins/plan-guidance.js +263 -0
- package/dist/plugins/runtime-config.js +57 -0
- package/dist/skills/agenthub-doctor/SKILL.md +238 -0
- package/dist/skills/agenthub-doctor/diagnose.js +213 -0
- package/dist/skills/agenthub-doctor/fix.js +293 -0
- package/dist/skills/agenthub-doctor/index.js +30 -0
- package/dist/skills/agenthub-doctor/interactive.js +756 -0
- package/dist/skills/hr-assembly/SKILL.md +121 -0
- package/dist/skills/hr-final-check/SKILL.md +98 -0
- package/dist/skills/hr-review/SKILL.md +100 -0
- package/dist/skills/hr-staffing/SKILL.md +85 -0
- package/dist/skills/hr-support/bin/sync_sources.py +560 -0
- package/dist/skills/hr-support/bin/validate_staged_package.py +290 -0
- package/dist/skills/hr-support/bin/vendor_stage_mcps.py +234 -0
- package/dist/skills/hr-support/bin/vendor_stage_skills.py +104 -0
- package/dist/types.js +11 -0
- package/package.json +54 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { chmod, cp, mkdir, readdir, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
5
|
+
import readline from "node:readline/promises";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { installPackageDependencies } from "./package-manager.js";
|
|
8
|
+
import {
|
|
9
|
+
buildBuiltinVersionManifest,
|
|
10
|
+
getManagedCodingHrHubAssetSpecs
|
|
11
|
+
} from "./builtin-assets.js";
|
|
12
|
+
import { readPackageVersion } from "./package-version.js";
|
|
13
|
+
import { resolveHomeConfigRoot, shouldChmod } from "./platform.js";
|
|
14
|
+
import {
|
|
15
|
+
buildInitialAgentHubSettings,
|
|
16
|
+
resolveHrBootstrapAgentModels,
|
|
17
|
+
mergeAgentHubSettingsDefaults,
|
|
18
|
+
readAgentHubSettings,
|
|
19
|
+
writeAgentHubSettings
|
|
20
|
+
} from "./settings.js";
|
|
21
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const libraryRoot = path.join(currentDir, "library");
|
|
23
|
+
const sourceRoot = path.dirname(currentDir);
|
|
24
|
+
const defaultHrGithubSources = [
|
|
25
|
+
"garrytan/gstack",
|
|
26
|
+
"anthropics/skills",
|
|
27
|
+
"msitarzewski/agency-agents",
|
|
28
|
+
"obra/superpowers"
|
|
29
|
+
];
|
|
30
|
+
const syncManagedAssetSpecs = async (specs, version, options = {}) => {
|
|
31
|
+
const added = [];
|
|
32
|
+
const updated = [];
|
|
33
|
+
const skipped = [];
|
|
34
|
+
const manifest = {};
|
|
35
|
+
for (const spec of specs) {
|
|
36
|
+
const exists = await pathExists(spec.target);
|
|
37
|
+
manifest[spec.manifestKey] = version;
|
|
38
|
+
if (exists && !options.force) {
|
|
39
|
+
skipped.push(spec.manifestKey);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!options.dryRun) {
|
|
43
|
+
await mkdir(path.dirname(spec.target), { recursive: true });
|
|
44
|
+
await cp(spec.source, spec.target, {
|
|
45
|
+
recursive: Boolean(spec.recursive),
|
|
46
|
+
force: true
|
|
47
|
+
});
|
|
48
|
+
if (spec.executable && shouldChmod()) {
|
|
49
|
+
await chmod(spec.target, 493);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
(exists ? updated : added).push(spec.manifestKey);
|
|
53
|
+
}
|
|
54
|
+
return { added, updated, skipped, manifest };
|
|
55
|
+
};
|
|
56
|
+
const mergeBuiltinManifest = (existing, next) => ({
|
|
57
|
+
...existing || {},
|
|
58
|
+
...next
|
|
59
|
+
});
|
|
60
|
+
const pruneBuiltinManifest = (existing, allowedKeys) => {
|
|
61
|
+
if (!existing) return void 0;
|
|
62
|
+
const allowed = new Set(allowedKeys);
|
|
63
|
+
const pruned = Object.fromEntries(
|
|
64
|
+
Object.entries(existing).filter(([key]) => allowed.has(key))
|
|
65
|
+
);
|
|
66
|
+
return Object.keys(pruned).length > 0 ? pruned : void 0;
|
|
67
|
+
};
|
|
68
|
+
const withBuiltinManifest = (settings, manifest) => ({
|
|
69
|
+
...settings,
|
|
70
|
+
meta: {
|
|
71
|
+
...settings.meta,
|
|
72
|
+
builtinVersion: mergeBuiltinManifest(settings.meta?.builtinVersion, manifest)
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const withBuiltinManifestForMode = (settings, manifest, mode) => ({
|
|
76
|
+
...settings,
|
|
77
|
+
meta: {
|
|
78
|
+
...settings.meta,
|
|
79
|
+
builtinVersion: mergeBuiltinManifest(
|
|
80
|
+
pruneBuiltinManifest(
|
|
81
|
+
settings.meta?.builtinVersion,
|
|
82
|
+
Object.keys(buildBuiltinVersionManifest(mode, readPackageVersion()))
|
|
83
|
+
),
|
|
84
|
+
manifest
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
const installCodingStarter = async (targetRoot, version) => syncManagedAssetSpecs(
|
|
89
|
+
getManagedCodingHrHubAssetSpecs(targetRoot, "auto"),
|
|
90
|
+
version
|
|
91
|
+
);
|
|
92
|
+
const installHrOfficeStarter = async (targetRoot, version) => syncManagedAssetSpecs(
|
|
93
|
+
getManagedCodingHrHubAssetSpecs(targetRoot, "hr-office"),
|
|
94
|
+
version
|
|
95
|
+
);
|
|
96
|
+
const installHrHelperScripts = async (hrRoot) => {
|
|
97
|
+
const scriptsRoot = path.join(sourceRoot, "skills", "hr-support", "bin");
|
|
98
|
+
const targetBinRoot = path.join(hrRoot, "bin");
|
|
99
|
+
await mkdir(targetBinRoot, { recursive: true });
|
|
100
|
+
for (const scriptName of [
|
|
101
|
+
"sync_sources.py",
|
|
102
|
+
"vendor_stage_skills.py",
|
|
103
|
+
"validate_staged_package.py"
|
|
104
|
+
]) {
|
|
105
|
+
const source = path.join(scriptsRoot, scriptName);
|
|
106
|
+
const target = path.join(targetBinRoot, scriptName);
|
|
107
|
+
if (!await pathExists(target)) {
|
|
108
|
+
await cp(source, target, { force: true });
|
|
109
|
+
if (shouldChmod()) {
|
|
110
|
+
await chmod(target, 493);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const defaultAgentHubHome = () => process.env.OPENCODE_AGENTHUB_HOME || resolveHomeConfigRoot(os.homedir(), "opencode-agenthub");
|
|
116
|
+
const defaultHrHome = () => process.env.OPENCODE_AGENTHUB_HR_HOME || resolveHomeConfigRoot(os.homedir(), "opencode-agenthub-hr");
|
|
117
|
+
const pathExists = async (target) => {
|
|
118
|
+
try {
|
|
119
|
+
await stat(target);
|
|
120
|
+
return true;
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const ensureAgentHubSkeleton = async (targetRoot) => {
|
|
126
|
+
await mkdir(targetRoot, { recursive: true });
|
|
127
|
+
await mkdir(path.join(targetRoot, "souls"), { recursive: true });
|
|
128
|
+
await mkdir(path.join(targetRoot, "skills"), { recursive: true });
|
|
129
|
+
await mkdir(path.join(targetRoot, "bundles"), { recursive: true });
|
|
130
|
+
await mkdir(path.join(targetRoot, "profiles"), { recursive: true });
|
|
131
|
+
};
|
|
132
|
+
const ensureHrHomeSkeleton = async (hrRoot) => {
|
|
133
|
+
await mkdir(hrRoot, { recursive: true });
|
|
134
|
+
await mkdir(path.join(hrRoot, "bin"), { recursive: true });
|
|
135
|
+
await mkdir(path.join(hrRoot, "inventory", "workers"), { recursive: true });
|
|
136
|
+
await mkdir(path.join(hrRoot, "inventory", "models"), { recursive: true });
|
|
137
|
+
await mkdir(path.join(hrRoot, "sources", "github"), { recursive: true });
|
|
138
|
+
await mkdir(path.join(hrRoot, "staging"), { recursive: true });
|
|
139
|
+
await mkdir(path.join(hrRoot, "output"), { recursive: true });
|
|
140
|
+
await mkdir(path.join(hrRoot, "logs"), { recursive: true });
|
|
141
|
+
await mkdir(path.join(hrRoot, "state", "staffing-plans"), { recursive: true });
|
|
142
|
+
await mkdir(path.join(hrRoot, "state", "architecture-reviews"), { recursive: true });
|
|
143
|
+
const configPath = path.join(hrRoot, "hr-config.json");
|
|
144
|
+
if (!await pathExists(configPath)) {
|
|
145
|
+
const defaultConfig = {
|
|
146
|
+
schema_version: "1.1",
|
|
147
|
+
sources: {
|
|
148
|
+
github: defaultHrGithubSources,
|
|
149
|
+
models: [
|
|
150
|
+
{
|
|
151
|
+
source_id: "models-dev",
|
|
152
|
+
url: "https://models.dev/api.json",
|
|
153
|
+
format: "models.dev"
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
settings: {
|
|
158
|
+
auto_sync: false,
|
|
159
|
+
sync_depth: 1
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
await writeFile(configPath, `${JSON.stringify(defaultConfig, null, 2)}
|
|
163
|
+
`, "utf-8");
|
|
164
|
+
}
|
|
165
|
+
await installHrHelperScripts(hrRoot);
|
|
166
|
+
};
|
|
167
|
+
const ensureHrOfficeSkeleton = async (hrRoot) => {
|
|
168
|
+
await ensureHrHomeSkeleton(hrRoot);
|
|
169
|
+
await ensureAgentHubSkeleton(hrRoot);
|
|
170
|
+
};
|
|
171
|
+
const hrHomeInitialized = async (hrRoot = defaultHrHome()) => {
|
|
172
|
+
const [libraryOk, configOk, profileOk, bundleOk, settingsOk] = await Promise.all([
|
|
173
|
+
agentHubHomeInitialized(hrRoot),
|
|
174
|
+
pathExists(path.join(hrRoot, "hr-config.json")),
|
|
175
|
+
pathExists(path.join(hrRoot, "profiles", "hr.json")),
|
|
176
|
+
pathExists(path.join(hrRoot, "bundles", "hr.json")),
|
|
177
|
+
pathExists(path.join(hrRoot, "settings.json"))
|
|
178
|
+
]);
|
|
179
|
+
return libraryOk && configOk && profileOk && bundleOk && settingsOk;
|
|
180
|
+
};
|
|
181
|
+
const installHrOfficeHome = async (hrRoot = defaultHrHome()) => {
|
|
182
|
+
return installHrOfficeHomeWithOptions({ hrRoot });
|
|
183
|
+
};
|
|
184
|
+
const installHrOfficeHomeWithOptions = async ({
|
|
185
|
+
hrRoot = defaultHrHome(),
|
|
186
|
+
hrModelSelection
|
|
187
|
+
}) => {
|
|
188
|
+
const packageVersion = readPackageVersion();
|
|
189
|
+
await ensureHrOfficeSkeleton(hrRoot);
|
|
190
|
+
await copyLibraryReadme(hrRoot);
|
|
191
|
+
await installHrOfficeStarter(hrRoot, packageVersion);
|
|
192
|
+
const resolvedHrModels = await resolveHrBootstrapAgentModels({
|
|
193
|
+
targetRoot: hrRoot,
|
|
194
|
+
selection: hrModelSelection
|
|
195
|
+
});
|
|
196
|
+
const existingSettings = await readAgentHubSettings(hrRoot);
|
|
197
|
+
if (!existingSettings) {
|
|
198
|
+
const initialSettings = await buildInitialAgentHubSettings({
|
|
199
|
+
targetRoot: hrRoot,
|
|
200
|
+
mode: "hr-office",
|
|
201
|
+
hrResolvedModels: resolvedHrModels
|
|
202
|
+
});
|
|
203
|
+
await writeAgentHubSettings(
|
|
204
|
+
hrRoot,
|
|
205
|
+
initialSettings
|
|
206
|
+
);
|
|
207
|
+
} else {
|
|
208
|
+
const mergedSettings = mergeAgentHubSettingsDefaults(existingSettings);
|
|
209
|
+
const builtinVersion = buildBuiltinVersionManifest("hr-office", packageVersion);
|
|
210
|
+
mergedSettings.agents = mergedSettings.agents || {};
|
|
211
|
+
for (const [agentName, modelSelection] of Object.entries(resolvedHrModels.agentModels)) {
|
|
212
|
+
const existingAgentSettings = mergedSettings.agents[agentName] || {};
|
|
213
|
+
if (!existingAgentSettings.model || !existingAgentSettings.variant) {
|
|
214
|
+
mergedSettings.agents[agentName] = {
|
|
215
|
+
...existingAgentSettings,
|
|
216
|
+
...existingAgentSettings.model ? {} : { model: modelSelection.model },
|
|
217
|
+
...existingAgentSettings.variant || !modelSelection.variant ? {} : { variant: modelSelection.variant }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
mergedSettings.meta = {
|
|
222
|
+
...mergedSettings.meta,
|
|
223
|
+
onboarding: {
|
|
224
|
+
...mergedSettings.meta?.onboarding,
|
|
225
|
+
modelStrategy: mergedSettings.meta?.onboarding?.modelStrategy || resolvedHrModels.strategy
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
await writeAgentHubSettings(
|
|
229
|
+
hrRoot,
|
|
230
|
+
withBuiltinManifestForMode(mergedSettings, builtinVersion, "hr-office")
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return hrRoot;
|
|
234
|
+
};
|
|
235
|
+
const copyLibraryReadme = async (targetRoot) => {
|
|
236
|
+
const source = path.join(libraryRoot, "README.md");
|
|
237
|
+
const target = path.join(targetRoot, "README.md");
|
|
238
|
+
if (await pathExists(target)) return;
|
|
239
|
+
await cp(source, target);
|
|
240
|
+
};
|
|
241
|
+
const copySkillsFrom = async (sourceRoot2, targetRoot) => {
|
|
242
|
+
const targetSkillsRoot = path.join(targetRoot, "skills");
|
|
243
|
+
await mkdir(targetSkillsRoot, { recursive: true });
|
|
244
|
+
for (const entry of await readdir(sourceRoot2, { withFileTypes: true })) {
|
|
245
|
+
if (!entry.isDirectory()) continue;
|
|
246
|
+
const source = path.join(sourceRoot2, entry.name);
|
|
247
|
+
const target = path.join(targetSkillsRoot, entry.name);
|
|
248
|
+
if (await pathExists(target)) continue;
|
|
249
|
+
await cp(source, target, { recursive: true });
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const copySoulsFrom = async (sourceRoot2, targetRoot) => {
|
|
253
|
+
const targetSoulsRoot = path.join(targetRoot, "souls");
|
|
254
|
+
await mkdir(targetSoulsRoot, { recursive: true });
|
|
255
|
+
for (const entry of await readdir(sourceRoot2, { withFileTypes: true })) {
|
|
256
|
+
if (!entry.isFile() || path.extname(entry.name) !== ".md") continue;
|
|
257
|
+
const source = path.join(sourceRoot2, entry.name);
|
|
258
|
+
const target = path.join(targetSoulsRoot, entry.name);
|
|
259
|
+
if (await pathExists(target)) continue;
|
|
260
|
+
await cp(source, target);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const copyInstructionsFrom = async (sourceRoot2, targetRoot) => {
|
|
264
|
+
const targetInstructionsRoot = path.join(targetRoot, "instructions");
|
|
265
|
+
await mkdir(targetInstructionsRoot, { recursive: true });
|
|
266
|
+
for (const entry of await readdir(sourceRoot2, { withFileTypes: true })) {
|
|
267
|
+
if (!entry.isFile() || path.extname(entry.name) !== ".md") continue;
|
|
268
|
+
const source = path.join(sourceRoot2, entry.name);
|
|
269
|
+
const target = path.join(targetInstructionsRoot, entry.name);
|
|
270
|
+
if (await pathExists(target)) continue;
|
|
271
|
+
await cp(source, target);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const supportedMcpServerExtensions = /* @__PURE__ */ new Set([".ts", ".js", ".mjs", ".cjs"]);
|
|
275
|
+
const mcpServerPackageManifest = "package.json";
|
|
276
|
+
const copyMcpServersFrom = async (sourceRoot2, targetRoot) => {
|
|
277
|
+
const targetMcpServersRoot = path.join(targetRoot, "mcp-servers");
|
|
278
|
+
await mkdir(targetMcpServersRoot, { recursive: true });
|
|
279
|
+
const importedServers = [];
|
|
280
|
+
const sourcePackageManifest = path.join(sourceRoot2, mcpServerPackageManifest);
|
|
281
|
+
const targetPackageManifest = path.join(
|
|
282
|
+
targetMcpServersRoot,
|
|
283
|
+
mcpServerPackageManifest
|
|
284
|
+
);
|
|
285
|
+
if (await pathExists(sourcePackageManifest)) {
|
|
286
|
+
await cp(sourcePackageManifest, targetPackageManifest, { force: true });
|
|
287
|
+
}
|
|
288
|
+
for (const entry of await readdir(sourceRoot2, { withFileTypes: true })) {
|
|
289
|
+
if (!entry.isFile()) continue;
|
|
290
|
+
const extension = path.extname(entry.name);
|
|
291
|
+
if (!supportedMcpServerExtensions.has(extension)) continue;
|
|
292
|
+
const source = path.join(sourceRoot2, entry.name);
|
|
293
|
+
const target = path.join(targetMcpServersRoot, entry.name);
|
|
294
|
+
if (!await pathExists(target)) {
|
|
295
|
+
await cp(source, target);
|
|
296
|
+
}
|
|
297
|
+
importedServers.push({
|
|
298
|
+
name: path.basename(entry.name, extension),
|
|
299
|
+
fileName: entry.name
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return importedServers;
|
|
303
|
+
};
|
|
304
|
+
const registerImportedMcpServers = async (servers, targetRoot) => {
|
|
305
|
+
const targetMcpRoot = path.join(targetRoot, "mcp");
|
|
306
|
+
await mkdir(targetMcpRoot, { recursive: true });
|
|
307
|
+
for (const server of servers) {
|
|
308
|
+
const target = path.join(targetMcpRoot, `${server.name}.json`);
|
|
309
|
+
if (await pathExists(target)) continue;
|
|
310
|
+
const extension = path.extname(server.fileName);
|
|
311
|
+
const scriptPath = `${"$"}{LIBRARY_ROOT}/mcp-servers/${server.fileName}`;
|
|
312
|
+
const payload = {
|
|
313
|
+
type: "local",
|
|
314
|
+
command: extension === ".ts" ? [
|
|
315
|
+
"node",
|
|
316
|
+
"--import",
|
|
317
|
+
`${"$"}{LIBRARY_ROOT}/mcp-servers/node_modules/tsx/dist/loader.mjs`,
|
|
318
|
+
scriptPath
|
|
319
|
+
] : ["node", scriptPath],
|
|
320
|
+
timeout: 3e4
|
|
321
|
+
};
|
|
322
|
+
await writeFile(target, `${JSON.stringify(payload, null, 2)}
|
|
323
|
+
`, "utf-8");
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const installMcpServerDependencies = async (targetRoot) => {
|
|
327
|
+
const targetMcpServersRoot = path.join(targetRoot, "mcp-servers");
|
|
328
|
+
const packageManifest = path.join(
|
|
329
|
+
targetMcpServersRoot,
|
|
330
|
+
mcpServerPackageManifest
|
|
331
|
+
);
|
|
332
|
+
if (!await pathExists(packageManifest)) return;
|
|
333
|
+
await installPackageDependencies(targetMcpServersRoot);
|
|
334
|
+
};
|
|
335
|
+
const installAgentHubHome = async ({
|
|
336
|
+
targetRoot = defaultAgentHubHome(),
|
|
337
|
+
importSoulsPath,
|
|
338
|
+
importInstructionsPath,
|
|
339
|
+
importSkillsPath,
|
|
340
|
+
importMcpServersPath,
|
|
341
|
+
mode = "auto"
|
|
342
|
+
} = {}) => {
|
|
343
|
+
let importedServers = [];
|
|
344
|
+
const packageVersion = readPackageVersion();
|
|
345
|
+
await ensureAgentHubSkeleton(targetRoot);
|
|
346
|
+
await copyLibraryReadme(targetRoot);
|
|
347
|
+
if (mode === "auto") {
|
|
348
|
+
await installCodingStarter(targetRoot, packageVersion);
|
|
349
|
+
}
|
|
350
|
+
if (importSoulsPath) {
|
|
351
|
+
await copySoulsFrom(importSoulsPath, targetRoot);
|
|
352
|
+
}
|
|
353
|
+
if (importInstructionsPath) {
|
|
354
|
+
await copyInstructionsFrom(importInstructionsPath, targetRoot);
|
|
355
|
+
}
|
|
356
|
+
if (importSkillsPath) {
|
|
357
|
+
await copySkillsFrom(importSkillsPath, targetRoot);
|
|
358
|
+
}
|
|
359
|
+
if (importMcpServersPath) {
|
|
360
|
+
importedServers = await copyMcpServersFrom(
|
|
361
|
+
importMcpServersPath,
|
|
362
|
+
targetRoot
|
|
363
|
+
);
|
|
364
|
+
await registerImportedMcpServers(importedServers, targetRoot);
|
|
365
|
+
await installMcpServerDependencies(targetRoot);
|
|
366
|
+
}
|
|
367
|
+
const existingSettings = await readAgentHubSettings(targetRoot);
|
|
368
|
+
if (!existingSettings) {
|
|
369
|
+
await writeAgentHubSettings(
|
|
370
|
+
targetRoot,
|
|
371
|
+
await buildInitialAgentHubSettings({ targetRoot, mode })
|
|
372
|
+
);
|
|
373
|
+
} else {
|
|
374
|
+
const mergedSettings = mergeAgentHubSettingsDefaults(existingSettings);
|
|
375
|
+
const builtinVersion = buildBuiltinVersionManifest(mode, packageVersion);
|
|
376
|
+
await writeAgentHubSettings(
|
|
377
|
+
targetRoot,
|
|
378
|
+
withBuiltinManifestForMode(mergedSettings, builtinVersion, mode)
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return targetRoot;
|
|
382
|
+
};
|
|
383
|
+
const syncBuiltInAgentHubAssets = async ({
|
|
384
|
+
targetRoot = defaultAgentHubHome(),
|
|
385
|
+
mode = "auto",
|
|
386
|
+
force = false,
|
|
387
|
+
dryRun = false
|
|
388
|
+
}) => {
|
|
389
|
+
if (!dryRun) {
|
|
390
|
+
await ensureAgentHubSkeleton(targetRoot);
|
|
391
|
+
}
|
|
392
|
+
const report = await syncManagedAssetSpecs(
|
|
393
|
+
getManagedCodingHrHubAssetSpecs(targetRoot, mode),
|
|
394
|
+
readPackageVersion(),
|
|
395
|
+
{ force, dryRun }
|
|
396
|
+
);
|
|
397
|
+
if (!dryRun) {
|
|
398
|
+
const existingSettings = await readAgentHubSettings(targetRoot);
|
|
399
|
+
if (existingSettings) {
|
|
400
|
+
await writeAgentHubSettings(targetRoot, withBuiltinManifestForMode(existingSettings, report.manifest, mode));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return report;
|
|
404
|
+
};
|
|
405
|
+
const agentHubHomeInitialized = async (targetRoot = defaultAgentHubHome()) => {
|
|
406
|
+
const [soulsOk, skillsOk, bundlesOk, profilesOk] = await Promise.all([
|
|
407
|
+
pathExists(path.join(targetRoot, "souls")),
|
|
408
|
+
pathExists(path.join(targetRoot, "skills")),
|
|
409
|
+
pathExists(path.join(targetRoot, "bundles")),
|
|
410
|
+
pathExists(path.join(targetRoot, "profiles"))
|
|
411
|
+
]);
|
|
412
|
+
return soulsOk && skillsOk && bundlesOk && profilesOk;
|
|
413
|
+
};
|
|
414
|
+
const normalizeOptionalPath = (value) => {
|
|
415
|
+
const trimmed = value.trim();
|
|
416
|
+
if (!trimmed) return void 0;
|
|
417
|
+
return path.resolve(trimmed);
|
|
418
|
+
};
|
|
419
|
+
const promptSetupMode = async (rl) => {
|
|
420
|
+
while (true) {
|
|
421
|
+
process.stdout.write("\nSetup options:\n");
|
|
422
|
+
process.stdout.write(" auto - Full setup with built-in Auto + Plan + Build\n");
|
|
423
|
+
process.stdout.write(" minimal - Structure only\n\n");
|
|
424
|
+
const modeAnswer = await rl.question(
|
|
425
|
+
"Setup [auto/minimal] (recommended: auto): "
|
|
426
|
+
);
|
|
427
|
+
const normalizedMode = modeAnswer.trim().toLowerCase();
|
|
428
|
+
if (!normalizedMode) return "auto";
|
|
429
|
+
if (normalizedMode === "minimal" || normalizedMode === "auto") {
|
|
430
|
+
return normalizedMode;
|
|
431
|
+
}
|
|
432
|
+
process.stdout.write(
|
|
433
|
+
"Invalid setup choice. Type 'auto', 'minimal', or press Enter for the recommended default.\n"
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const promptHubInitAnswers = async () => {
|
|
438
|
+
const rl = readline.createInterface({ input, output });
|
|
439
|
+
try {
|
|
440
|
+
const suggestedRoot = defaultAgentHubHome();
|
|
441
|
+
const locationAnswer = await rl.question(
|
|
442
|
+
`Agent Hub home location [${suggestedRoot}]: `
|
|
443
|
+
);
|
|
444
|
+
const targetRoot = normalizeOptionalPath(locationAnswer) || suggestedRoot;
|
|
445
|
+
const mode = await promptSetupMode(rl);
|
|
446
|
+
let importSoulsPath;
|
|
447
|
+
let importInstructionsPath;
|
|
448
|
+
let importSkillsPath;
|
|
449
|
+
let importMcpServersPath;
|
|
450
|
+
if (mode === "minimal") {
|
|
451
|
+
const importSoulsAnswer = await rl.question(
|
|
452
|
+
"Existing soul/agent prompt folder to import (leave blank to skip): "
|
|
453
|
+
);
|
|
454
|
+
importSoulsPath = normalizeOptionalPath(importSoulsAnswer);
|
|
455
|
+
const importInstructionsAnswer = await rl.question(
|
|
456
|
+
"Existing instructions folder to import (leave blank to skip): "
|
|
457
|
+
);
|
|
458
|
+
importInstructionsPath = normalizeOptionalPath(importInstructionsAnswer);
|
|
459
|
+
const importAnswer = await rl.question(
|
|
460
|
+
"Existing skills folder to import (leave blank to skip): "
|
|
461
|
+
);
|
|
462
|
+
importSkillsPath = normalizeOptionalPath(importAnswer);
|
|
463
|
+
const importMcpAnswer = await rl.question(
|
|
464
|
+
"Existing MCP server folder to import (leave blank to skip): "
|
|
465
|
+
);
|
|
466
|
+
importMcpServersPath = normalizeOptionalPath(importMcpAnswer);
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
targetRoot,
|
|
470
|
+
importSoulsPath,
|
|
471
|
+
importInstructionsPath,
|
|
472
|
+
importSkillsPath,
|
|
473
|
+
importMcpServersPath,
|
|
474
|
+
mode
|
|
475
|
+
};
|
|
476
|
+
} finally {
|
|
477
|
+
rl.close();
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
export {
|
|
481
|
+
agentHubHomeInitialized,
|
|
482
|
+
defaultAgentHubHome,
|
|
483
|
+
defaultHrHome,
|
|
484
|
+
ensureAgentHubSkeleton,
|
|
485
|
+
ensureHrHomeSkeleton,
|
|
486
|
+
ensureHrOfficeSkeleton,
|
|
487
|
+
hrHomeInitialized,
|
|
488
|
+
installAgentHubHome,
|
|
489
|
+
installHrOfficeHome,
|
|
490
|
+
installHrOfficeHomeWithOptions,
|
|
491
|
+
promptHubInitAnswers,
|
|
492
|
+
syncBuiltInAgentHubAssets
|
|
493
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const builtInLibraryRoot = path.join(currentDir, "library");
|
|
6
|
+
const builtInSkillsRoot = path.resolve(currentDir, "..", "skills");
|
|
7
|
+
const hrSupportBinRoot = path.join(builtInSkillsRoot, "hr-support", "bin");
|
|
8
|
+
const codingLibraryFiles = [
|
|
9
|
+
"bundles/auto.json",
|
|
10
|
+
"bundles/plan.json",
|
|
11
|
+
"bundles/build.json",
|
|
12
|
+
"profiles/auto.json",
|
|
13
|
+
"souls/auto.md",
|
|
14
|
+
"souls/plan.md",
|
|
15
|
+
"souls/build.md"
|
|
16
|
+
];
|
|
17
|
+
const hrLibraryFiles = [
|
|
18
|
+
"bundles/hr.json",
|
|
19
|
+
"bundles/hr-planner.json",
|
|
20
|
+
"bundles/hr-sourcer.json",
|
|
21
|
+
"bundles/hr-evaluator.json",
|
|
22
|
+
"bundles/hr-cto.json",
|
|
23
|
+
"bundles/hr-adapter.json",
|
|
24
|
+
"bundles/hr-verifier.json",
|
|
25
|
+
"instructions/hr-boundaries.md",
|
|
26
|
+
"instructions/hr-protocol.md",
|
|
27
|
+
"profiles/hr.json",
|
|
28
|
+
"souls/hr.md",
|
|
29
|
+
"souls/hr-planner.md",
|
|
30
|
+
"souls/hr-sourcer.md",
|
|
31
|
+
"souls/hr-evaluator.md",
|
|
32
|
+
"souls/hr-cto.md",
|
|
33
|
+
"souls/hr-adapter.md",
|
|
34
|
+
"souls/hr-verifier.md"
|
|
35
|
+
];
|
|
36
|
+
const hrSkillDirectories = [
|
|
37
|
+
"hr-staffing",
|
|
38
|
+
"hr-review",
|
|
39
|
+
"hr-assembly",
|
|
40
|
+
"hr-final-check"
|
|
41
|
+
];
|
|
42
|
+
const hrHelperScripts = [
|
|
43
|
+
"sync_sources.py",
|
|
44
|
+
"vendor_stage_skills.py",
|
|
45
|
+
"vendor_stage_mcps.py",
|
|
46
|
+
"validate_staged_package.py"
|
|
47
|
+
];
|
|
48
|
+
const listNamesByExtension = async (root, extension) => {
|
|
49
|
+
try {
|
|
50
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
51
|
+
return new Set(
|
|
52
|
+
entries.filter((entry) => entry.isFile() && entry.name.endsWith(extension)).map((entry) => entry.name.slice(0, -extension.length))
|
|
53
|
+
);
|
|
54
|
+
} catch {
|
|
55
|
+
return /* @__PURE__ */ new Set();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const listDirectoryNames = async (root) => {
|
|
59
|
+
try {
|
|
60
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
61
|
+
return new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
|
|
62
|
+
} catch {
|
|
63
|
+
return /* @__PURE__ */ new Set();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const listBuiltInAssetNames = async (kind) => {
|
|
67
|
+
if (kind === "bundle") {
|
|
68
|
+
return listNamesByExtension(path.join(builtInLibraryRoot, "bundles"), ".json");
|
|
69
|
+
}
|
|
70
|
+
if (kind === "profile") {
|
|
71
|
+
return listNamesByExtension(path.join(builtInLibraryRoot, "profiles"), ".json");
|
|
72
|
+
}
|
|
73
|
+
if (kind === "soul") {
|
|
74
|
+
return listNamesByExtension(path.join(builtInLibraryRoot, "souls"), ".md");
|
|
75
|
+
}
|
|
76
|
+
if (kind === "instruction") {
|
|
77
|
+
return listNamesByExtension(path.join(builtInLibraryRoot, "instructions"), ".md");
|
|
78
|
+
}
|
|
79
|
+
return listDirectoryNames(builtInSkillsRoot);
|
|
80
|
+
};
|
|
81
|
+
const manifestScopeForMode = (mode) => {
|
|
82
|
+
if (mode === "auto") {
|
|
83
|
+
return codingLibraryFiles;
|
|
84
|
+
}
|
|
85
|
+
if (mode === "hr-office") {
|
|
86
|
+
return [
|
|
87
|
+
...hrLibraryFiles,
|
|
88
|
+
...hrSkillDirectories.map((name) => `skills/${name}`),
|
|
89
|
+
...hrHelperScripts.map((name) => `hr-home/bin/${name}`)
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
};
|
|
94
|
+
const getBuiltInManifestKeysForMode = (mode) => manifestScopeForMode(mode);
|
|
95
|
+
const buildBuiltinVersionManifest = (mode, version) => Object.fromEntries(
|
|
96
|
+
getBuiltInManifestKeysForMode(mode).map((manifestKey) => [manifestKey, version])
|
|
97
|
+
);
|
|
98
|
+
const getManagedHubAssetSpecs = (targetRoot, mode) => {
|
|
99
|
+
const manifestKeys = manifestScopeForMode(mode).filter(
|
|
100
|
+
(key) => !key.startsWith("hr-home/") && !key.startsWith("skills/")
|
|
101
|
+
);
|
|
102
|
+
return manifestKeys.map((manifestKey) => ({
|
|
103
|
+
manifestKey,
|
|
104
|
+
source: path.join(builtInLibraryRoot, ...manifestKey.split("/")),
|
|
105
|
+
target: path.join(targetRoot, ...manifestKey.split("/")),
|
|
106
|
+
recursive: false
|
|
107
|
+
}));
|
|
108
|
+
};
|
|
109
|
+
const getManagedCodingHrHubAssetSpecs = (targetRoot, mode) => {
|
|
110
|
+
const specs = getManagedHubAssetSpecs(targetRoot, mode);
|
|
111
|
+
if (mode !== "hr-office") return specs;
|
|
112
|
+
return [
|
|
113
|
+
...specs,
|
|
114
|
+
...hrSkillDirectories.map((name) => ({
|
|
115
|
+
manifestKey: `skills/${name}`,
|
|
116
|
+
source: path.join(builtInSkillsRoot, name),
|
|
117
|
+
target: path.join(targetRoot, "skills", name),
|
|
118
|
+
recursive: true
|
|
119
|
+
}))
|
|
120
|
+
];
|
|
121
|
+
};
|
|
122
|
+
const getManagedHrHomeAssetSpecs = (hrRoot, mode) => {
|
|
123
|
+
if (mode !== "hr-office") return [];
|
|
124
|
+
return hrHelperScripts.map((name) => ({
|
|
125
|
+
manifestKey: `hr-home/bin/${name}`,
|
|
126
|
+
source: path.join(hrSupportBinRoot, name),
|
|
127
|
+
target: path.join(hrRoot, "bin", name),
|
|
128
|
+
recursive: false,
|
|
129
|
+
executable: true
|
|
130
|
+
}));
|
|
131
|
+
};
|
|
132
|
+
export {
|
|
133
|
+
buildBuiltinVersionManifest,
|
|
134
|
+
getBuiltInManifestKeysForMode,
|
|
135
|
+
getManagedCodingHrHubAssetSpecs,
|
|
136
|
+
getManagedHrHomeAssetSpecs,
|
|
137
|
+
getManagedHubAssetSpecs,
|
|
138
|
+
listBuiltInAssetNames
|
|
139
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const PROFILE_ADD_CAPABILITY_REGISTRY = {
|
|
2
|
+
"hr-suite": [
|
|
3
|
+
"hr",
|
|
4
|
+
"hr-planner",
|
|
5
|
+
"hr-sourcer",
|
|
6
|
+
"hr-evaluator",
|
|
7
|
+
"hr-cto",
|
|
8
|
+
"hr-adapter",
|
|
9
|
+
"hr-verifier"
|
|
10
|
+
]
|
|
11
|
+
};
|
|
12
|
+
const expandProfileAddSelections = (selections) => selections.flatMap(
|
|
13
|
+
(selection) => PROFILE_ADD_CAPABILITY_REGISTRY[selection] ?? [selection]
|
|
14
|
+
);
|
|
15
|
+
const listProfileAddCapabilityNames = () => Object.keys(PROFILE_ADD_CAPABILITY_REGISTRY).sort();
|
|
16
|
+
export {
|
|
17
|
+
PROFILE_ADD_CAPABILITY_REGISTRY,
|
|
18
|
+
expandProfileAddSelections,
|
|
19
|
+
listProfileAddCapabilityNames
|
|
20
|
+
};
|