@hybridlabor-api/aos 4.16.0 → 4.18.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/.agents/plugins/marketplace.json +20 -0
- package/.claude/hooks/aos-bus.mjs +8 -4
- package/.claude/hooks/env-file-protection.mjs +19 -1
- package/.claude/hooks/go-gate.mjs +788 -69
- package/.claude/hooks/go-grant.mjs +88 -0
- package/.claude/hooks/go-token.mjs +17 -2
- package/.claude/hooks/memb-inject.mjs +61 -37
- package/.claude/settings.json +8 -0
- package/.codex-plugin/plugin.json +36 -6
- package/.opencode/commands/bdb-aos-brainstorm.md +5 -0
- package/.opencode/commands/bdb-aos-doctor.md +5 -0
- package/.opencode/commands/bdb-aos-graph.md +5 -0
- package/.opencode/commands/bdb-aos-init.md +5 -0
- package/.opencode/commands/bdb-aos-loop.md +5 -0
- package/.opencode/commands/bdb-aos-mastersession.md +5 -0
- package/.opencode/commands/bdb-aos-memb.md +5 -0
- package/.opencode/commands/bdb-aos-orchestrator.md +5 -0
- package/.opencode/commands/bdb-aos-plan.md +5 -0
- package/.opencode/commands/bdb-aos-playbooks.md +5 -0
- package/.opencode/commands/bdb-aos-setup.md +5 -0
- package/.opencode/commands/bdb-aos-shipping.md +5 -0
- package/.opencode/commands/bdb-aos-startproject.md +5 -0
- package/.opencode/commands/bdb-aos-store.md +5 -0
- package/.opencode/plugins/bdb-aos.js +54 -10
- package/README.md +5 -4
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/agy-commands/loop.md +5 -0
- package/bin/aos-acp.mjs +27 -1
- package/bin/aos-doctor.mjs +41 -4
- package/bin/aos-uninstall.mjs +41 -3
- package/bin/go-check.mjs +79 -0
- package/bin/guarded-patterns.json +106 -0
- package/commands/brainstorm.md +5 -0
- package/commands/doctor.md +5 -0
- package/commands/graph.md +5 -0
- package/commands/init.md +5 -0
- package/commands/loop.md +5 -0
- package/commands/mastersession.md +5 -0
- package/commands/memb.md +5 -0
- package/commands/orchestrator.md +5 -0
- package/commands/plan.md +5 -0
- package/commands/playbooks.md +5 -0
- package/commands/setup.md +5 -0
- package/commands/shipping.md +5 -0
- package/commands/startproject.md +5 -0
- package/commands/store.md +5 -0
- package/docs/codenotch.md +44 -0
- package/docs/codex-agy-setup.md +16 -0
- package/docs/codex-gate-smoke.md +43 -0
- package/docs/delegation-routing.md +32 -0
- package/docs/go-check.md +60 -0
- package/docs/master-session-acp.md +2 -0
- package/docs/opencode-setup.md +54 -0
- package/docs/plugin-migration.md +61 -0
- package/installer.js +351 -121
- package/lib/codenotch.js +389 -0
- package/lib/plugin-migration.js +462 -0
- package/lib/retired-skills.js +101 -0
- package/lib/store-ui/index.html +9 -1
- package/lib/store-ui/server.mjs +2 -0
- package/mcps/mcsc/README.md +1 -1
- package/mcps/mcsc/packages/core/src/adapters/agy.js +3 -1
- package/mcps/mcsc/packages/core/src/adapters/codex.js +2 -1
- package/mcps/mcsc/packages/core/src/adapters/opencode.js +2 -1
- package/mcps/mcsc/packages/core/src/depth.js +16 -0
- package/mcps/mcsc/packages/mcp/server.js +15 -2
- package/package.json +7 -2
- package/plugin-commands.json +143 -0
- package/plugin.json +299 -0
- package/plugins/bdb-aos-codex/.codex-plugin/plugin.json +38 -0
- package/plugins/bdb-aos-codex/skills/brainstorm/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/doctor/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/graph/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/init/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/loop/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/mastersession/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/memb/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/orchestrator/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/plan/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/playbooks/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/setup/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/shipping/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/startproject/SKILL.md +6 -0
- package/plugins/bdb-aos-codex/skills/store/SKILL.md +6 -0
- package/scripts/build-plugin-manifest.mjs +202 -3
- package/scripts/codex-gate-smoke.mjs +73 -0
- package/skills/basic/master-session/SKILL.md +11 -0
- package/skills/bdb-aos/scripts/list-playbooks.mjs +72 -0
- package/skills/global_config/agenttrail/SKILL.md +3 -1
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +255 -117
- package/skills/global_config/agenttrail/bin/ensure.mjs +60 -40
- package/skills/global_config/agenttrail/bin/repoid.mjs +70 -0
- package/skills/global_config/agenttrail/public/index.html +9 -1
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/bdb-memb-mcp/SKILL.md +5 -4
- package/skills/global_config/bdb-visual-edit/SKILL.md +28 -32
- package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +2 -2
- package/skills/global_config/bdb-visual-edit/scripts/locate-source.mjs +135 -0
- package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +30 -2
- package/skills/global_config/gogate/SKILL.md +123 -0
- package/skills/global_config/loop-templates/SKILL.md +27 -0
- package/skills/global_config/loop-templates/references/ci-until-green.md +27 -0
- package/skills/global_config/loop-templates/references/daily-summary.md +27 -0
- package/skills/global_config/loop-templates/references/pr-to-merge.md +27 -0
- package/skills/global_config/loop-templates/references/review-rounds.md +27 -0
- package/skills/global_config/mcsc/SKILL.md +9 -1
- package/skills/global_config/plan-arbiter/SKILL.md +1 -1
- package/skills/global_config/plan-canvas/SKILL.md +42 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +1 -1
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +2 -2
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/geometry.js +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/index.js +596 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/model.js +192 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/toolbar.js +99 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-server.js +282 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotation-schema.js +210 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/route.js +10 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sdk.js +6 -230
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +45 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sessions.js +60 -21
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/trail-on-approve.js +103 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +19 -7
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +118 -20
- package/skills/global_config/subagent-setup/SKILL.md +6 -0
- package/skills/global_config/subagent-setup/scripts/setup-subagents.mjs +20 -1
- package/skills/playbooks/pb-idea-to-launch/SKILL.md +2 -2
- package/skills/playbooks/pb-redesign-app/SKILL.md +3 -3
- package/skills/playbooks/pb-release-aos/SKILL.md +2 -2
- package/skills/playbooks/pb-ship/SKILL.md +2 -2
- package/skills/playbooks/pb-worktrees-land/SKILL.md +2 -2
- package/.codex-plugin/marketplace.json +0 -11
- package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +0 -27
- package/skills/global_config/visual-edit/README.md +0 -96
- package/skills/global_config/visual-edit/SKILL.md +0 -615
- package/skills/global_config/visual-plan/README.md +0 -93
- package/skills/global_config/visual-plan/SKILL.md +0 -544
- package/skills/global_config/visual-plan/references/canvas.md +0 -139
- package/skills/global_config/visual-plan/references/connection.md +0 -51
- package/skills/global_config/visual-plan/references/document-quality.md +0 -186
- package/skills/global_config/visual-plan/references/exemplar.md +0 -62
- package/skills/global_config/visual-plan/references/local-files.md +0 -99
- package/skills/global_config/visual-plan/references/wireframe.md +0 -319
- package/skills/global_config/visual-recap/README.md +0 -103
- package/skills/global_config/visual-recap/SKILL.md +0 -560
- package/skills/global_config/visual-recap/references/connection.md +0 -51
- package/skills/global_config/visual-recap/references/local-files.md +0 -99
- package/skills/global_config/visual-recap/references/wireframe.md +0 -319
package/installer.js
CHANGED
|
@@ -23,6 +23,8 @@ const net = require('net');
|
|
|
23
23
|
const readline = require('readline');
|
|
24
24
|
const util = require('util');
|
|
25
25
|
const crypto = require('crypto');
|
|
26
|
+
const pluginMigration = require('./lib/plugin-migration');
|
|
27
|
+
const { pruneRetiredSkills } = require('./lib/retired-skills');
|
|
26
28
|
|
|
27
29
|
function verifyDaemonListening(port, name, timeoutMs = 4000) {
|
|
28
30
|
return new Promise((resolve) => {
|
|
@@ -682,25 +684,6 @@ function initSessionManifest(existingManifest, sourceDirs) {
|
|
|
682
684
|
_sessionHashes = buildKnownSourceHashes(sourceDirs);
|
|
683
685
|
}
|
|
684
686
|
|
|
685
|
-
// Skills that left the package in 4.4.2 (Plan 02: migrated to the private
|
|
686
|
-
// aos-internal repo, per ~/dev/_plans/aos-2026-09-14/), by their flattened
|
|
687
|
-
// directory name at a sync destination.
|
|
688
|
-
//
|
|
689
|
-
// This is deliberately a fixed list, not a diff against what the current
|
|
690
|
-
// payload ships. A generic "anything on disk that is not in skills/ right
|
|
691
|
-
// now" pass was tried first and swept up two unrelated things: skill folders
|
|
692
|
-
// that MCP servers bundle inside their own package tree (e.g.
|
|
693
|
-
// mcps/tdmcp/.agents/skills/...), which happen to share the literal path
|
|
694
|
-
// segment "skills" but belong to a different install entirely; and skills
|
|
695
|
-
// under skills/workspace_agents/, which syncSkillsToGlobalHarnesses already
|
|
696
|
-
// deliberately excludes from the six global destinations, so a stray global
|
|
697
|
-
// copy of one says nothing about whether it is safe to delete. Both are real
|
|
698
|
-
// cleanup opportunities, but they are a separate investigation, not a side
|
|
699
|
-
// effect of this migration's beta test.
|
|
700
|
-
const SKILLS_REMOVED_IN_4_4_2 = new Set([
|
|
701
|
-
'bdbsaastraining', 'bdb-dev-os-skill', 'bdbsaashost', 'bdb-ecosystem-health', 'bdbsaas-ops',
|
|
702
|
-
]);
|
|
703
|
-
|
|
704
687
|
// The exact roots syncSkillsToGlobalHarnesses and the Gemini/Antigravity
|
|
705
688
|
// config-skills path write into. Pruning only touches a manifest entry whose
|
|
706
689
|
// path starts under one of these -- never one that merely contains a
|
|
@@ -717,51 +700,16 @@ function globalSkillDestRoots() {
|
|
|
717
700
|
];
|
|
718
701
|
}
|
|
719
702
|
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
// payload no longer ships. That is fine for new skills and updated ones, but
|
|
723
|
-
// it means a skill removed from a release stays on every user's disk
|
|
724
|
-
// forever, through every future update, because nothing after the removal
|
|
725
|
-
// ever looks at what is on disk that no longer has a source. This walks the
|
|
726
|
-
// install manifest -- which already has one entry per file this installer
|
|
727
|
-
// has ever placed -- for the specific skills 4.4.2 removed, under the six
|
|
728
|
-
// known global skill roots, and deletes them. A user-modified file is backed
|
|
729
|
-
// up first, the same way resolveFileConflict backs up a user edit it is
|
|
730
|
-
// about to overwrite, rather than silently deleting local changes.
|
|
703
|
+
// Retires skills the package no longer ships (lib/retired-skills.js). Edited or foreign files are
|
|
704
|
+
// copied to the backup dir before the skill folder is removed.
|
|
731
705
|
function pruneRemovedSkills(manifest) {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (!root) continue;
|
|
740
|
-
const skillDirName = targetPath.slice(root.length + 1).split(path.sep)[0];
|
|
741
|
-
if (!SKILLS_REMOVED_IN_4_4_2.has(skillDirName)) continue;
|
|
742
|
-
|
|
743
|
-
if (fs.existsSync(targetPath)) {
|
|
744
|
-
const diskHash = computeFileHash(targetPath);
|
|
745
|
-
const manifestEntry = manifest[targetPath];
|
|
746
|
-
if (diskHash !== null && manifestEntry && diskHash !== manifestEntry.sha256) {
|
|
747
|
-
const bakPath = `${targetPath}.${timestamp}.bak`;
|
|
748
|
-
try { fs.copyFileSync(targetPath, bakPath); } catch (e) { logDebug(e, 'prune-removed-skill backup'); }
|
|
749
|
-
log.warn(`[manifest] Retired skill left a user-edited file, backed up to ${bakPath} instead of deleting it.`);
|
|
750
|
-
} else {
|
|
751
|
-
try { fs.unlinkSync(targetPath); removedFiles++; } catch (e) { logDebug(e, 'prune-removed-skill unlink'); }
|
|
752
|
-
}
|
|
753
|
-
staleDirs.add(path.join(root, skillDirName));
|
|
754
|
-
}
|
|
755
|
-
delete manifest[targetPath];
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
for (const dir of staleDirs) {
|
|
759
|
-
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (e) { logDebug(e, 'prune-removed-skill rmdir'); }
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
if (staleDirs.size > 0) {
|
|
763
|
-
log.step(`Removed ${removedFiles} file(s) from ${staleDirs.size} retired skill install(s) no longer shipped.`);
|
|
764
|
-
}
|
|
706
|
+
pruneRetiredSkills({
|
|
707
|
+
home: homeDir,
|
|
708
|
+
manifest,
|
|
709
|
+
roots: globalSkillDestRoots(),
|
|
710
|
+
backupDir: path.join(homeDir, '.agents', 'backups', `retired-skills-${timestamp}`),
|
|
711
|
+
log,
|
|
712
|
+
});
|
|
765
713
|
}
|
|
766
714
|
|
|
767
715
|
function flushSessionManifest() {
|
|
@@ -1277,9 +1225,94 @@ function reportFatal(stage, e) {
|
|
|
1277
1225
|
process.exitCode = 1;
|
|
1278
1226
|
}
|
|
1279
1227
|
|
|
1228
|
+
// AOS_PLUGIN_MIGRATION=off skips plugin registration and loose-copy removal; =check only reports.
|
|
1229
|
+
function pluginMigrationMode(argv = process.argv, env = process.env) {
|
|
1230
|
+
const flag = argv.find((a) => a.startsWith('--plugin-migration='));
|
|
1231
|
+
const raw = String(flag ? flag.slice('--plugin-migration='.length) : (env.AOS_PLUGIN_MIGRATION || '')).trim().toLowerCase();
|
|
1232
|
+
if (raw === 'off' || raw === '0' || raw === 'false') return 'off';
|
|
1233
|
+
return raw === 'check' || DRY_RUN ? 'check' : 'on';
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// .agents/plugins holds the Codex marketplace file; it belongs to the repo checkout, not to ~/.agents or a project.
|
|
1237
|
+
const AGENTS_COPY_EXCLUDE = ['plugins'];
|
|
1238
|
+
|
|
1239
|
+
let _pluginMigration = null;
|
|
1240
|
+
function runPluginMigration({ targetHome = homeDir, detected = null, mode = pluginMigrationMode(), registrars } = {}) {
|
|
1241
|
+
if (_pluginMigration && targetHome === homeDir) return _pluginMigration;
|
|
1242
|
+
const keys = detected || detectPlatforms().map((d) => d.key);
|
|
1243
|
+
const ownsManifest = !_sessionManifest;
|
|
1244
|
+
const manifest = _sessionManifest || loadInstallManifest();
|
|
1245
|
+
let result;
|
|
1246
|
+
try {
|
|
1247
|
+
result = pluginMigration.migrate({ home: targetHome, manifest, detected: keys, mode, ...(registrars ? { registrars } : {}) });
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
log.warn(`Plugin migration skipped: ${e.message}`);
|
|
1250
|
+
result = { covered: new Set(), lines: [] };
|
|
1251
|
+
}
|
|
1252
|
+
for (const line of result.lines) log.step(line);
|
|
1253
|
+
if (ownsManifest && mode === 'on') saveInstallManifest(manifest);
|
|
1254
|
+
if (targetHome === homeDir) _pluginMigration = result;
|
|
1255
|
+
return result;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// The plugin migration runs first: when it retires Claude's loose copies, ~/.claude/skills is not
|
|
1259
|
+
// written again, so a rerun neither recreates nor re-backs-up what the plugin now delivers.
|
|
1260
|
+
function installTargetSkills(targets, { mode, backupDir, excludeSkills, skillsBase }) {
|
|
1261
|
+
const detected = detectPlatforms().map((d) => d.key);
|
|
1262
|
+
if (targets.some((t) => t.value === '2') && !detected.includes('claudecode')) detected.push('claudecode');
|
|
1263
|
+
const pluginCovered = runPluginMigration({ detected }).covered;
|
|
1264
|
+
|
|
1265
|
+
for (const t of targets) {
|
|
1266
|
+
const viaPlugin = t.value === '2' && pluginCovered.has('claudecode');
|
|
1267
|
+
if (mode === 'replace') {
|
|
1268
|
+
if (!viaPlugin) {
|
|
1269
|
+
moveIfExists(t.targetSkillDir, path.join(backupDir, `config_skills_backup_${t.value}`), `global config skills (${t.value})`);
|
|
1270
|
+
moveIfExists(t.targetLegacyDir, path.join(backupDir, `legacy_skills_backup_${t.value}`), `legacy skills (${t.value})`);
|
|
1271
|
+
}
|
|
1272
|
+
moveIfExists(t.targetWorkspaceDir, path.join(backupDir, `workspace_skills_backup_${t.value}`), `workspace skills (${t.value})`);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
installStep(`create the skill target directories (${t.value})`, () => {
|
|
1276
|
+
if (!viaPlugin) {
|
|
1277
|
+
fs.mkdirSync(t.targetSkillDir, { recursive: true });
|
|
1278
|
+
retireObsoleteLegacyDir(t.targetLegacyDir);
|
|
1279
|
+
}
|
|
1280
|
+
fs.mkdirSync(t.targetWorkspaceDir, { recursive: true });
|
|
1281
|
+
}, 'The skill copies below will most likely be skipped as well.');
|
|
1282
|
+
|
|
1283
|
+
if (fs.existsSync(skillsBase)) {
|
|
1284
|
+
installStep(`install the skills (${t.value})`, () => {
|
|
1285
|
+
const rawDirs = fs.readdirSync(skillsBase);
|
|
1286
|
+
const dirs = rawDirs.sort((a, b) => {
|
|
1287
|
+
const aIsLeaf = fs.existsSync(path.join(skillsBase, a, 'SKILL.md'));
|
|
1288
|
+
const bIsLeaf = fs.existsSync(path.join(skillsBase, b, 'SKILL.md'));
|
|
1289
|
+
if (aIsLeaf && !bIsLeaf) return 1;
|
|
1290
|
+
if (!aIsLeaf && bIsLeaf) return -1;
|
|
1291
|
+
return 0;
|
|
1292
|
+
});
|
|
1293
|
+
for (const dir of dirs) {
|
|
1294
|
+
const fullPath = path.join(skillsBase, dir);
|
|
1295
|
+
if (!fs.statSync(fullPath).isDirectory()) continue;
|
|
1296
|
+
|
|
1297
|
+
if (viaPlugin && dir !== 'workspace_agents') continue;
|
|
1298
|
+
if (dir === 'global_legacy') {
|
|
1299
|
+
copyDirRecursiveSync(fullPath, t.targetLegacyDir, excludeSkills);
|
|
1300
|
+
} else if (dir === 'workspace_agents') {
|
|
1301
|
+
copyDirRecursiveSync(fullPath, t.targetWorkspaceDir, excludeSkills);
|
|
1302
|
+
} else {
|
|
1303
|
+
syncSkillEntry(fullPath, dir, t.targetSkillDir, excludeSkills);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
log.step(`Installed all global config & core skills to ${t.targetSkillDir}`);
|
|
1307
|
+
}, 'The skills are missing or incomplete; the rest of the installation continues.');
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1280
1312
|
function syncSkillsToGlobalHarnesses(excludeSkills = []) {
|
|
1281
1313
|
const skillsBase = path.join(srcDir, 'skills');
|
|
1282
1314
|
if (!fs.existsSync(skillsBase)) return;
|
|
1315
|
+
const pluginCovered = runPluginMigration().covered;
|
|
1283
1316
|
|
|
1284
1317
|
// Mirror only into harnesses that are actually present. This list used to
|
|
1285
1318
|
// be unconditional, which both wrote skills nobody would read and planted
|
|
@@ -1300,7 +1333,7 @@ function syncSkillsToGlobalHarnesses(excludeSkills = []) {
|
|
|
1300
1333
|
{ dir: path.join(homeDir, '.cursor', 'skills'), key: 'cursor' },
|
|
1301
1334
|
{ dir: path.join(homeDir, '.roo', 'skills'), key: 'vscode' },
|
|
1302
1335
|
{ dir: process.platform === 'win32' ? path.join(process.env.APPDATA || homeDir, 'opencode', 'skills') : path.join(homeDir, '.config', 'opencode', 'skills'), key: 'opencode' },
|
|
1303
|
-
].filter((d) => d.key === null || detectedKeys.has(d.key));
|
|
1336
|
+
].filter((d) => d.key === null || (detectedKeys.has(d.key) && !pluginCovered.has(d.key)));
|
|
1304
1337
|
|
|
1305
1338
|
for (const { dir: dest } of extraSkillDestinations) {
|
|
1306
1339
|
try {
|
|
@@ -1549,6 +1582,17 @@ async function promptCredentials(referenceMcpDir) {
|
|
|
1549
1582
|
|
|
1550
1583
|
const DAEMON_LOGON_FALLBACK_EXIT_CODE = 10;
|
|
1551
1584
|
|
|
1585
|
+
// The daemon job points at these scripts for good, so they live in the installer-owned shared store
|
|
1586
|
+
// (~/.agents/skills), which the plugin migration never retires, not in ~/.claude/skills.
|
|
1587
|
+
function stableOpenWikiScripts() {
|
|
1588
|
+
const dest = path.join(homeDir, '.agents', 'skills', 'openwiki-skill');
|
|
1589
|
+
if (!fs.existsSync(path.join(dest, 'scripts', 'install_daemon.sh'))) {
|
|
1590
|
+
const src = path.join(srcDir, 'skills', 'global_config', 'openwiki-skill');
|
|
1591
|
+
if (fs.existsSync(src)) copyDirRecursiveSync(src, dest);
|
|
1592
|
+
}
|
|
1593
|
+
return path.join(dest, 'scripts');
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1552
1596
|
async function installOpenWikiDaemon(apiKey, targetSkillDir, openwikiEnv = {}) {
|
|
1553
1597
|
const prov = openwikiEnv.provider || "google";
|
|
1554
1598
|
if (!apiKey && !["ollama", "lmstudio"].includes(prov)) {
|
|
@@ -1562,7 +1606,7 @@ async function installOpenWikiDaemon(apiKey, targetSkillDir, openwikiEnv = {}) {
|
|
|
1562
1606
|
const s = spinner();
|
|
1563
1607
|
s.start('Installing OpenWiki Daemon...');
|
|
1564
1608
|
|
|
1565
|
-
const scriptBase =
|
|
1609
|
+
const scriptBase = stableOpenWikiScripts();
|
|
1566
1610
|
|
|
1567
1611
|
const daemonEnv = Object.assign({}, process.env, {
|
|
1568
1612
|
OPENWIKI_PROVIDER: prov,
|
|
@@ -3886,7 +3930,7 @@ function injectHarnessRules() {
|
|
|
3886
3930
|
const targetPath = path.join(homeDir, dir);
|
|
3887
3931
|
// settings.json is merged separately below: a wholesale
|
|
3888
3932
|
// copy would clobber user-owned keys like enabledPlugins.
|
|
3889
|
-
const exclude = dir === '.claude' ? ['settings.json'] : [];
|
|
3933
|
+
const exclude = dir === '.claude' ? ['settings.json'] : dir === '.agents' ? AGENTS_COPY_EXCLUDE : [];
|
|
3890
3934
|
copyDirRecursiveSync(sourcePath, targetPath, exclude);
|
|
3891
3935
|
log.step(`Copied ${dir} to ${targetPath}`);
|
|
3892
3936
|
}
|
|
@@ -3898,13 +3942,165 @@ function injectHarnessRules() {
|
|
|
3898
3942
|
const globalAgentsDir = path.join(os.homedir(), '.agents');
|
|
3899
3943
|
const agentsDirSrc = path.join(srcDir, '.agents');
|
|
3900
3944
|
if (fs.existsSync(agentsDirSrc)) {
|
|
3901
|
-
copyDirRecursiveSync(agentsDirSrc, globalAgentsDir);
|
|
3945
|
+
copyDirRecursiveSync(agentsDirSrc, globalAgentsDir, AGENTS_COPY_EXCLUDE);
|
|
3902
3946
|
log.step(`Synced global .agents/ to ${globalAgentsDir}`);
|
|
3903
3947
|
}
|
|
3904
3948
|
}, 'agents.md and workflows/startcycle.md may be missing globally.');
|
|
3905
3949
|
}
|
|
3906
3950
|
}
|
|
3907
3951
|
|
|
3952
|
+
// Optional third-party OpenCode components. Off unless named in
|
|
3953
|
+
// AOS_OPENCODE_OPTIONAL=ponytail,loop,rtk or --opencode-optional=ponytail,loop,rtk.
|
|
3954
|
+
// Pinned entries only; the foreign installers (opencode-loop's npx installer, rtk init)
|
|
3955
|
+
// are never run because they rewrite the user's opencode config.
|
|
3956
|
+
const OPENCODE_OPTIONAL_PINS = {
|
|
3957
|
+
ponytail: '@dietrichgebert/ponytail@4.10.0',
|
|
3958
|
+
loop: '@bybrawe/opencode-loop@0.6.2',
|
|
3959
|
+
};
|
|
3960
|
+
const OPENCODE_RTK_HINT = 'rtk is not installed by AOS. Run yourself: brew install rtk && rtk init -g --opencode';
|
|
3961
|
+
const OPENCODE_LOOP_SHELL_WARNING = 'opencode-loop: /loop-shell style commands run shell commands as child processes. ' +
|
|
3962
|
+
'tool.execute.before, and so the AOS go-gate, may never see them (unverified). ' +
|
|
3963
|
+
'Do not schedule git push, publish or other gated commands through them.';
|
|
3964
|
+
|
|
3965
|
+
let codenotchResult = null;
|
|
3966
|
+
async function maybeInstallCodenotch() {
|
|
3967
|
+
if (process.platform !== 'darwin' && process.platform !== 'win32') return;
|
|
3968
|
+
const codenotch = require('./lib/codenotch.js');
|
|
3969
|
+
codenotchResult = await codenotch.runCodenotchStep({
|
|
3970
|
+
interactive: !isAutoYes,
|
|
3971
|
+
dryRun: DRY_RUN,
|
|
3972
|
+
ask: async (message, initialValue = true) => { const a = await askConfirm({ message, initialValue }); return !isCancel(a) && !!a; },
|
|
3973
|
+
log: { info: (m) => log.info(m), warn: (m) => log.warn(m), success: (m) => log.success(m) },
|
|
3974
|
+
});
|
|
3975
|
+
const line = codenotch.codenotchSummaryLine(codenotchResult);
|
|
3976
|
+
if (line) (codenotchResult.status === 'error' || /FAILED/.test(line) ? log.warn : log.info)(line);
|
|
3977
|
+
}
|
|
3978
|
+
|
|
3979
|
+
function parseOpencodeOptional(argv = process.argv, env = process.env) {
|
|
3980
|
+
const flag = argv.find((a) => a.startsWith('--opencode-optional='));
|
|
3981
|
+
const raw = [env.AOS_OPENCODE_OPTIONAL, flag && flag.slice('--opencode-optional='.length)].filter(Boolean).join(',');
|
|
3982
|
+
return new Set(raw.split(',').map((x) => x.trim().toLowerCase()).filter(Boolean));
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
// Opt-in OpenCode permission. Off unless AOS_OPENCODE_PERMISSION=external_directory or
|
|
3986
|
+
// --opencode-permission=external_directory. Only permission.external_directory is ever set,
|
|
3987
|
+
// as path patterns (object form verified in the OpenCode config schema; ~ expands per its docs).
|
|
3988
|
+
const OPENCODE_EXTERNAL_DIRECTORY_RULES = { '~/.agents/**': 'allow', '~/.config/opencode/**': 'allow' };
|
|
3989
|
+
|
|
3990
|
+
function parseOpencodePermission(argv = process.argv, env = process.env) {
|
|
3991
|
+
const flag = argv.find((a) => a.startsWith('--opencode-permission='));
|
|
3992
|
+
const raw = [env.AOS_OPENCODE_PERMISSION, flag && flag.slice('--opencode-permission='.length)].filter(Boolean).join(',');
|
|
3993
|
+
return new Set(raw.split(',').map((x) => x.trim().toLowerCase()).filter(Boolean));
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
function applyOpencodePermission(data, configPath, wanted) {
|
|
3997
|
+
for (const name of wanted) {
|
|
3998
|
+
if (name !== 'external_directory') log.warn(`Unknown OpenCode permission "${name}" ignored.`);
|
|
3999
|
+
}
|
|
4000
|
+
if (!wanted.has('external_directory') || DRY_RUN) return;
|
|
4001
|
+
const perm = data.permission;
|
|
4002
|
+
if (perm !== undefined && (perm === null || typeof perm !== 'object' || Array.isArray(perm))) {
|
|
4003
|
+
log.warn('opencode.jsonc "permission" is not an object (string shorthand?); external_directory not set, left untouched.');
|
|
4004
|
+
return;
|
|
4005
|
+
}
|
|
4006
|
+
if (perm && 'external_directory' in perm) {
|
|
4007
|
+
if (JSON.stringify(perm.external_directory) !== JSON.stringify(OPENCODE_EXTERNAL_DIRECTORY_RULES)) {
|
|
4008
|
+
log.message('opencode.jsonc already has permission.external_directory; left untouched.');
|
|
4009
|
+
}
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
if (configPath && fs.existsSync(configPath)) {
|
|
4013
|
+
if (readJsoncFile(configPath) === null) {
|
|
4014
|
+
log.warn(`Could not parse ${configPath}; permission.external_directory not set.`);
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
const bak = `${configPath}.${timestamp}.bak`;
|
|
4018
|
+
try { fs.copyFileSync(configPath, bak); } catch (e) {
|
|
4019
|
+
log.warn(`Could not back up ${configPath}, skipping permission.external_directory: ${e.message}`);
|
|
4020
|
+
return;
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
data.permission = Object.assign(perm || {}, { external_directory: { ...OPENCODE_EXTERNAL_DIRECTORY_RULES } });
|
|
4024
|
+
log.step('Added opt-in OpenCode permission.external_directory for AOS paths (comments in opencode.jsonc are not preserved on rewrite)');
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
const pluginPackageName = (entry) => {
|
|
4028
|
+
const str = typeof entry === 'string' ? entry : (Array.isArray(entry) ? String(entry[0]) : '');
|
|
4029
|
+
return str.replace(/(?<=.)@[^@/]*$/, '');
|
|
4030
|
+
};
|
|
4031
|
+
|
|
4032
|
+
// OpenCode auto-loads plugins/*.js and also loads plugin[] paths, then dedupes by file
|
|
4033
|
+
// URL (verified in the 1.18.30 binary strings, not by running it). Same path = one load;
|
|
4034
|
+
// a second differing spelling would load the gate and loop keeper twice.
|
|
4035
|
+
function warnOnDuplicateAosPlugin(plugins, canonical) {
|
|
4036
|
+
const norm = (v) => String(Array.isArray(v) ? v[0] : v).replace(/\\/g, '/');
|
|
4037
|
+
const others = plugins.filter((p) => /bdb-aos\.js$/.test(norm(p)) && norm(p) !== canonical);
|
|
4038
|
+
if (others.length > 0) {
|
|
4039
|
+
log.warn(`opencode.jsonc lists bdb-aos.js under another path (${others.map(norm).join(', ')}). ` +
|
|
4040
|
+
`plugins/bdb-aos.js is auto-loaded too; remove the extra entry to avoid a double gate.`);
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4044
|
+
function appendOptionalOpencodePlugins(data, configPath, optional) {
|
|
4045
|
+
const wanted = optional instanceof Set ? optional : new Set(optional || []);
|
|
4046
|
+
for (const name of wanted) {
|
|
4047
|
+
if (!(name in OPENCODE_OPTIONAL_PINS) && name !== 'rtk') log.warn(`Unknown OpenCode optional component "${name}" ignored.`);
|
|
4048
|
+
}
|
|
4049
|
+
if (wanted.has('rtk')) log.message(OPENCODE_RTK_HINT);
|
|
4050
|
+
let backedUp = false;
|
|
4051
|
+
for (const name of Object.keys(OPENCODE_OPTIONAL_PINS)) {
|
|
4052
|
+
if (!wanted.has(name) || DRY_RUN) continue;
|
|
4053
|
+
const pin = OPENCODE_OPTIONAL_PINS[name];
|
|
4054
|
+
if (data.plugin.some((p) => pluginPackageName(p) === pluginPackageName(pin))) continue;
|
|
4055
|
+
if (!backedUp && configPath && fs.existsSync(configPath)) {
|
|
4056
|
+
const bak = `${configPath}.${timestamp}.bak`;
|
|
4057
|
+
try { fs.copyFileSync(configPath, bak); backedUp = true; } catch (e) {
|
|
4058
|
+
log.warn(`Could not back up ${configPath}, skipping optional ${name}: ${e.message}`);
|
|
4059
|
+
continue;
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
data.plugin.push(pin);
|
|
4063
|
+
log.step(`Added opt-in OpenCode plugin ${pin}`);
|
|
4064
|
+
}
|
|
4065
|
+
if (data.plugin.some((p) => pluginPackageName(p) === pluginPackageName(OPENCODE_OPTIONAL_PINS.loop))) {
|
|
4066
|
+
log.warn(OPENCODE_LOOP_SHELL_WARNING);
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
// Idempotent and edit-safe: a file is overwritten only when it is absent, already
|
|
4071
|
+
// identical, or still byte-equal to what this installer last recorded. Anything else
|
|
4072
|
+
// (user edit, or an untracked file with the same name) is kept and the shipped copy
|
|
4073
|
+
// lands as <file>.new. Files the installer did not create are never removed.
|
|
4074
|
+
function installOpencodeCommands(srcDirPath, destDirPath) {
|
|
4075
|
+
if (DRY_RUN) {
|
|
4076
|
+
log.message(`[dry-run] install OpenCode commands: ${srcDirPath} -> ${destDirPath}`);
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
const ownsManifest = !_sessionManifest;
|
|
4080
|
+
const manifest = _sessionManifest || loadInstallManifest();
|
|
4081
|
+
fs.mkdirSync(destDirPath, { recursive: true });
|
|
4082
|
+
let wrote = 0;
|
|
4083
|
+
for (const file of fs.readdirSync(srcDirPath)) {
|
|
4084
|
+
const src = path.join(srcDirPath, file);
|
|
4085
|
+
if (!fs.statSync(src).isFile()) continue;
|
|
4086
|
+
const dest = path.join(destDirPath, file);
|
|
4087
|
+
const srcHash = computeFileHash(src);
|
|
4088
|
+
const diskHash = fs.existsSync(dest) ? computeFileHash(dest) : null;
|
|
4089
|
+
if (diskHash === null || diskHash === srcHash || diskHash === manifest[dest]?.sha256) {
|
|
4090
|
+
if (diskHash !== srcHash) { fs.copyFileSync(src, dest); wrote++; }
|
|
4091
|
+
manifest[dest] = { path: dest, sha256: srcHash, version: pkg.version, installedAt: new Date().toISOString() };
|
|
4092
|
+
} else {
|
|
4093
|
+
fs.copyFileSync(src, `${dest}.new`);
|
|
4094
|
+
keptUserEdits.push(dest);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
if (ownsManifest) {
|
|
4098
|
+
saveInstallManifest(manifest);
|
|
4099
|
+
reportKeptUserEdits();
|
|
4100
|
+
}
|
|
4101
|
+
log.step(`OpenCode commands in ${destDirPath}: ${wrote} written`);
|
|
4102
|
+
}
|
|
4103
|
+
|
|
3908
4104
|
// Copy the OpenCode plugin + command payload and register both in
|
|
3909
4105
|
// opencode.jsonc. This is the single copy site: the Quick Update path
|
|
3910
4106
|
// (injectHarnessRules -> installGlobalHooks) and the fresh-install path
|
|
@@ -3915,7 +4111,7 @@ function injectHarnessRules() {
|
|
|
3915
4111
|
// Sync merges MCP servers in the same pass and writes once). When `data` is
|
|
3916
4112
|
// omitted the function loads and saves the config itself, which is what lets the
|
|
3917
4113
|
// Quick Update path register the plugin as well as copy it.
|
|
3918
|
-
function installOpencodePlugin({ targetHome = homeDir, configPath = null, data = null } = {}) {
|
|
4114
|
+
function installOpencodePlugin({ targetHome = homeDir, configPath = null, data = null, optional = parseOpencodeOptional(), permission = parseOpencodePermission() } = {}) {
|
|
3919
4115
|
const opencodeDir = configPath
|
|
3920
4116
|
? path.dirname(configPath)
|
|
3921
4117
|
: (process.platform === 'win32'
|
|
@@ -3952,13 +4148,11 @@ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data =
|
|
|
3952
4148
|
try { copyDirRecursiveSync(trailLibSrc, path.join(opencodeDir, 'plugins', 'lib')); } catch (e) { log.warn(`Could not install OpenCode plugin lib: ${e.message}`); }
|
|
3953
4149
|
}
|
|
3954
4150
|
|
|
3955
|
-
// Slash-command payloads
|
|
3956
|
-
// native resolution and only survives as a raw-text match in the plugin.
|
|
4151
|
+
// Slash-command payloads (/startcycle-graph and the generated /bdb-aos-<cmd> files).
|
|
3957
4152
|
const commandsSrc = path.join(srcDir, '.opencode', 'commands');
|
|
3958
4153
|
if (fs.existsSync(commandsSrc)) {
|
|
3959
4154
|
try {
|
|
3960
|
-
|
|
3961
|
-
log.step(`Installed OpenCode commands to ${path.join(opencodeDir, 'commands')}`);
|
|
4155
|
+
installOpencodeCommands(commandsSrc, path.join(opencodeDir, 'commands'));
|
|
3962
4156
|
} catch (e) {
|
|
3963
4157
|
log.warn(`Could not install OpenCode commands: ${e.message}`);
|
|
3964
4158
|
}
|
|
@@ -3985,6 +4179,8 @@ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data =
|
|
|
3985
4179
|
return str.includes('bdb-aos');
|
|
3986
4180
|
});
|
|
3987
4181
|
if (!alreadyRegistered) data.plugin.push(pluginPathNormalized);
|
|
4182
|
+
warnOnDuplicateAosPlugin(data.plugin, pluginPathNormalized);
|
|
4183
|
+
appendOptionalOpencodePlugins(data, configPath, optional);
|
|
3988
4184
|
|
|
3989
4185
|
// Skill paths. `.agents/skills` stays project-relative on purpose: it is the
|
|
3990
4186
|
// per-project contract, and OpenCode resolves it against each project, so it
|
|
@@ -4002,6 +4198,8 @@ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data =
|
|
|
4002
4198
|
}
|
|
4003
4199
|
}
|
|
4004
4200
|
|
|
4201
|
+
applyOpencodePermission(data, configPath, permission instanceof Set ? permission : new Set(permission || []));
|
|
4202
|
+
|
|
4005
4203
|
if (ownsWrite) {
|
|
4006
4204
|
// Write only on a real change. readJsoncFile strips comments, so
|
|
4007
4205
|
// serialising an unchanged config would silently eat a user's comments
|
|
@@ -4091,6 +4289,26 @@ function installGlobalHooks({ targetHome = homeDir, targetGemini = geminiDir } =
|
|
|
4091
4289
|
|
|
4092
4290
|
// 5. Global CLI launcher binaries (aos-config, aos-dashboard, aos-uninstall)
|
|
4093
4291
|
installGlobalBinaries();
|
|
4292
|
+
|
|
4293
|
+
// 6. Fixed-path GO check for AO and other non-hook callers
|
|
4294
|
+
installGoCheck({ targetHome });
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
// go-check.mjs is the fixed-path GO check for callers that are not a harness hook (AO).
|
|
4298
|
+
// It imports go-gate.mjs from its own directory, so both land in ~/.aos/bin, together with
|
|
4299
|
+
// the guarded patterns as JSON. Same conflict/backup rules as every other installed file.
|
|
4300
|
+
function installGoCheck({ targetHome = homeDir } = {}) {
|
|
4301
|
+
const dest = path.join(targetHome, '.aos', 'bin');
|
|
4302
|
+
const files = [
|
|
4303
|
+
[path.join(srcDir, 'bin', 'go-check.mjs'), 'go-check.mjs'],
|
|
4304
|
+
[path.join(srcDir, '.claude', 'hooks', 'go-gate.mjs'), 'go-gate.mjs'],
|
|
4305
|
+
[path.join(srcDir, 'bin', 'guarded-patterns.json'), 'guarded-patterns.json'],
|
|
4306
|
+
];
|
|
4307
|
+
if (!files.every(([src]) => fs.existsSync(src))) return false;
|
|
4308
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
4309
|
+
for (const [src, name] of files) copyDirRecursiveSync(src, path.join(dest, name));
|
|
4310
|
+
log.step(`Installed go-check to ${dest}`);
|
|
4311
|
+
return true;
|
|
4094
4312
|
}
|
|
4095
4313
|
|
|
4096
4314
|
function installGlobalBinaries() {
|
|
@@ -4150,7 +4368,7 @@ function installGlobalBinaries() {
|
|
|
4150
4368
|
// merged result goes to a .bdb-new.json sidecar -- the same recovery pattern
|
|
4151
4369
|
// the MCP config merge in installMcpsForTarget uses.
|
|
4152
4370
|
function mergeBdbSettingsHooks(settingsPath, { projectLocal = false } = {}) {
|
|
4153
|
-
const bdbHookScripts = ['go-gate.mjs', 'go-token.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
4371
|
+
const bdbHookScripts = ['go-gate.mjs', 'go-token.mjs', 'go-grant.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
|
|
4154
4372
|
// memb-inject reads the machine-global memB store under $HOME and is
|
|
4155
4373
|
// installed once per machine, so it stays $HOME-anchored even inside a
|
|
4156
4374
|
// project harness -- unlike the two gate hooks, which are per-checkout by
|
|
@@ -4427,6 +4645,21 @@ function mergeCodexTomlMcpServers(configTomlPath, servers) {
|
|
|
4427
4645
|
return skipped;
|
|
4428
4646
|
}
|
|
4429
4647
|
|
|
4648
|
+
// The go-gate PreToolUse stanza for Codex's config.toml. Whether it fires under `codex exec`
|
|
4649
|
+
// or under aos-acp is UNVERIFIED (docs/codex-gate-smoke.md).
|
|
4650
|
+
function codexGateSnippet(hooksDir) {
|
|
4651
|
+
return [
|
|
4652
|
+
'[[hooks.PreToolUse]]',
|
|
4653
|
+
'matcher = "^(Bash|run_command)$"',
|
|
4654
|
+
'[[hooks.PreToolUse.hooks]]',
|
|
4655
|
+
'type = "command"',
|
|
4656
|
+
`command = ${JSON.stringify(`node "${path.join(hooksDir, 'go-gate.mjs')}"`)}`,
|
|
4657
|
+
'timeout = 30',
|
|
4658
|
+
].join('\n');
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4661
|
+
const CODEX_GATE_NOTICE = 'Codex asks you to trust new or changed hooks before it runs them; approve the go-gate hook in Codex, or it stays inactive. Firing under `codex exec` and under aos-acp is UNVERIFIED: run the smoke test in docs/codex-gate-smoke.md.';
|
|
4662
|
+
|
|
4430
4663
|
// Merge the BDB hooks into ChatGPT Codex CLI's config.toml (~/.codex/config.toml or
|
|
4431
4664
|
// .codex/config.toml), ensuring [features] hooks = true and preserving existing
|
|
4432
4665
|
// non-BDB settings, comments, and MCP servers.
|
|
@@ -4438,12 +4671,7 @@ function mergeCodexTomlHooks(configTomlPath, { projectLocal = false } = {}) {
|
|
|
4438
4671
|
|
|
4439
4672
|
const tomlSnippet = [
|
|
4440
4673
|
'# AOS:HOOKS:START',
|
|
4441
|
-
|
|
4442
|
-
'matcher = "^(Bash|run_command)$"',
|
|
4443
|
-
'[[hooks.PreToolUse.hooks]]',
|
|
4444
|
-
'type = "command"',
|
|
4445
|
-
`command = ${JSON.stringify(`node "${path.join(hooksDir, 'go-gate.mjs')}"`)}`,
|
|
4446
|
-
'timeout = 30',
|
|
4674
|
+
codexGateSnippet(hooksDir),
|
|
4447
4675
|
'',
|
|
4448
4676
|
'[[hooks.Stop]]',
|
|
4449
4677
|
'[[hooks.Stop.hooks]]',
|
|
@@ -4557,7 +4785,7 @@ function installProjectHarness() {
|
|
|
4557
4785
|
installStep('copy .agents/ contract into project', () => {
|
|
4558
4786
|
const agentsSrc = path.join(srcDir, '.agents');
|
|
4559
4787
|
if (!fs.existsSync(agentsSrc)) throw new Error(`missing payload: ${agentsSrc}`);
|
|
4560
|
-
copyDirRecursiveSync(agentsSrc, projectAgentsDir);
|
|
4788
|
+
copyDirRecursiveSync(agentsSrc, projectAgentsDir, AGENTS_COPY_EXCLUDE);
|
|
4561
4789
|
log.step(`Copied .agents/ contract to ${projectAgentsDir}`);
|
|
4562
4790
|
}, 'graph.md / state.schema.json may be missing in the project.');
|
|
4563
4791
|
|
|
@@ -5577,6 +5805,7 @@ async function runQuickUpdate(installState) {
|
|
|
5577
5805
|
|
|
5578
5806
|
console.log('');
|
|
5579
5807
|
verifyEcosystemInstallation();
|
|
5808
|
+
await maybeInstallCodenotch();
|
|
5580
5809
|
// Fresh installs open this via universalHarnessSync(); Quick Update never
|
|
5581
5810
|
// did, so a dev-workflow machine only ever saw it once, on day one --
|
|
5582
5811
|
// every subsequent run is a Quick Update, which is what almost every
|
|
@@ -5655,11 +5884,15 @@ function installBinaryAtomically(src, dest) {
|
|
|
5655
5884
|
|
|
5656
5885
|
async function main() {
|
|
5657
5886
|
const args = process.argv.slice(2);
|
|
5658
|
-
if (args.includes('--version') || args.includes('-V')) {
|
|
5887
|
+
if (args.includes('--version') || args.includes('-V') || args[0] === 'version') {
|
|
5659
5888
|
console.log(pkg.version);
|
|
5660
5889
|
return;
|
|
5661
5890
|
}
|
|
5662
|
-
if (args.includes('--
|
|
5891
|
+
if (args.includes('--codex-gate')) {
|
|
5892
|
+
console.log(`${codexGateSnippet(path.join(homeDir, '.codex', 'hooks'))}\n\n${CODEX_GATE_NOTICE}`);
|
|
5893
|
+
return;
|
|
5894
|
+
}
|
|
5895
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
5663
5896
|
console.log(`Usage: aos [command] [options]
|
|
5664
5897
|
|
|
5665
5898
|
Commands:
|
|
@@ -5667,8 +5900,31 @@ Commands:
|
|
|
5667
5900
|
doctor|checkup Check the installation
|
|
5668
5901
|
|
|
5669
5902
|
Options:
|
|
5903
|
+
--codex-gate Print the go-gate [[hooks.PreToolUse]] stanza for ~/.codex/config.toml
|
|
5904
|
+
and the Codex hook-trust note, then exit (writes nothing)
|
|
5670
5905
|
-y, --yes Non-interactive install (implied without a TTY)
|
|
5671
5906
|
--dry-run Show what would change
|
|
5907
|
+
--opencode-optional=LIST
|
|
5908
|
+
Opt in to OpenCode extras (comma list: ponytail, loop, rtk), or set
|
|
5909
|
+
AOS_OPENCODE_OPTIONAL. Pinned plugin[] entries are appended after a
|
|
5910
|
+
config backup; rtk only prints a brew hint. Off by default. AOS never
|
|
5911
|
+
runs foreign installers and never touches OpenCode's mcp set.
|
|
5912
|
+
--no-codenotch macOS and Windows only (never Linux): BDB AO Codenotch (desktop app,
|
|
5913
|
+
from hybridlabor-api/bdb-ao-codenotch) installs by default;
|
|
5914
|
+
skip it with this flag or AOS_CODENOTCH=0. --codenotch or
|
|
5915
|
+
AOS_CODENOTCH=1 forces it. Interactive prompt defaults to yes. Failures
|
|
5916
|
+
only warn and never fail the install. SHA-256 verified; Windows runs
|
|
5917
|
+
the per-user installer silently (/S, no admin). Never overwrites an app
|
|
5918
|
+
AOS did not install. See docs/codenotch.md.
|
|
5919
|
+
--opencode-permission=external_directory
|
|
5920
|
+
Opt in to OpenCode's permission.external_directory for ~/.agents and
|
|
5921
|
+
~/.config/opencode only (path patterns, allow), or set
|
|
5922
|
+
AOS_OPENCODE_PERMISSION. Existing keys are never overwritten; a string
|
|
5923
|
+
"permission" is refused. Config backed up first. Off by default.
|
|
5924
|
+
--plugin-migration=off|check
|
|
5925
|
+
Skip (off) or only report (check) the bdb-aos plugin registration and
|
|
5926
|
+
removal of AOS's own loose skill copies; same as AOS_PLUGIN_MIGRATION.
|
|
5927
|
+
--dry-run implies check. Default: on.
|
|
5672
5928
|
--verbose, -v Verbose output
|
|
5673
5929
|
-V, --version Print the version and exit
|
|
5674
5930
|
-h, --help Print this help and exit`);
|
|
@@ -6036,45 +6292,7 @@ Options:
|
|
|
6036
6292
|
const s = spinner();
|
|
6037
6293
|
s.start(`Installing optimized skills${tier === '2' ? ' [Basic Tier]' : ''} to ${targets.length} target(s)...`);
|
|
6038
6294
|
|
|
6039
|
-
|
|
6040
|
-
if (mode === 'replace') {
|
|
6041
|
-
moveIfExists(t.targetSkillDir, path.join(backupDir, `config_skills_backup_${t.value}`), `global config skills (${t.value})`);
|
|
6042
|
-
moveIfExists(t.targetLegacyDir, path.join(backupDir, `legacy_skills_backup_${t.value}`), `legacy skills (${t.value})`);
|
|
6043
|
-
moveIfExists(t.targetWorkspaceDir, path.join(backupDir, `workspace_skills_backup_${t.value}`), `workspace skills (${t.value})`);
|
|
6044
|
-
}
|
|
6045
|
-
|
|
6046
|
-
installStep(`create the skill target directories (${t.value})`, () => {
|
|
6047
|
-
fs.mkdirSync(t.targetSkillDir, { recursive: true });
|
|
6048
|
-
retireObsoleteLegacyDir(t.targetLegacyDir);
|
|
6049
|
-
fs.mkdirSync(t.targetWorkspaceDir, { recursive: true });
|
|
6050
|
-
}, 'The skill copies below will most likely be skipped as well.');
|
|
6051
|
-
|
|
6052
|
-
if (fs.existsSync(skillsBase)) {
|
|
6053
|
-
installStep(`install the skills (${t.value})`, () => {
|
|
6054
|
-
const rawDirs = fs.readdirSync(skillsBase);
|
|
6055
|
-
const dirs = rawDirs.sort((a, b) => {
|
|
6056
|
-
const aIsLeaf = fs.existsSync(path.join(skillsBase, a, 'SKILL.md'));
|
|
6057
|
-
const bIsLeaf = fs.existsSync(path.join(skillsBase, b, 'SKILL.md'));
|
|
6058
|
-
if (aIsLeaf && !bIsLeaf) return 1;
|
|
6059
|
-
if (!aIsLeaf && bIsLeaf) return -1;
|
|
6060
|
-
return 0;
|
|
6061
|
-
});
|
|
6062
|
-
for (const dir of dirs) {
|
|
6063
|
-
const fullPath = path.join(skillsBase, dir);
|
|
6064
|
-
if (!fs.statSync(fullPath).isDirectory()) continue;
|
|
6065
|
-
|
|
6066
|
-
if (dir === 'global_legacy') {
|
|
6067
|
-
copyDirRecursiveSync(fullPath, t.targetLegacyDir, excludeSkills);
|
|
6068
|
-
} else if (dir === 'workspace_agents') {
|
|
6069
|
-
copyDirRecursiveSync(fullPath, t.targetWorkspaceDir, excludeSkills);
|
|
6070
|
-
} else {
|
|
6071
|
-
syncSkillEntry(fullPath, dir, t.targetSkillDir, excludeSkills);
|
|
6072
|
-
}
|
|
6073
|
-
}
|
|
6074
|
-
log.step(`Installed all global config & core skills to ${t.targetSkillDir}`);
|
|
6075
|
-
}, 'The skills are missing or incomplete; the rest of the installation continues.');
|
|
6076
|
-
}
|
|
6077
|
-
}
|
|
6295
|
+
installTargetSkills(targets, { mode, backupDir, excludeSkills, skillsBase });
|
|
6078
6296
|
|
|
6079
6297
|
syncSkillsToGlobalHarnesses(excludeSkills);
|
|
6080
6298
|
pruneRemovedSkills(_sessionManifest);
|
|
@@ -6117,6 +6335,7 @@ Options:
|
|
|
6117
6335
|
|
|
6118
6336
|
console.log('');
|
|
6119
6337
|
verifyEcosystemInstallation();
|
|
6338
|
+
await maybeInstallCodenotch();
|
|
6120
6339
|
|
|
6121
6340
|
outro(`🎉 Installation complete! Targets: ${targets.map(t => t.value).join(', ')}${aosCliRequested ? ' + AOS CLI' : ''} · Tier: ${tier === '1' ? 'Pro MEDIA' : 'Basic'}${DRY_RUN ? ' · DRY-RUN (nothing was modified)' : ''}`);
|
|
6122
6341
|
}
|
|
@@ -6153,7 +6372,15 @@ module.exports = {
|
|
|
6153
6372
|
mergeCodexHooks: mergeCodexTomlHooks,
|
|
6154
6373
|
mergeCodexTomlMcpServers,
|
|
6155
6374
|
installGlobalHooks,
|
|
6375
|
+
installGoCheck,
|
|
6376
|
+
codexGateSnippet,
|
|
6377
|
+
CODEX_GATE_NOTICE,
|
|
6156
6378
|
installOpencodePlugin,
|
|
6379
|
+
installOpencodeCommands,
|
|
6380
|
+
parseOpencodeOptional,
|
|
6381
|
+
maybeInstallCodenotch,
|
|
6382
|
+
pluginMigrationMode,
|
|
6383
|
+
runPluginMigration,
|
|
6157
6384
|
installProjectHarness,
|
|
6158
6385
|
promptMcpSelection,
|
|
6159
6386
|
mirrorMcpServersTo,
|
|
@@ -6166,6 +6393,9 @@ module.exports = {
|
|
|
6166
6393
|
resolveFileConflict,
|
|
6167
6394
|
buildKnownSourceHashes,
|
|
6168
6395
|
initSessionManifest,
|
|
6396
|
+
installTargetSkills,
|
|
6397
|
+
stableOpenWikiScripts,
|
|
6398
|
+
AGENTS_COPY_EXCLUDE,
|
|
6169
6399
|
flushSessionManifest,
|
|
6170
6400
|
copyDirRecursiveSync,
|
|
6171
6401
|
INSTALL_MANIFEST_PATH,
|