@hybridlabor-api/aos 4.4.2-beta.0 → 4.4.2-beta.2

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 (3) hide show
  1. package/CLAUDE.md +52 -0
  2. package/installer.js +99 -0
  3. package/package.json +1 -1
package/CLAUDE.md CHANGED
@@ -616,6 +616,58 @@ must not do.
616
616
 
617
617
  ## Headless timeouts
618
618
 
619
+ A trivial headless `agy` prompt was measured at **605s** in 2026-09.
620
+ `agy-delegate` defaults to `--print-timeout 5m`, so it aborts at 300s and
621
+ reports an empty body while the answer is still arriving. Pass `--timeout 15m`
622
+ for anything non-trivial. A short timeout does not read as "slow", it reads as
623
+ "broken".
624
+
625
+ # AOS — Antigravity / Gemini
626
+
627
+ **Read [AGENTS.md](AGENTS.md) first.** It holds every rule that applies to all
628
+ harnesses: the non-negotiables, the release gate, Conventional Commits, the
629
+ skill contract and category routing, the pipeline variants, and the delegation
630
+ policy. Nothing from it is repeated here — a rule that lives in two files is a
631
+ rule that will eventually disagree with itself.
632
+
633
+ This file covers only what is specific to running AOS under Antigravity.
634
+
635
+ ## Nothing enforces the gate here — you are the enforcement
636
+
637
+ `.claude/hooks/go-gate.mjs` is a Claude Code hook and does not run on this
638
+ harness. Not one of the four release commands is mechanically blocked for you,
639
+ and neither is anything else.
640
+
641
+ So the full rule in AGENTS.md applies here in its entirety, not just its
642
+ hook-enforced subset: when the user asks for a plan, review, audit or
643
+ multi-step action, you are read-only until they answer with the literal **GO**
644
+ — including file writes and `git commit`. On Claude Code a hook would catch the
645
+ four worst cases if discipline failed. Here there is no backstop, which makes
646
+ this the harness where the rule matters most, not least.
647
+
648
+ ## Model routing
649
+
650
+ The tier names in `agy-delegate` map to models that go stale — the built-in
651
+ `flash` still points at Gemini 3.7 while 3.8 ships. Re-check against
652
+ `agy models` after every upgrade; the id carries both the version and the
653
+ effort suffix.
654
+
655
+ | Work | Model |
656
+ |---|---|
657
+ | Media, fast or mechanical coding, boilerplate | `Gemini 3.8 Flash (Medium)` |
658
+ | Trivial one-liners | `Gemini 3.8 Flash (Low)` |
659
+ | Review, architecture, hard reasoning | `Claude Sonnet 4.6 (Thinking)` |
660
+
661
+ Set these once via `CLAUDE_PLUGIN_OPTION_TIER_{FLASH,FLASH_LO,PRO}` in
662
+ `~/.zshenv` — **not** `~/.zshrc`, which non-interactive tool shells never
663
+ source.
664
+
665
+ Adversarial review is the case that most repays the stronger model: a Flash
666
+ tier tends to agree with what it is shown, which is the one thing a reviewer
667
+ must not do.
668
+
669
+ ## Headless timeouts
670
+
619
671
  A trivial headless `agy` prompt was measured at **605s** in 2026-09.
620
672
  `agy-delegate` defaults to `--print-timeout 5m`, so it aborts at 300s and
621
673
  reports an empty body while the answer is still arriving. Pass `--timeout 15m`
package/installer.js CHANGED
@@ -608,6 +608,88 @@ function initSessionManifest(existingManifest, sourceDirs) {
608
608
  _sessionHashes = buildKnownSourceHashes(sourceDirs);
609
609
  }
610
610
 
611
+ // Skills that left the package in 4.4.2 (Plan 02: migrated to the private
612
+ // aos-internal repo, per ~/dev/_plans/aos-2026-09-14/), by their flattened
613
+ // directory name at a sync destination.
614
+ //
615
+ // This is deliberately a fixed list, not a diff against what the current
616
+ // payload ships. A generic "anything on disk that is not in skills/ right
617
+ // now" pass was tried first and swept up two unrelated things: skill folders
618
+ // that MCP servers bundle inside their own package tree (e.g.
619
+ // mcps/tdmcp/.agents/skills/...), which happen to share the literal path
620
+ // segment "skills" but belong to a different install entirely; and skills
621
+ // under skills/workspace_agents/, which syncSkillsToGlobalHarnesses already
622
+ // deliberately excludes from the six global destinations, so a stray global
623
+ // copy of one says nothing about whether it is safe to delete. Both are real
624
+ // cleanup opportunities, but they are a separate investigation, not a side
625
+ // effect of this migration's beta test.
626
+ const SKILLS_REMOVED_IN_4_4_2 = new Set([
627
+ 'bdbsaastraining', 'bdb-dev-os-skill', 'bdbsaashost', 'bdb-ecosystem-health', 'bdbsaas-ops',
628
+ ]);
629
+
630
+ // The exact roots syncSkillsToGlobalHarnesses and the Gemini/Antigravity
631
+ // config-skills path write into. Pruning only touches a manifest entry whose
632
+ // path starts under one of these -- never one that merely contains a
633
+ // "skills" segment somewhere deeper, which is what let the MCP-bundle and
634
+ // workspace_agents cases above leak in during testing.
635
+ function globalSkillDestRoots() {
636
+ return [
637
+ path.join(homeDir, '.agents', 'skills'),
638
+ path.join(homeDir, '.claude', 'skills'),
639
+ path.join(homeDir, '.codex', 'skills'),
640
+ path.join(homeDir, '.cursor', 'skills'),
641
+ path.join(homeDir, '.roo', 'skills'),
642
+ path.join(geminiDir, 'config', 'skills'),
643
+ ];
644
+ }
645
+
646
+ // syncSkillsToGlobalHarnesses (and its sibling copy loops) only ever add or
647
+ // update -- nothing in that path ever deletes a skill that the current
648
+ // payload no longer ships. That is fine for new skills and updated ones, but
649
+ // it means a skill removed from a release stays on every user's disk
650
+ // forever, through every future update, because nothing after the removal
651
+ // ever looks at what is on disk that no longer has a source. This walks the
652
+ // install manifest -- which already has one entry per file this installer
653
+ // has ever placed -- for the specific skills 4.4.2 removed, under the six
654
+ // known global skill roots, and deletes them. A user-modified file is backed
655
+ // up first, the same way resolveFileConflict backs up a user edit it is
656
+ // about to overwrite, rather than silently deleting local changes.
657
+ function pruneRemovedSkills(manifest) {
658
+ if (!manifest) return;
659
+ const roots = globalSkillDestRoots();
660
+
661
+ const staleDirs = new Set();
662
+ let removedFiles = 0;
663
+ for (const targetPath of Object.keys(manifest)) {
664
+ const root = roots.find((r) => targetPath.startsWith(r + path.sep));
665
+ if (!root) continue;
666
+ const skillDirName = targetPath.slice(root.length + 1).split(path.sep)[0];
667
+ if (!SKILLS_REMOVED_IN_4_4_2.has(skillDirName)) continue;
668
+
669
+ if (fs.existsSync(targetPath)) {
670
+ const diskHash = computeFileHash(targetPath);
671
+ const manifestEntry = manifest[targetPath];
672
+ if (diskHash !== null && manifestEntry && diskHash !== manifestEntry.sha256) {
673
+ const bakPath = `${targetPath}.${timestamp}.bak`;
674
+ try { fs.copyFileSync(targetPath, bakPath); } catch (e) { logDebug(e, 'prune-removed-skill backup'); }
675
+ log.warn(`[manifest] Retired skill left a user-edited file, backed up to ${bakPath} instead of deleting it.`);
676
+ } else {
677
+ try { fs.unlinkSync(targetPath); removedFiles++; } catch (e) { logDebug(e, 'prune-removed-skill unlink'); }
678
+ }
679
+ staleDirs.add(path.join(root, skillDirName));
680
+ }
681
+ delete manifest[targetPath];
682
+ }
683
+
684
+ for (const dir of staleDirs) {
685
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (e) { logDebug(e, 'prune-removed-skill rmdir'); }
686
+ }
687
+
688
+ if (staleDirs.size > 0) {
689
+ log.step(`Removed ${removedFiles} file(s) from ${staleDirs.size} retired skill install(s) no longer shipped.`);
690
+ }
691
+ }
692
+
611
693
  function flushSessionManifest() {
612
694
  if (_sessionManifest) saveInstallManifest(_sessionManifest);
613
695
  }
@@ -3464,6 +3546,7 @@ async function runQuickUpdate(installState) {
3464
3546
  }
3465
3547
  }
3466
3548
  syncSkillsToGlobalHarnesses(excludeSkills);
3549
+ pruneRemovedSkills(_sessionManifest);
3467
3550
  s.stop('Skills refreshed');
3468
3551
 
3469
3552
  // Everything injectHarnessRules() delivers -- GEMINI.md, the dispatcher
@@ -3579,12 +3662,14 @@ async function main() {
3579
3662
  { value: 'quick', label: `⚡ Quick Update (v${installState.localVersion} ➔ v${installState.currentVersion})`, hint: 'refresh skills, templates & daemons' },
3580
3663
  { value: 'project', label: '📁 Drop Local Project Harness', hint: 'copy dispatcher contract to cwd' },
3581
3664
  { value: 'reconfigure', label: '🛠️ Reconfigure System', hint: 'change targets, tier or options' },
3665
+ { value: 'uninstall', label: '🗑️ Uninstall AOS', hint: 'remove what this installer placed; your data stays' },
3582
3666
  { value: 'cancel', label: '❌ Exit' }
3583
3667
  ]
3584
3668
  : [
3585
3669
  { value: 'project', label: '📁 Drop Local Project Harness', hint: 'copy dispatcher contract to cwd' },
3586
3670
  { value: 'quick', label: '🔄 Verify & Refresh All Skills', hint: 're-sync and health check' },
3587
3671
  { value: 'reconfigure', label: '🛠️ Reconfigure System', hint: 'switch tier or targets' },
3672
+ { value: 'uninstall', label: '🗑️ Uninstall AOS', hint: 'remove what this installer placed; your data stays' },
3588
3673
  { value: 'cancel', label: '❌ Exit' }
3589
3674
  ];
3590
3675
 
@@ -3608,6 +3693,19 @@ async function main() {
3608
3693
  outro('Quick Update complete.');
3609
3694
  return;
3610
3695
  }
3696
+ if (action === 'uninstall') {
3697
+ // aos-uninstall.mjs is its own script, not a function of this file --
3698
+ // it is ESM (installer.js is CommonJS) and drives its own readline
3699
+ // confirmation prompt, so it runs as a real child process with
3700
+ // inherited stdio rather than being imported in-process.
3701
+ const uninstallScript = path.join(srcDir, 'bin', 'aos-uninstall.mjs');
3702
+ if (!fs.existsSync(uninstallScript)) {
3703
+ log.error(`Uninstaller not found at ${uninstallScript} -- run it directly: npx @hybridlabor-api/aos-uninstall`);
3704
+ return;
3705
+ }
3706
+ spawnSync('node', [uninstallScript], { stdio: 'inherit' });
3707
+ return;
3708
+ }
3611
3709
  }
3612
3710
 
3613
3711
  const detectedNames = detections.map(d => d.name).join(', ');
@@ -3868,6 +3966,7 @@ async function main() {
3868
3966
  }
3869
3967
 
3870
3968
  syncSkillsToGlobalHarnesses(excludeSkills);
3969
+ pruneRemovedSkills(_sessionManifest);
3871
3970
  s.stop('Skills installed.');
3872
3971
 
3873
3972
  injectHarnessRules();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.4.2-beta.0",
3
+ "version": "4.4.2-beta.2",
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
  "bin": {