@hybridlabor-api/aos 4.17.0 → 4.18.1

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 (88) hide show
  1. package/.claude/hooks/aos-bus.mjs +8 -4
  2. package/.claude/hooks/go-gate.mjs +1 -1
  3. package/.claude/hooks/go-token.mjs +17 -2
  4. package/.claude/hooks/memb-inject.mjs +61 -37
  5. package/.codex-plugin/plugin.json +1 -1
  6. package/.opencode/commands/bdb-aos-plan.md +1 -1
  7. package/README.md +1 -0
  8. package/THIRD_PARTY_NOTICES.md +2 -2
  9. package/bin/aos-acp.mjs +27 -1
  10. package/bin/aos-doctor.mjs +36 -1
  11. package/bin/aos-uninstall.mjs +16 -2
  12. package/bin/go-check.mjs +79 -0
  13. package/bin/guarded-patterns.json +106 -0
  14. package/commands/plan.md +1 -1
  15. package/docs/codenotch.md +44 -0
  16. package/docs/codex-gate-smoke.md +43 -0
  17. package/docs/delegation-routing.md +32 -0
  18. package/docs/go-check.md +60 -0
  19. package/docs/master-session-acp.md +2 -0
  20. package/docs/opencode-setup.md +18 -0
  21. package/installer.js +155 -70
  22. package/lib/codenotch.js +389 -0
  23. package/lib/retired-skills.js +101 -0
  24. package/mcps/mcsc/README.md +1 -1
  25. package/mcps/mcsc/packages/core/src/adapters/agy.js +3 -1
  26. package/mcps/mcsc/packages/core/src/adapters/codex.js +2 -1
  27. package/mcps/mcsc/packages/core/src/adapters/opencode.js +2 -1
  28. package/mcps/mcsc/packages/core/src/depth.js +16 -0
  29. package/mcps/mcsc/packages/mcp/server.js +15 -2
  30. package/package.json +2 -2
  31. package/plugin-commands.json +1 -2
  32. package/plugin.json +1 -4
  33. package/plugins/bdb-aos-codex/.codex-plugin/plugin.json +1 -1
  34. package/plugins/bdb-aos-codex/skills/plan/SKILL.md +1 -1
  35. package/scripts/codex-gate-smoke.mjs +73 -0
  36. package/skills/basic/master-session/SKILL.md +11 -0
  37. package/skills/global_config/agenttrail/SKILL.md +3 -1
  38. package/skills/global_config/agenttrail/bin/agenttrail.mjs +255 -117
  39. package/skills/global_config/agenttrail/bin/ensure.mjs +60 -40
  40. package/skills/global_config/agenttrail/bin/repoid.mjs +70 -0
  41. package/skills/global_config/agenttrail/public/index.html +9 -1
  42. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
  43. package/skills/global_config/bdb-memb-mcp/SKILL.md +5 -4
  44. package/skills/global_config/bdb-visual-edit/SKILL.md +28 -32
  45. package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +2 -2
  46. package/skills/global_config/bdb-visual-edit/scripts/locate-source.mjs +135 -0
  47. package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +30 -2
  48. package/skills/global_config/mcsc/SKILL.md +9 -1
  49. package/skills/global_config/plan-arbiter/SKILL.md +1 -1
  50. package/skills/global_config/plan-canvas/SKILL.md +42 -4
  51. package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +1 -1
  52. package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +2 -2
  53. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/geometry.js +76 -0
  54. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/index.js +596 -0
  55. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/model.js +192 -0
  56. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/toolbar.js +99 -0
  57. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-server.js +282 -0
  58. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotation-schema.js +210 -0
  59. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/route.js +10 -0
  60. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sdk.js +6 -230
  61. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +45 -4
  62. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sessions.js +60 -21
  63. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/trail-on-approve.js +103 -0
  64. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +19 -7
  65. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +118 -20
  66. package/skills/global_config/subagent-setup/SKILL.md +6 -0
  67. package/skills/global_config/subagent-setup/scripts/setup-subagents.mjs +20 -1
  68. package/skills/playbooks/pb-idea-to-launch/SKILL.md +2 -2
  69. package/skills/playbooks/pb-redesign-app/SKILL.md +3 -3
  70. package/skills/playbooks/pb-release-aos/SKILL.md +2 -2
  71. package/skills/playbooks/pb-ship/SKILL.md +2 -2
  72. package/skills/playbooks/pb-worktrees-land/SKILL.md +2 -2
  73. package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +0 -27
  74. package/skills/global_config/visual-edit/README.md +0 -96
  75. package/skills/global_config/visual-edit/SKILL.md +0 -615
  76. package/skills/global_config/visual-plan/README.md +0 -93
  77. package/skills/global_config/visual-plan/SKILL.md +0 -544
  78. package/skills/global_config/visual-plan/references/canvas.md +0 -139
  79. package/skills/global_config/visual-plan/references/connection.md +0 -51
  80. package/skills/global_config/visual-plan/references/document-quality.md +0 -186
  81. package/skills/global_config/visual-plan/references/exemplar.md +0 -62
  82. package/skills/global_config/visual-plan/references/local-files.md +0 -99
  83. package/skills/global_config/visual-plan/references/wireframe.md +0 -319
  84. package/skills/global_config/visual-recap/README.md +0 -103
  85. package/skills/global_config/visual-recap/SKILL.md +0 -560
  86. package/skills/global_config/visual-recap/references/connection.md +0 -51
  87. package/skills/global_config/visual-recap/references/local-files.md +0 -99
  88. package/skills/global_config/visual-recap/references/wireframe.md +0 -319
package/installer.js CHANGED
@@ -24,6 +24,7 @@ const readline = require('readline');
24
24
  const util = require('util');
25
25
  const crypto = require('crypto');
26
26
  const pluginMigration = require('./lib/plugin-migration');
27
+ const { pruneRetiredSkills } = require('./lib/retired-skills');
27
28
 
28
29
  function verifyDaemonListening(port, name, timeoutMs = 4000) {
29
30
  return new Promise((resolve) => {
@@ -378,6 +379,30 @@ function isNewerVersion(local, remote) {
378
379
  return false;
379
380
  }
380
381
 
382
+ // Newest dist-tag of `pkg` that is ahead of `localVer`: {tag, version}, or null when none is
383
+ // ahead or npm cannot be reached. `view` is injectable for tests.
384
+ function newestDistTag(pkg, localVer, view = null) {
385
+ try {
386
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
387
+ const opts = { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8', timeout: 4000 };
388
+ const out = view
389
+ ? view(pkg)
390
+ : process.platform === 'win32'
391
+ ? execSync(`"${npmCmd}" view ${pkg} dist-tags --json`, opts)
392
+ : execFileSync(npmCmd, ['view', pkg, 'dist-tags', '--json'], opts);
393
+ const distTags = JSON.parse(String(out).trim());
394
+ let best = null;
395
+ for (const [tag, ver] of Object.entries(distTags || {})) {
396
+ if (typeof ver !== 'string' || !isNewerVersion(localVer, ver)) continue;
397
+ if (!best || isNewerVersion(best.version, ver)) best = { tag, version: ver };
398
+ }
399
+ return best;
400
+ } catch (e) {
401
+ logDebug(e, `npm view ${pkg}`);
402
+ return null;
403
+ }
404
+ }
405
+
381
406
  function checkForUpdates() {
382
407
  return new Promise((resolve) => {
383
408
  const req = https.get(`https://registry.npmjs.org/${pkg.name}/latest`, { timeout: 1500 }, (res) => {
@@ -683,25 +708,6 @@ function initSessionManifest(existingManifest, sourceDirs) {
683
708
  _sessionHashes = buildKnownSourceHashes(sourceDirs);
684
709
  }
685
710
 
686
- // Skills that left the package in 4.4.2 (Plan 02: migrated to the private
687
- // aos-internal repo, per ~/dev/_plans/aos-2026-09-14/), by their flattened
688
- // directory name at a sync destination.
689
- //
690
- // This is deliberately a fixed list, not a diff against what the current
691
- // payload ships. A generic "anything on disk that is not in skills/ right
692
- // now" pass was tried first and swept up two unrelated things: skill folders
693
- // that MCP servers bundle inside their own package tree (e.g.
694
- // mcps/tdmcp/.agents/skills/...), which happen to share the literal path
695
- // segment "skills" but belong to a different install entirely; and skills
696
- // under skills/workspace_agents/, which syncSkillsToGlobalHarnesses already
697
- // deliberately excludes from the six global destinations, so a stray global
698
- // copy of one says nothing about whether it is safe to delete. Both are real
699
- // cleanup opportunities, but they are a separate investigation, not a side
700
- // effect of this migration's beta test.
701
- const SKILLS_REMOVED_IN_4_4_2 = new Set([
702
- 'bdbsaastraining', 'bdb-dev-os-skill', 'bdbsaashost', 'bdb-ecosystem-health', 'bdbsaas-ops',
703
- ]);
704
-
705
711
  // The exact roots syncSkillsToGlobalHarnesses and the Gemini/Antigravity
706
712
  // config-skills path write into. Pruning only touches a manifest entry whose
707
713
  // path starts under one of these -- never one that merely contains a
@@ -718,51 +724,16 @@ function globalSkillDestRoots() {
718
724
  ];
719
725
  }
720
726
 
721
- // syncSkillsToGlobalHarnesses (and its sibling copy loops) only ever add or
722
- // update -- nothing in that path ever deletes a skill that the current
723
- // payload no longer ships. That is fine for new skills and updated ones, but
724
- // it means a skill removed from a release stays on every user's disk
725
- // forever, through every future update, because nothing after the removal
726
- // ever looks at what is on disk that no longer has a source. This walks the
727
- // install manifest -- which already has one entry per file this installer
728
- // has ever placed -- for the specific skills 4.4.2 removed, under the six
729
- // known global skill roots, and deletes them. A user-modified file is backed
730
- // up first, the same way resolveFileConflict backs up a user edit it is
731
- // about to overwrite, rather than silently deleting local changes.
727
+ // Retires skills the package no longer ships (lib/retired-skills.js). Edited or foreign files are
728
+ // copied to the backup dir before the skill folder is removed.
732
729
  function pruneRemovedSkills(manifest) {
733
- if (!manifest) return;
734
- const roots = globalSkillDestRoots();
735
-
736
- const staleDirs = new Set();
737
- let removedFiles = 0;
738
- for (const targetPath of Object.keys(manifest)) {
739
- const root = roots.find((r) => targetPath.startsWith(r + path.sep));
740
- if (!root) continue;
741
- const skillDirName = targetPath.slice(root.length + 1).split(path.sep)[0];
742
- if (!SKILLS_REMOVED_IN_4_4_2.has(skillDirName)) continue;
743
-
744
- if (fs.existsSync(targetPath)) {
745
- const diskHash = computeFileHash(targetPath);
746
- const manifestEntry = manifest[targetPath];
747
- if (diskHash !== null && manifestEntry && diskHash !== manifestEntry.sha256) {
748
- const bakPath = `${targetPath}.${timestamp}.bak`;
749
- try { fs.copyFileSync(targetPath, bakPath); } catch (e) { logDebug(e, 'prune-removed-skill backup'); }
750
- log.warn(`[manifest] Retired skill left a user-edited file, backed up to ${bakPath} instead of deleting it.`);
751
- } else {
752
- try { fs.unlinkSync(targetPath); removedFiles++; } catch (e) { logDebug(e, 'prune-removed-skill unlink'); }
753
- }
754
- staleDirs.add(path.join(root, skillDirName));
755
- }
756
- delete manifest[targetPath];
757
- }
758
-
759
- for (const dir of staleDirs) {
760
- try { fs.rmSync(dir, { recursive: true, force: true }); } catch (e) { logDebug(e, 'prune-removed-skill rmdir'); }
761
- }
762
-
763
- if (staleDirs.size > 0) {
764
- log.step(`Removed ${removedFiles} file(s) from ${staleDirs.size} retired skill install(s) no longer shipped.`);
765
- }
730
+ pruneRetiredSkills({
731
+ home: homeDir,
732
+ manifest,
733
+ roots: globalSkillDestRoots(),
734
+ backupDir: path.join(homeDir, '.agents', 'backups', `retired-skills-${timestamp}`),
735
+ log,
736
+ });
766
737
  }
767
738
 
768
739
  function flushSessionManifest() {
@@ -4015,12 +3986,68 @@ const OPENCODE_LOOP_SHELL_WARNING = 'opencode-loop: /loop-shell style commands r
4015
3986
  'tool.execute.before, and so the AOS go-gate, may never see them (unverified). ' +
4016
3987
  'Do not schedule git push, publish or other gated commands through them.';
4017
3988
 
3989
+ let codenotchResult = null;
3990
+ async function maybeInstallCodenotch() {
3991
+ if (process.platform !== 'darwin' && process.platform !== 'win32') return;
3992
+ const codenotch = require('./lib/codenotch.js');
3993
+ codenotchResult = await codenotch.runCodenotchStep({
3994
+ interactive: !isAutoYes,
3995
+ dryRun: DRY_RUN,
3996
+ ask: async (message, initialValue = true) => { const a = await askConfirm({ message, initialValue }); return !isCancel(a) && !!a; },
3997
+ log: { info: (m) => log.info(m), warn: (m) => log.warn(m), success: (m) => log.success(m) },
3998
+ });
3999
+ const line = codenotch.codenotchSummaryLine(codenotchResult);
4000
+ if (line) (codenotchResult.status === 'error' || /FAILED/.test(line) ? log.warn : log.info)(line);
4001
+ }
4002
+
4018
4003
  function parseOpencodeOptional(argv = process.argv, env = process.env) {
4019
4004
  const flag = argv.find((a) => a.startsWith('--opencode-optional='));
4020
4005
  const raw = [env.AOS_OPENCODE_OPTIONAL, flag && flag.slice('--opencode-optional='.length)].filter(Boolean).join(',');
4021
4006
  return new Set(raw.split(',').map((x) => x.trim().toLowerCase()).filter(Boolean));
4022
4007
  }
4023
4008
 
4009
+ // Opt-in OpenCode permission. Off unless AOS_OPENCODE_PERMISSION=external_directory or
4010
+ // --opencode-permission=external_directory. Only permission.external_directory is ever set,
4011
+ // as path patterns (object form verified in the OpenCode config schema; ~ expands per its docs).
4012
+ const OPENCODE_EXTERNAL_DIRECTORY_RULES = { '~/.agents/**': 'allow', '~/.config/opencode/**': 'allow' };
4013
+
4014
+ function parseOpencodePermission(argv = process.argv, env = process.env) {
4015
+ const flag = argv.find((a) => a.startsWith('--opencode-permission='));
4016
+ const raw = [env.AOS_OPENCODE_PERMISSION, flag && flag.slice('--opencode-permission='.length)].filter(Boolean).join(',');
4017
+ return new Set(raw.split(',').map((x) => x.trim().toLowerCase()).filter(Boolean));
4018
+ }
4019
+
4020
+ function applyOpencodePermission(data, configPath, wanted) {
4021
+ for (const name of wanted) {
4022
+ if (name !== 'external_directory') log.warn(`Unknown OpenCode permission "${name}" ignored.`);
4023
+ }
4024
+ if (!wanted.has('external_directory') || DRY_RUN) return;
4025
+ const perm = data.permission;
4026
+ if (perm !== undefined && (perm === null || typeof perm !== 'object' || Array.isArray(perm))) {
4027
+ log.warn('opencode.jsonc "permission" is not an object (string shorthand?); external_directory not set, left untouched.');
4028
+ return;
4029
+ }
4030
+ if (perm && 'external_directory' in perm) {
4031
+ if (JSON.stringify(perm.external_directory) !== JSON.stringify(OPENCODE_EXTERNAL_DIRECTORY_RULES)) {
4032
+ log.message('opencode.jsonc already has permission.external_directory; left untouched.');
4033
+ }
4034
+ return;
4035
+ }
4036
+ if (configPath && fs.existsSync(configPath)) {
4037
+ if (readJsoncFile(configPath) === null) {
4038
+ log.warn(`Could not parse ${configPath}; permission.external_directory not set.`);
4039
+ return;
4040
+ }
4041
+ const bak = `${configPath}.${timestamp}.bak`;
4042
+ try { fs.copyFileSync(configPath, bak); } catch (e) {
4043
+ log.warn(`Could not back up ${configPath}, skipping permission.external_directory: ${e.message}`);
4044
+ return;
4045
+ }
4046
+ }
4047
+ data.permission = Object.assign(perm || {}, { external_directory: { ...OPENCODE_EXTERNAL_DIRECTORY_RULES } });
4048
+ log.step('Added opt-in OpenCode permission.external_directory for AOS paths (comments in opencode.jsonc are not preserved on rewrite)');
4049
+ }
4050
+
4024
4051
  const pluginPackageName = (entry) => {
4025
4052
  const str = typeof entry === 'string' ? entry : (Array.isArray(entry) ? String(entry[0]) : '');
4026
4053
  return str.replace(/(?<=.)@[^@/]*$/, '');
@@ -4108,7 +4135,7 @@ function installOpencodeCommands(srcDirPath, destDirPath) {
4108
4135
  // Sync merges MCP servers in the same pass and writes once). When `data` is
4109
4136
  // omitted the function loads and saves the config itself, which is what lets the
4110
4137
  // Quick Update path register the plugin as well as copy it.
4111
- function installOpencodePlugin({ targetHome = homeDir, configPath = null, data = null, optional = parseOpencodeOptional() } = {}) {
4138
+ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data = null, optional = parseOpencodeOptional(), permission = parseOpencodePermission() } = {}) {
4112
4139
  const opencodeDir = configPath
4113
4140
  ? path.dirname(configPath)
4114
4141
  : (process.platform === 'win32'
@@ -4195,6 +4222,8 @@ function installOpencodePlugin({ targetHome = homeDir, configPath = null, data =
4195
4222
  }
4196
4223
  }
4197
4224
 
4225
+ applyOpencodePermission(data, configPath, permission instanceof Set ? permission : new Set(permission || []));
4226
+
4198
4227
  if (ownsWrite) {
4199
4228
  // Write only on a real change. readJsoncFile strips comments, so
4200
4229
  // serialising an unchanged config would silently eat a user's comments
@@ -4284,6 +4313,26 @@ function installGlobalHooks({ targetHome = homeDir, targetGemini = geminiDir } =
4284
4313
 
4285
4314
  // 5. Global CLI launcher binaries (aos-config, aos-dashboard, aos-uninstall)
4286
4315
  installGlobalBinaries();
4316
+
4317
+ // 6. Fixed-path GO check for AO and other non-hook callers
4318
+ installGoCheck({ targetHome });
4319
+ }
4320
+
4321
+ // go-check.mjs is the fixed-path GO check for callers that are not a harness hook (AO).
4322
+ // It imports go-gate.mjs from its own directory, so both land in ~/.aos/bin, together with
4323
+ // the guarded patterns as JSON. Same conflict/backup rules as every other installed file.
4324
+ function installGoCheck({ targetHome = homeDir } = {}) {
4325
+ const dest = path.join(targetHome, '.aos', 'bin');
4326
+ const files = [
4327
+ [path.join(srcDir, 'bin', 'go-check.mjs'), 'go-check.mjs'],
4328
+ [path.join(srcDir, '.claude', 'hooks', 'go-gate.mjs'), 'go-gate.mjs'],
4329
+ [path.join(srcDir, 'bin', 'guarded-patterns.json'), 'guarded-patterns.json'],
4330
+ ];
4331
+ if (!files.every(([src]) => fs.existsSync(src))) return false;
4332
+ fs.mkdirSync(dest, { recursive: true });
4333
+ for (const [src, name] of files) copyDirRecursiveSync(src, path.join(dest, name));
4334
+ log.step(`Installed go-check to ${dest}`);
4335
+ return true;
4287
4336
  }
4288
4337
 
4289
4338
  function installGlobalBinaries() {
@@ -4620,6 +4669,21 @@ function mergeCodexTomlMcpServers(configTomlPath, servers) {
4620
4669
  return skipped;
4621
4670
  }
4622
4671
 
4672
+ // The go-gate PreToolUse stanza for Codex's config.toml. Whether it fires under `codex exec`
4673
+ // or under aos-acp is UNVERIFIED (docs/codex-gate-smoke.md).
4674
+ function codexGateSnippet(hooksDir) {
4675
+ return [
4676
+ '[[hooks.PreToolUse]]',
4677
+ 'matcher = "^(Bash|run_command)$"',
4678
+ '[[hooks.PreToolUse.hooks]]',
4679
+ 'type = "command"',
4680
+ `command = ${JSON.stringify(`node "${path.join(hooksDir, 'go-gate.mjs')}"`)}`,
4681
+ 'timeout = 30',
4682
+ ].join('\n');
4683
+ }
4684
+
4685
+ 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.';
4686
+
4623
4687
  // Merge the BDB hooks into ChatGPT Codex CLI's config.toml (~/.codex/config.toml or
4624
4688
  // .codex/config.toml), ensuring [features] hooks = true and preserving existing
4625
4689
  // non-BDB settings, comments, and MCP servers.
@@ -4631,12 +4695,7 @@ function mergeCodexTomlHooks(configTomlPath, { projectLocal = false } = {}) {
4631
4695
 
4632
4696
  const tomlSnippet = [
4633
4697
  '# AOS:HOOKS:START',
4634
- '[[hooks.PreToolUse]]',
4635
- 'matcher = "^(Bash|run_command)$"',
4636
- '[[hooks.PreToolUse.hooks]]',
4637
- 'type = "command"',
4638
- `command = ${JSON.stringify(`node "${path.join(hooksDir, 'go-gate.mjs')}"`)}`,
4639
- 'timeout = 30',
4698
+ codexGateSnippet(hooksDir),
4640
4699
  '',
4641
4700
  '[[hooks.Stop]]',
4642
4701
  '[[hooks.Stop.hooks]]',
@@ -5770,6 +5829,7 @@ async function runQuickUpdate(installState) {
5770
5829
 
5771
5830
  console.log('');
5772
5831
  verifyEcosystemInstallation();
5832
+ await maybeInstallCodenotch();
5773
5833
  // Fresh installs open this via universalHarnessSync(); Quick Update never
5774
5834
  // did, so a dev-workflow machine only ever saw it once, on day one --
5775
5835
  // every subsequent run is a Quick Update, which is what almost every
@@ -5852,6 +5912,10 @@ async function main() {
5852
5912
  console.log(pkg.version);
5853
5913
  return;
5854
5914
  }
5915
+ if (args.includes('--codex-gate')) {
5916
+ console.log(`${codexGateSnippet(path.join(homeDir, '.codex', 'hooks'))}\n\n${CODEX_GATE_NOTICE}`);
5917
+ return;
5918
+ }
5855
5919
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
5856
5920
  console.log(`Usage: aos [command] [options]
5857
5921
 
@@ -5860,6 +5924,8 @@ Commands:
5860
5924
  doctor|checkup Check the installation
5861
5925
 
5862
5926
  Options:
5927
+ --codex-gate Print the go-gate [[hooks.PreToolUse]] stanza for ~/.codex/config.toml
5928
+ and the Codex hook-trust note, then exit (writes nothing)
5863
5929
  -y, --yes Non-interactive install (implied without a TTY)
5864
5930
  --dry-run Show what would change
5865
5931
  --opencode-optional=LIST
@@ -5867,6 +5933,18 @@ Options:
5867
5933
  AOS_OPENCODE_OPTIONAL. Pinned plugin[] entries are appended after a
5868
5934
  config backup; rtk only prints a brew hint. Off by default. AOS never
5869
5935
  runs foreign installers and never touches OpenCode's mcp set.
5936
+ --no-codenotch macOS and Windows only (never Linux): BDB AO Codenotch (desktop app,
5937
+ from hybridlabor-api/bdb-ao-codenotch) installs by default;
5938
+ skip it with this flag or AOS_CODENOTCH=0. --codenotch or
5939
+ AOS_CODENOTCH=1 forces it. Interactive prompt defaults to yes. Failures
5940
+ only warn and never fail the install. SHA-256 verified; Windows runs
5941
+ the per-user installer silently (/S, no admin). Never overwrites an app
5942
+ AOS did not install. See docs/codenotch.md.
5943
+ --opencode-permission=external_directory
5944
+ Opt in to OpenCode's permission.external_directory for ~/.agents and
5945
+ ~/.config/opencode only (path patterns, allow), or set
5946
+ AOS_OPENCODE_PERMISSION. Existing keys are never overwritten; a string
5947
+ "permission" is refused. Config backed up first. Off by default.
5870
5948
  --plugin-migration=off|check
5871
5949
  Skip (off) or only report (check) the bdb-aos plugin registration and
5872
5950
  removal of AOS's own loose skill copies; same as AOS_PLUGIN_MIGRATION.
@@ -6281,6 +6359,7 @@ Options:
6281
6359
 
6282
6360
  console.log('');
6283
6361
  verifyEcosystemInstallation();
6362
+ await maybeInstallCodenotch();
6284
6363
 
6285
6364
  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)' : ''}`);
6286
6365
  }
@@ -6291,6 +6370,8 @@ if (require.main === module) {
6291
6370
 
6292
6371
  // Exported for tests -- requiring installer.js must not launch the TUI.
6293
6372
  module.exports = {
6373
+ newestDistTag,
6374
+ verifyEcosystemInstallation,
6294
6375
  installBinaryAtomically,
6295
6376
  installOSAgentWorkspace,
6296
6377
  readGoBuildInfo,
@@ -6317,9 +6398,13 @@ module.exports = {
6317
6398
  mergeCodexHooks: mergeCodexTomlHooks,
6318
6399
  mergeCodexTomlMcpServers,
6319
6400
  installGlobalHooks,
6401
+ installGoCheck,
6402
+ codexGateSnippet,
6403
+ CODEX_GATE_NOTICE,
6320
6404
  installOpencodePlugin,
6321
6405
  installOpencodeCommands,
6322
6406
  parseOpencodeOptional,
6407
+ maybeInstallCodenotch,
6323
6408
  pluginMigrationMode,
6324
6409
  runPluginMigration,
6325
6410
  installProjectHarness,