@hybridlabor-api/aos 4.5.1 → 4.6.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 (34) hide show
  1. package/.agents/rules/antigravity-rtk-rules.md +32 -0
  2. package/.claude/agents/architect.md +0 -1
  3. package/.claude/agents/database-reviewer.md +3 -102
  4. package/.claude/agents/go-build-resolver.md +3 -105
  5. package/.claude/agents/godmode-engineering.md +1 -2
  6. package/.claude/agents/godmode-media-eventtech.md +1 -2
  7. package/.claude/agents/godmode-shipping.md +2 -3
  8. package/.claude/agents/godmode-ui-ux.md +2 -3
  9. package/.claude/agents/opensource-forker.md +4 -210
  10. package/.claude/agents/opensource-sanitizer.md +3 -199
  11. package/.claude/agents/reviewer.md +1 -2
  12. package/.claude/agents/security-reviewer.md +3 -119
  13. package/.claude/agents/silent-failure-hunter.md +3 -61
  14. package/.claude/agents/techlead.md +1 -2
  15. package/.opencode/agents/architect.md +1 -0
  16. package/.opencode/agents/database-reviewer.md +10 -0
  17. package/.opencode/agents/go-build-resolver.md +10 -0
  18. package/.opencode/agents/godmode-engineering.md +1 -0
  19. package/.opencode/agents/godmode-media-eventtech.md +1 -0
  20. package/.opencode/agents/godmode-shipping.md +2 -1
  21. package/.opencode/agents/godmode-ui-ux.md +1 -0
  22. package/.opencode/agents/opensource-forker.md +10 -0
  23. package/.opencode/agents/opensource-sanitizer.md +10 -0
  24. package/.opencode/agents/reviewer.md +1 -0
  25. package/.opencode/agents/security-reviewer.md +10 -0
  26. package/.opencode/agents/silent-failure-hunter.md +10 -0
  27. package/.opencode/agents/techlead.md +1 -0
  28. package/.opencode/plugins/bdb-aos.js +171 -0
  29. package/docs/email/aos-4.5.0-update-banner.html +211 -0
  30. package/installer.js +261 -30
  31. package/package.json +1 -1
  32. package/skills/global_config/aos-project-init/SKILL.md +2 -0
  33. package/skills/global_config/subagent-setup/SKILL.md +71 -0
  34. package/skills/global_config/subagent-setup/scripts/setup-subagents.mjs +185 -0
package/installer.js CHANGED
@@ -2919,17 +2919,83 @@ function yamlQuote(str) {
2919
2919
  return `"${str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, ' ')}"`;
2920
2920
  }
2921
2921
 
2922
+ // Canonical model tiers mapped to verified provider-specific model IDs for each harness.
2923
+ // Protects against vendor model churn and provides seamless cross-harness defaults.
2924
+ const CANONICAL_TIERS = {
2925
+ reasoning_max: {
2926
+ claude: 'opus',
2927
+ antigravity: 'gemini-3.1-pro-high',
2928
+ opencode: 'opencode/muse-spark-1.3-contributor-free',
2929
+ codex: 'o3-mini'
2930
+ },
2931
+ standard_fast: {
2932
+ claude: 'sonnet',
2933
+ antigravity: 'gemini-3.8-flash-high',
2934
+ opencode: 'opencode/muse-spark-1.3-contributor-free',
2935
+ codex: 'gpt-4o'
2936
+ },
2937
+ trivial_low: {
2938
+ claude: 'haiku',
2939
+ antigravity: 'gemini-3.8-flash-low',
2940
+ opencode: 'opencode/muse-spark-1.3-contributor-free',
2941
+ codex: 'gpt-4o-mini'
2942
+ }
2943
+ };
2944
+
2945
+ // Loads .aos/pipeline.json or .aos/project.json containing role-to-model/harness mappings.
2946
+ function loadPipelineConfig(projectDir = currentDir) {
2947
+ const candidates = [
2948
+ path.join(projectDir, '.aos', 'pipeline.json'),
2949
+ path.join(projectDir, '.aos', 'project.json'),
2950
+ path.join(homeDir, '.aos', 'pipeline.json')
2951
+ ];
2952
+ for (const p of candidates) {
2953
+ if (fs.existsSync(p)) {
2954
+ try {
2955
+ const data = JSON.parse(fs.readFileSync(p, 'utf8'));
2956
+ if (data && data.pipeline) return data.pipeline;
2957
+ } catch {}
2958
+ }
2959
+ }
2960
+ return null;
2961
+ }
2962
+
2963
+ // Resolves model ID, tier, and enabled status for a given agent role and target harness.
2964
+ function resolveAgentConfig(agentSlug, harness, pipelineConfig = null) {
2965
+ const normSlug = agentSlug.toLowerCase().replace(/-/g, '_');
2966
+ const roleCfg = pipelineConfig ? (pipelineConfig[normSlug] || pipelineConfig[agentSlug]) : null;
2967
+ if (roleCfg) {
2968
+ if (roleCfg.model) {
2969
+ return {
2970
+ model: roleCfg.model,
2971
+ tier: roleCfg.tier || 'custom',
2972
+ enabled: roleCfg.enabled !== false,
2973
+ harness: roleCfg.harness || harness
2974
+ };
2975
+ }
2976
+ if (roleCfg.tier && CANONICAL_TIERS[roleCfg.tier] && CANONICAL_TIERS[roleCfg.tier][harness]) {
2977
+ return {
2978
+ model: CANONICAL_TIERS[roleCfg.tier][harness],
2979
+ tier: roleCfg.tier,
2980
+ enabled: roleCfg.enabled !== false,
2981
+ harness: roleCfg.harness || harness
2982
+ };
2983
+ }
2984
+ }
2985
+ const defaultTier = (normSlug === 'architect' || normSlug === 'reviewer') ? 'reasoning_max' : 'standard_fast';
2986
+ const fallbackModel = (CANONICAL_TIERS[defaultTier] && CANONICAL_TIERS[defaultTier][harness]) || 'inherit';
2987
+ return { model: fallbackModel, tier: defaultTier, enabled: true, harness };
2988
+ }
2989
+
2922
2990
  // Generates .claude/agents/<name>.md — Claude Code's native subagent format.
2923
2991
  // Only frontmatter fields confirmed against code.claude.com/docs/en/sub-agents
2924
- // are emitted (name, description, model). `tools:`/`permission`-style
2925
- // allowlists are deliberately omitted rather than guessed from the MCP-server
2926
- // list -- an unverified mapping there would silently over- or under-scope a
2927
- // subagent's tool access, which is worse than inheriting the default set.
2928
- function compileClaudeAgents(agents, targetDir) {
2992
+ // are emitted (name, description, model).
2993
+ function compileClaudeAgents(agents, targetDir, pipelineConfig = null) {
2929
2994
  fs.mkdirSync(targetDir, { recursive: true });
2930
2995
  for (const a of agents) {
2931
2996
  const slug = a.name.toLowerCase().replace(/_/g, '-');
2932
- const model = a.model ? a.model.toLowerCase() : 'inherit';
2997
+ const resolved = resolveAgentConfig(slug, 'claude', pipelineConfig);
2998
+ const model = (resolved && resolved.model) ? resolved.model.toLowerCase() : (a.model ? a.model.toLowerCase() : 'inherit');
2933
2999
  const body = [
2934
3000
  a.role,
2935
3001
  a.skills.length ? `**Primary skills:** ${a.skills.join(', ')}` : null,
@@ -2950,18 +3016,13 @@ function compileClaudeAgents(agents, targetDir) {
2950
3016
  }
2951
3017
  }
2952
3018
 
2953
- // Generates .opencode/agents/<name>.md. Only `description` and `mode` are
2954
- // emitted -- confirmed fields per opencode.ai/docs/agents. `model` and
2955
- // `permission` are left unset: OpenCode's model-id format and its mapping
2956
- // from an MCP-server list to `permission` keys are not verified against its
2957
- // docs, so guessing either would risk emitting a value OpenCode silently
2958
- // can't resolve. `mode: subagent` is used for every generated agent, since
2959
- // none of these five is meant to be a primary/default agent a user talks to
2960
- // directly.
2961
- function compileOpenCodeAgents(agents, targetDir) {
3019
+ // Generates .opencode/agents/<name>.md. Emits description, mode: subagent, and model.
3020
+ function compileOpenCodeAgents(agents, targetDir, pipelineConfig = null) {
2962
3021
  fs.mkdirSync(targetDir, { recursive: true });
2963
3022
  for (const a of agents) {
2964
3023
  const slug = a.name.toLowerCase().replace(/_/g, '-');
3024
+ const resolved = resolveAgentConfig(slug, 'opencode', pipelineConfig);
3025
+ const model = resolved && resolved.model ? resolved.model : null;
2965
3026
  const body = [
2966
3027
  a.role,
2967
3028
  a.skills.length ? `**Primary skills:** ${a.skills.join(', ')}` : null,
@@ -2969,18 +3030,54 @@ function compileOpenCodeAgents(agents, targetDir) {
2969
3030
  a.output ? `**Output artifact(s):** ${a.output}` : null,
2970
3031
  ].filter(Boolean).join('\n\n');
2971
3032
 
2972
- const frontmatter = [
3033
+ const frontmatterLines = [
2973
3034
  '---',
2974
3035
  `description: ${yamlQuote(a.role)}`,
2975
3036
  'mode: subagent',
2976
- '---',
2977
- '',
3037
+ ];
3038
+ if (model) {
3039
+ frontmatterLines.push(`model: ${model}`);
3040
+ }
3041
+ frontmatterLines.push('---', '');
3042
+
3043
+ fs.writeFileSync(path.join(targetDir, `${slug}.md`), frontmatterLines.join('\n') + body + '\n');
3044
+ }
3045
+ }
3046
+
3047
+ // Generates .codex/agents/<name>.toml and .codex/agents/<name>.md for ChatGPT Codex CLI.
3048
+ function compileCodexAgents(agents, targetDir, pipelineConfig = null) {
3049
+ fs.mkdirSync(targetDir, { recursive: true });
3050
+ for (const a of agents) {
3051
+ const slug = a.name.toLowerCase().replace(/_/g, '-');
3052
+ const resolved = resolveAgentConfig(slug, 'codex', pipelineConfig);
3053
+ const model = resolved && resolved.model ? resolved.model : 'o3-mini';
3054
+
3055
+ const tomlContent = [
3056
+ `# Codex subagent configuration for ${slug}`,
3057
+ `name = "${slug}"`,
3058
+ `description = ${JSON.stringify(a.role)}`,
3059
+ `model = "${model}"`,
3060
+ `tier = "${resolved.tier || 'standard_fast'}"`,
3061
+ `enabled = ${resolved.enabled !== false}`,
3062
+ `prompt_file = "${slug}.md"`
2978
3063
  ].join('\n');
3064
+ fs.writeFileSync(path.join(targetDir, `${slug}.toml`), tomlContent + '\n');
2979
3065
 
2980
- fs.writeFileSync(path.join(targetDir, `${slug}.md`), frontmatter + body + '\n');
3066
+ const body = [
3067
+ `# Role: ${a.name}`,
3068
+ a.role,
3069
+ a.skills.length ? `**Primary skills:** ${a.skills.join(', ')}` : null,
3070
+ a.mcpServers.length ? `**MCP servers used:** ${a.mcpServers.join(', ')}` : null,
3071
+ a.output ? `**Output artifact(s):** ${a.output}` : null,
3072
+ '',
3073
+ '## Instructions',
3074
+ a.systemPrompt
3075
+ ].filter(Boolean).join('\n\n');
3076
+ fs.writeFileSync(path.join(targetDir, `${slug}.md`), body + '\n');
2981
3077
  }
2982
3078
  }
2983
3079
 
3080
+
2984
3081
  function injectHarnessRules() {
2985
3082
  const geminiMdSrc = path.join(srcDir, 'GEMINI.md');
2986
3083
  const agentsMdSrc = path.join(srcDir, '.agents', 'agents.md');
@@ -3034,11 +3131,16 @@ function injectHarnessRules() {
3034
3131
  const agyAgentsDir = path.join(geminiDir, 'config', 'agents');
3035
3132
  if (!fs.existsSync(agyAgentsDir)) fs.mkdirSync(agyAgentsDir, { recursive: true });
3036
3133
 
3134
+ const pipelineConfig = loadPipelineConfig();
3037
3135
  const agents = parseAgentsMd(agentsMdContent);
3038
3136
  for (const a of agents) {
3137
+ const slug = a.name.toLowerCase().replace(/_/g, '-');
3138
+ const resolved = resolveAgentConfig(slug, 'antigravity', pipelineConfig);
3039
3139
  const agentConfig = {
3040
3140
  name: a.name,
3041
3141
  description: a.role,
3142
+ model: resolved.model,
3143
+ model_tier: resolved.tier,
3042
3144
  enable_write_tools: true,
3043
3145
  enable_subagent_tools: true,
3044
3146
  enable_mcp_tools: true,
@@ -3053,22 +3155,34 @@ function injectHarnessRules() {
3053
3155
 
3054
3156
  installStep('compile Claude Code subagents', () => {
3055
3157
  if (agentsMdContent) {
3158
+ const pipelineConfig = loadPipelineConfig();
3056
3159
  const agents = parseAgentsMd(agentsMdContent);
3057
3160
  const claudeAgentsDir = path.join(homeDir, '.claude', 'agents');
3058
- compileClaudeAgents(agents, claudeAgentsDir);
3161
+ compileClaudeAgents(agents, claudeAgentsDir, pipelineConfig);
3059
3162
  log.step(`Compiled AGENTS.md to Claude Code subagents in ${claudeAgentsDir}`);
3060
3163
  }
3061
3164
  }, 'Claude Code agents unchanged');
3062
3165
 
3063
3166
  installStep('compile OpenCode subagents', () => {
3064
3167
  if (agentsMdContent) {
3168
+ const pipelineConfig = loadPipelineConfig();
3065
3169
  const agents = parseAgentsMd(agentsMdContent);
3066
3170
  const opencodeAgentsDir = path.join(homeDir, '.opencode', 'agents');
3067
- compileOpenCodeAgents(agents, opencodeAgentsDir);
3171
+ compileOpenCodeAgents(agents, opencodeAgentsDir, pipelineConfig);
3068
3172
  log.step(`Compiled AGENTS.md to OpenCode subagents in ${opencodeAgentsDir}`);
3069
3173
  }
3070
3174
  }, 'OpenCode agents unchanged');
3071
3175
 
3176
+ installStep('compile Codex subagents', () => {
3177
+ if (agentsMdContent) {
3178
+ const pipelineConfig = loadPipelineConfig();
3179
+ const agents = parseAgentsMd(agentsMdContent);
3180
+ const codexAgentsDir = path.join(homeDir, '.codex', 'agents');
3181
+ compileCodexAgents(agents, codexAgentsDir, pipelineConfig);
3182
+ log.step(`Compiled AGENTS.md to Codex subagents in ${codexAgentsDir}`);
3183
+ }
3184
+ }, 'Codex agents unchanged');
3185
+
3072
3186
  const claudeMdPath = path.join(currentDir, 'CLAUDE.md');
3073
3187
  installStep(`sync ${claudeMdPath}`, () => {
3074
3188
  let claudeContent = fs.existsSync(claudeMdPath) ? fs.readFileSync(claudeMdPath, 'utf8') : '';
@@ -3213,6 +3327,24 @@ function installGlobalHooks() {
3213
3327
  copyDirRecursiveSync(workflowsSrc, codexWorkflowsDir);
3214
3328
  }
3215
3329
  mergeCodexTomlHooks(path.join(homeDir, '.codex', 'config.toml'));
3330
+
3331
+ // 4. OpenCode CLI
3332
+ const opencodeDir = process.platform === 'win32'
3333
+ ? path.join(process.env.APPDATA || homeDir, 'opencode')
3334
+ : path.join(homeDir, '.config', 'opencode');
3335
+ const opencodePluginSrc = path.join(srcDir, '.opencode', 'plugins', 'bdb-aos.js');
3336
+ if (fs.existsSync(opencodePluginSrc)) {
3337
+ const opencodePluginsDir = path.join(opencodeDir, 'plugins');
3338
+ const opencodePluginDest = path.join(opencodePluginsDir, 'bdb-aos.js');
3339
+ try {
3340
+ fs.mkdirSync(opencodePluginsDir, { recursive: true });
3341
+ fs.copyFileSync(opencodePluginSrc, opencodePluginDest);
3342
+ try { fs.chmodSync(opencodePluginDest, 0o644); } catch (e) { logDebug(e, 'chmod opencode plugin'); }
3343
+ log.step(`Installed OpenCode plugin to ${opencodePluginDest}`);
3344
+ } catch (e) {
3345
+ log.warn(`Could not install OpenCode plugin: ${e.message}`);
3346
+ }
3347
+ }
3216
3348
  }
3217
3349
 
3218
3350
  // Merge the BDB hooks -- the two gates plus the memB ambient-memory hook --
@@ -3495,10 +3627,20 @@ function installProjectHarness() {
3495
3627
  }, 'go-gate / graph-gate enforcement stays inactive in this project.');
3496
3628
 
3497
3629
  installStep('copy agent definitions into project', () => {
3498
- const agentsSrc = path.join(srcDir, '.claude', 'agents');
3499
- if (fs.existsSync(agentsSrc)) {
3500
- copyDirRecursiveSync(agentsSrc, path.join(projectClaudeDir, 'agents'));
3501
- log.step(`Copied agent definitions to ${path.join(projectClaudeDir, 'agents')}`);
3630
+ const agentsMdSrc = path.join(srcDir, '.agents', 'agents.md');
3631
+ const pipelineConfig = loadPipelineConfig(currentDir);
3632
+ if (fs.existsSync(agentsMdSrc)) {
3633
+ const agents = parseAgentsMd(fs.readFileSync(agentsMdSrc, 'utf8'));
3634
+ compileClaudeAgents(agents, path.join(projectClaudeDir, 'agents'), pipelineConfig);
3635
+ compileOpenCodeAgents(agents, path.join(currentDir, '.opencode', 'agents'), pipelineConfig);
3636
+ compileCodexAgents(agents, path.join(projectCodexDir, 'agents'), pipelineConfig);
3637
+ log.step(`Compiled project agent definitions for Claude, OpenCode, and Codex`);
3638
+ } else {
3639
+ const agentsSrc = path.join(srcDir, '.claude', 'agents');
3640
+ if (fs.existsSync(agentsSrc)) {
3641
+ copyDirRecursiveSync(agentsSrc, path.join(projectClaudeDir, 'agents'));
3642
+ log.step(`Copied agent definitions to ${path.join(projectClaudeDir, 'agents')}`);
3643
+ }
3502
3644
  }
3503
3645
  }, 'The dispatcher runs, but its agent-file pointers resolve to nothing.');
3504
3646
 
@@ -4022,17 +4164,98 @@ async function universalHarnessSync(primaryMcpConfigPath) {
4022
4164
  const existing = readJsoncFile(targetPath);
4023
4165
  const data = existing && existing.mcp ? existing : Object.assign({}, existing || {}, { mcp: {} });
4024
4166
  if (masterMcpData.mcpServers) {
4167
+ // OpenCode provider APIs (e.g. OpenAI-compatible, Console) enforce tool name length limits
4168
+ // (e.g. max 64 chars) and context limits. Loading 20+ MCPs causes provider errors (e.g. 73-char tool names).
4169
+ // OpenCode uses a slim profile: only core servers (memb_mcp, zavora_computer_use) are enabled by default.
4170
+ const OPENCODE_DEFAULT_SLIM = new Set(['memb_mcp', 'zavora_computer_use']);
4171
+ const existingMcp = existing && existing.mcp ? existing.mcp : {};
4172
+ const hasExistingKeys = Object.keys(existingMcp).length > 0;
4173
+
4025
4174
  for (const [key, val] of Object.entries(masterMcpData.mcpServers)) {
4026
- const cmd = Array.isArray(val.command) ? val.command : [val.command];
4027
- const args = Array.isArray(val.args) ? val.args : [];
4175
+ const rawCmd = Array.isArray(val.command) ? val.command : [val.command];
4176
+ const rawArgs = Array.isArray(val.args) ? val.args : [];
4177
+ const fullCmd = [...rawCmd, ...rawArgs].map(c => {
4178
+ if (c === '__PYTHON_BIN__') {
4179
+ return process.platform === 'win32'
4180
+ ? path.join(homeDir, '.gemini', 'config', 'mcps', 'memb-mcp', '.venv', 'Scripts', 'python.exe')
4181
+ : path.join(homeDir, '.gemini', 'config', 'mcps', 'memb-mcp', '.venv', 'bin', 'python');
4182
+ }
4183
+ return c;
4184
+ });
4185
+
4186
+ const existingEntry = existingMcp[key];
4187
+
4188
+ // If existing config is intentionally slimmed (has keys, but omitted this one), don't resurrect unless in slim set
4189
+ if (hasExistingKeys && !existingEntry && !OPENCODE_DEFAULT_SLIM.has(key)) {
4190
+ continue;
4191
+ }
4192
+
4193
+ // Preserve user's explicit enabled/disabled setting; otherwise default to true only for slim set
4194
+ const isEnabled = existingEntry && typeof existingEntry.enabled === 'boolean'
4195
+ ? existingEntry.enabled
4196
+ : OPENCODE_DEFAULT_SLIM.has(key);
4197
+
4198
+ let envObj = val.environment || val.env;
4199
+ if (envObj) {
4200
+ envObj = Object.assign({}, envObj);
4201
+ for (const [eKey, eVal] of Object.entries(envObj)) {
4202
+ if (eVal === '__GEMINI_API_KEY__') {
4203
+ envObj[eKey] = '${GEMINI_API_KEY}';
4204
+ }
4205
+ }
4206
+ }
4207
+
4028
4208
  data.mcp[key] = {
4029
4209
  type: "local",
4030
- command: [...cmd, ...args],
4031
- enabled: true,
4032
- ...(val.environment || val.env ? { environment: val.environment || val.env } : {})
4210
+ command: fullCmd,
4211
+ enabled: isEnabled,
4212
+ ...(envObj ? { environment: envObj } : {})
4033
4213
  };
4034
4214
  }
4035
4215
  }
4216
+
4217
+ // Wire BDB AOS Plugin for OpenCode
4218
+ const opencodeDir = path.dirname(targetPath);
4219
+ const pluginsDir = path.join(opencodeDir, 'plugins');
4220
+ const pluginFile = path.join(pluginsDir, 'bdb-aos.js');
4221
+ const pluginSrc = path.join(srcDir, '.opencode', 'plugins', 'bdb-aos.js');
4222
+
4223
+ try {
4224
+ if (fs.existsSync(pluginSrc)) {
4225
+ fs.mkdirSync(pluginsDir, { recursive: true });
4226
+ fs.copyFileSync(pluginSrc, pluginFile);
4227
+ try { fs.chmodSync(pluginFile, 0o644); } catch (e) { logDebug(e, 'chmod pluginFile'); }
4228
+ }
4229
+ } catch (pluginErr) {
4230
+ log.warn(`Could not install OpenCode plugin: ${pluginErr.message}`);
4231
+ }
4232
+
4233
+ // Register plugin in opencode.jsonc if plugin file exists
4234
+ if (fs.existsSync(pluginFile)) {
4235
+ if (!Array.isArray(data.plugin)) {
4236
+ data.plugin = [];
4237
+ }
4238
+ const pluginPathNormalized = pluginFile.replace(/\\/g, '/');
4239
+ const alreadyRegistered = data.plugin.some(p => {
4240
+ const str = typeof p === 'string' ? p : (Array.isArray(p) ? p[0] : '');
4241
+ return str.includes('bdb-aos');
4242
+ });
4243
+ if (!alreadyRegistered) {
4244
+ data.plugin.push(pluginPathNormalized);
4245
+ }
4246
+ }
4247
+
4248
+ // Register skills paths for OpenCode
4249
+ if (!data.skills || typeof data.skills !== 'object') {
4250
+ data.skills = { paths: [".agents/skills"] };
4251
+ } else if (Array.isArray(data.skills.paths)) {
4252
+ if (!data.skills.paths.includes(".agents/skills")) {
4253
+ data.skills.paths.push(".agents/skills");
4254
+ }
4255
+ } else {
4256
+ data.skills.paths = [".agents/skills"];
4257
+ }
4258
+
4036
4259
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
4037
4260
  fs.writeFileSync(targetPath, JSON.stringify(data, null, 2), { mode: 0o600 });
4038
4261
  try { fs.chmodSync(targetPath, 0o600); } catch (e) { logDebug(e, 'chmod targetPath'); }
@@ -4616,4 +4839,12 @@ module.exports = {
4616
4839
  flushSessionManifest,
4617
4840
  copyDirRecursiveSync,
4618
4841
  INSTALL_MANIFEST_PATH,
4842
+ // Agent compilers & pipeline helpers
4843
+ parseAgentsMd,
4844
+ compileClaudeAgents,
4845
+ compileOpenCodeAgents,
4846
+ compileCodexAgents,
4847
+ loadPipelineConfig,
4848
+ resolveAgentConfig,
4849
+ CANONICAL_TIERS,
4619
4850
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.5.1",
3
+ "version": "4.6.0",
4
4
  "description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
5
5
  "main": "installer.js",
6
6
  "engines": {
@@ -77,6 +77,8 @@ your head. Ask; do not default it on.
77
77
  holds content, merge it into `AGENTS.md` first and only then replace it with a
78
78
  symlink. Never silently overwrite it.
79
79
 
80
+ **Subagents & Multi-Harness Pipeline.** Offer to initialize `.aos/pipeline.json` using canonical tiers (or custom roles via `/subagent-setup`). Ensures subagents are automatically compiled across Claude Code, Antigravity, OpenCode, and Codex CLI.
81
+
80
82
  **With a GitHub remote, two more.** Skip both entirely without a remote — a
81
83
  local-only repo has nowhere to put them, and a skipped question is clearer than
82
84
  one answered "no".
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: subagent-setup
3
+ description: Configure and synchronize multi-harness subagents (Claude Code, Google Antigravity, OpenCode, ChatGPT Codex CLI) with canonical model tiers and single source of truth in .aos/pipeline.json.
4
+ category: bdb-core
5
+ risk: low
6
+ source: bdb
7
+ date_added: "2026-09-20"
8
+ ---
9
+
10
+ # Skill: subagent-setup — Multi-Harness Subagent Configuration
11
+
12
+ Manages and synchronizes autonomous subagent roles across all four supported execution harnesses:
13
+ 1. **Claude Code** (`.claude/agents/*.md`)
14
+ 2. **Google Antigravity** (`~/.gemini/config/agents/*.json`)
15
+ 3. **OpenCode** (`.opencode/agents/*.md`)
16
+ 4. **ChatGPT Codex CLI** (`.codex/agents/*.toml` & `.codex/agents/*.md`)
17
+
18
+ Single Source of Truth: **`.aos/pipeline.json`** (compatible with AO Orchestrator Pipeline Wizard).
19
+
20
+ ---
21
+
22
+ ## 1. Canonical Model Tiers (Resilience against Model Churn)
23
+
24
+ To prevent breaking configurations when providers update model names, roles map to canonical tiers:
25
+
26
+ | Tier | Description | Claude Code | Antigravity | OpenCode | Codex CLI |
27
+ |---|---|---|---|---|---|
28
+ | `reasoning_max` | Deep reasoning, planning, adversarial review | `opus` | `gemini-3.1-pro-high` | `opencode/muse-spark-1.3-contributor-free` | `o3-mini` |
29
+ | `standard_fast` | Fast coding, frontend/backend engineering | `sonnet` | `gemini-3.8-flash-high` | `opencode/muse-spark-1.3-contributor-free` | `gpt-4o` |
30
+ | `trivial_low` | Mechanical tasks, summaries | `haiku` | `gemini-3.8-flash-low` | `opencode/muse-spark-1.3-contributor-free` | `gpt-4o-mini` |
31
+
32
+ *Note:* Custom model IDs can also be set directly per role and harness.
33
+
34
+ ---
35
+
36
+ ## 2. CLI Usage
37
+
38
+ ### View Current Pipeline Status
39
+ ```bash
40
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs --show
41
+ ```
42
+
43
+ ### Initialize Default Pipeline Configuration
44
+ ```bash
45
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs --init-default
46
+ ```
47
+
48
+ ### Update a Specific Role
49
+ ```bash
50
+ # Example: Set Reviewer to Codex with o3-mini
51
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs \
52
+ --set-role reviewer --harness codex --model o3-mini --tier reasoning_max
53
+
54
+ # Example: Set Shipping to OpenCode with Free Tier
55
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs \
56
+ --set-role shipping --harness opencode --model opencode/muse-spark-1.3-contributor-free
57
+
58
+ # Example: Enable or Disable a Role
59
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs --set-role media_eventtech --enable
60
+ ```
61
+
62
+ ### Synchronize to All Harnesses
63
+ ```bash
64
+ node skills/global_config/subagent-setup/scripts/setup-subagents.mjs --sync
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 3. Integration with `aos-project-init` & AO Orchestrator
70
+ - **AO Orchestrator (Port 3101)**: The Pipeline Wizard (Schritt 3 von 4: Agenten & Rollen) directly reads and writes `.aos/pipeline.json`.
71
+ - **`aos-project-init`**: Offers subagent configuration during project interview, defaulting to the canonical tier matrix.
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * skills/global_config/subagent-setup/scripts/setup-subagents.mjs
5
+ *
6
+ * Interactive & CLI tool to configure subagent roles and model tiers across:
7
+ * - Claude Code (.claude/agents/*.md)
8
+ * - Google Antigravity (~/.gemini/config/agents/*.json)
9
+ * - OpenCode (.opencode/agents/*.md)
10
+ * - ChatGPT Codex CLI (.codex/agents/*.toml & *.md)
11
+ *
12
+ * Single Source of Truth: .aos/pipeline.json (or .aos/project.json)
13
+ */
14
+
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import readline from 'node:readline';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { createRequire } from 'node:module';
20
+
21
+ const require = createRequire(import.meta.url);
22
+ const installer = require('../../../../installer.js');
23
+
24
+ const {
25
+ CANONICAL_TIERS,
26
+ loadPipelineConfig,
27
+ resolveAgentConfig,
28
+ parseAgentsMd,
29
+ compileClaudeAgents,
30
+ compileOpenCodeAgents,
31
+ compileCodexAgents
32
+ } = installer;
33
+
34
+ const ROLES = [
35
+ { key: 'architect', label: 'Architect', defaultTier: 'reasoning_max', defaultHarness: 'claude' },
36
+ { key: 'techlead', label: 'TechLead', defaultTier: 'standard_fast', defaultHarness: 'antigravity' },
37
+ { key: 'ui_ux', label: 'Godmode_UI_UX', defaultTier: 'standard_fast', defaultHarness: 'claude' },
38
+ { key: 'engineering', label: 'Godmode_Engineering', defaultTier: 'standard_fast', defaultHarness: 'claude' },
39
+ { key: 'media_eventtech', label: 'Godmode_Media_EventTech', defaultTier: 'standard_fast', defaultHarness: 'antigravity', defaultEnabled: false },
40
+ { key: 'reviewer', label: 'Reviewer', defaultTier: 'reasoning_max', defaultHarness: 'codex' },
41
+ { key: 'shipping', label: 'Godmode_Shipping', defaultTier: 'standard_fast', defaultHarness: 'opencode' }
42
+ ];
43
+
44
+ function printPipeline(pipeline) {
45
+ console.log('\n=== AOS Multi-Harness Subagent Pipeline ===\n');
46
+ console.log(
47
+ 'Role'.padEnd(20) +
48
+ 'Status'.padEnd(10) +
49
+ 'Harness'.padEnd(15) +
50
+ 'Tier'.padEnd(18) +
51
+ 'Model'
52
+ );
53
+ console.log('-'.repeat(80));
54
+
55
+ for (const r of ROLES) {
56
+ const cfg = pipeline[r.key] || {};
57
+ const enabled = cfg.enabled !== false;
58
+ const harness = cfg.harness || r.defaultHarness;
59
+ const tier = cfg.tier || r.defaultTier;
60
+ const model = cfg.model || (CANONICAL_TIERS[tier] ? CANONICAL_TIERS[tier][harness] : 'inherit');
61
+
62
+ console.log(
63
+ r.label.padEnd(20) +
64
+ (enabled ? '✔ ON' : '✖ OFF').padEnd(10) +
65
+ harness.padEnd(15) +
66
+ tier.padEnd(18) +
67
+ model
68
+ );
69
+ }
70
+ console.log('\nConfig file: .aos/pipeline.json\n');
71
+ }
72
+
73
+ function getDefaultPipeline() {
74
+ const pipeline = {};
75
+ for (const r of ROLES) {
76
+ const harness = r.defaultHarness;
77
+ const tier = r.defaultTier;
78
+ const model = CANONICAL_TIERS[tier] ? CANONICAL_TIERS[tier][harness] : 'inherit';
79
+ pipeline[r.key] = {
80
+ enabled: r.defaultEnabled !== false,
81
+ harness,
82
+ tier,
83
+ model
84
+ };
85
+ }
86
+ return pipeline;
87
+ }
88
+
89
+ function syncAllHarnesses(projectDir, pipeline) {
90
+ const agentsMdSrc = path.join(projectDir, '.agents', 'agents.md');
91
+ if (!fs.existsSync(agentsMdSrc)) {
92
+ console.error(`Error: .agents/agents.md not found at ${agentsMdSrc}`);
93
+ return false;
94
+ }
95
+
96
+ const content = fs.readFileSync(agentsMdSrc, 'utf8');
97
+ const agents = parseAgentsMd(content);
98
+
99
+ // 1. Claude
100
+ const claudeDir = path.join(projectDir, '.claude', 'agents');
101
+ compileClaudeAgents(agents, claudeDir, pipeline);
102
+ console.log(` ✓ Synced Claude Code agents to ${claudeDir}`);
103
+
104
+ // 2. OpenCode
105
+ const opencodeDir = path.join(projectDir, '.opencode', 'agents');
106
+ compileOpenCodeAgents(agents, opencodeDir, pipeline);
107
+ console.log(` ✓ Synced OpenCode agents to ${opencodeDir}`);
108
+
109
+ // 3. Codex
110
+ const codexDir = path.join(projectDir, '.codex', 'agents');
111
+ compileCodexAgents(agents, codexDir, pipeline);
112
+ console.log(` ✓ Synced Codex CLI agents to ${codexDir}`);
113
+
114
+ return true;
115
+ }
116
+
117
+ async function main() {
118
+ const args = process.argv.slice(2);
119
+ const cwd = process.cwd();
120
+ const aosDir = path.join(cwd, '.aos');
121
+ const pipelinePath = path.join(aosDir, 'pipeline.json');
122
+
123
+ let pipeline = loadPipelineConfig(cwd) || getDefaultPipeline();
124
+
125
+ if (args.includes('--show')) {
126
+ printPipeline(pipeline);
127
+ process.exit(0);
128
+ }
129
+
130
+ if (args.includes('--init-default')) {
131
+ fs.mkdirSync(aosDir, { recursive: true });
132
+ fs.writeFileSync(pipelinePath, JSON.stringify({ version: 1, pipeline: getDefaultPipeline() }, null, 2));
133
+ console.log(`Initialized default pipeline at ${pipelinePath}`);
134
+ printPipeline(getDefaultPipeline());
135
+ syncAllHarnesses(cwd, getDefaultPipeline());
136
+ process.exit(0);
137
+ }
138
+
139
+ if (args.includes('--sync')) {
140
+ console.log('Syncing all harnesses from .aos/pipeline.json...');
141
+ syncAllHarnesses(cwd, pipeline);
142
+ console.log('✓ All subagents synchronized.');
143
+ process.exit(0);
144
+ }
145
+
146
+ const setRoleIdx = args.indexOf('--set-role');
147
+ if (setRoleIdx !== -1 && args[setRoleIdx + 1]) {
148
+ const role = args[setRoleIdx + 1].toLowerCase().replace(/-/g, '_');
149
+ const harnessIdx = args.indexOf('--harness');
150
+ const modelIdx = args.indexOf('--model');
151
+ const tierIdx = args.indexOf('--tier');
152
+ const disableFlag = args.includes('--disable');
153
+ const enableFlag = args.includes('--enable');
154
+
155
+ if (!pipeline[role]) {
156
+ pipeline[role] = {};
157
+ }
158
+
159
+ if (harnessIdx !== -1 && args[harnessIdx + 1]) pipeline[role].harness = args[harnessIdx + 1];
160
+ if (modelIdx !== -1 && args[modelIdx + 1]) pipeline[role].model = args[modelIdx + 1];
161
+ if (tierIdx !== -1 && args[tierIdx + 1]) pipeline[role].tier = args[tierIdx + 1];
162
+ if (disableFlag) pipeline[role].enabled = false;
163
+ if (enableFlag) pipeline[role].enabled = true;
164
+
165
+ fs.mkdirSync(aosDir, { recursive: true });
166
+ fs.writeFileSync(pipelinePath, JSON.stringify({ version: 1, pipeline }, null, 2));
167
+ console.log(`Updated role "${role}" in ${pipelinePath}`);
168
+ printPipeline(pipeline);
169
+ syncAllHarnesses(cwd, pipeline);
170
+ process.exit(0);
171
+ }
172
+
173
+ // Default: Show pipeline and usage
174
+ printPipeline(pipeline);
175
+ console.log('Commands:');
176
+ console.log(' node setup-subagents.mjs --show');
177
+ console.log(' node setup-subagents.mjs --init-default');
178
+ console.log(' node setup-subagents.mjs --sync');
179
+ console.log(' node setup-subagents.mjs --set-role <role> --harness <claude|antigravity|opencode|codex> --model <model> [--tier <tier>]');
180
+ }
181
+
182
+ main().catch(err => {
183
+ console.error(err);
184
+ process.exit(1);
185
+ });